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

11148 lines
354 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. Sudarshan A. Chitre 20-Aug-2001 Cleanup: Removed all code that dealt with polling
  16. that was not being used. Finally changed the prefix
  17. of polling code from FrsNewDs to FrsDs.
  18. Environment
  19. User mode winnt
  20. --*/
  21. #include <ntreppch.h>
  22. #pragma hdrstop
  23. #include <perrepsr.h>
  24. #undef DEBSUB
  25. #define DEBSUB "DS:"
  26. #include <ntdsapi.h>
  27. #include <frs.h>
  28. #include <ntdsapip.h> // ms internal flags for DsCrackNames()
  29. #include <ntfrsapi.h>
  30. #include <tablefcn.h>
  31. #include <lmaccess.h>
  32. #include <dsrole.h>
  33. #include <lmapibuf.h>
  34. #ifdef SECURITY_WIN32
  35. #include <security.h>
  36. #else
  37. #define SECURITY_WIN32
  38. #include <security.h>
  39. #undef SECURITY_WIN32
  40. #endif
  41. #include <winsock2.h>
  42. //
  43. // No reason to hold memory if all we are doing is periodically
  44. // polling the DS to find there is no replication work to be
  45. // performed
  46. //
  47. // extern BOOL MainInitSucceeded;
  48. ULONG
  49. ActiveChildrenHashCalc(
  50. PVOID Buf,
  51. PULONGLONG QKey
  52. );
  53. BOOL
  54. ActiveChildrenKeyMatch(
  55. PVOID Buf,
  56. PVOID QKey
  57. );
  58. //
  59. // This is a reference counted structure that contains two tables.
  60. // The tables are used to look up valid partners by name or by connection
  61. // guid.
  62. //
  63. // Each time we poll, we create a new struct and then swap the pointer to the
  64. // old struct with a pointer to the new struct. We want to minimize the amount of
  65. // time that a lock needs to be held. We only write to the tables when they are
  66. // being created. At that time, only the local thread will access them so no
  67. // lock needs to be held. Later, we only read form the tables so no lock needs
  68. // to be held either. The thing that needs to be controlled by the lock is
  69. // access to this global pointer.
  70. //
  71. // Before using the struct you must aquire the crit sec, increase the reference
  72. // count, and make a local copy of the pointer. Use the local pointer copy so
  73. // that you do not get screwed up when the pointer is swapped for a new struct.
  74. // We also will hold the crit sec while swapping the pointers.
  75. //
  76. // If you keep the local pointer around, you will not need to hold the lock to
  77. // decrement the ref count. Nobody will be writing to the stuct itself and it
  78. // will not be cleaned up while the reference count is greater than zero. Be
  79. // sure to use InterlockedDecrement since other threads may be touching the ref
  80. // count at the same time.
  81. //
  82. // For simplicity, use ACQUIRE_VALID_PARTNER_TABLE_POINTER,
  83. // RELEASE_VALID_PARTNER_TABLE_POINTER, and SWAP_VALID_PARTNER_TABLE_POINTER.
  84. //
  85. PFRS_VALID_PARTNER_TABLE_STRUCT pValidPartnerTableStruct = NULL;
  86. CRITICAL_SECTION CritSec_pValidPartnerTableStruct;
  87. // List of old structs to be cleaned up when ref counts hit zero.
  88. PFRS_VALID_PARTNER_TABLE_STRUCT OldValidPartnerTableStructListHead = NULL;
  89. CRITICAL_SECTION OldValidPartnerTableStructListHeadLock;
  90. extern BOOL NeedNewPartnerTable;
  91. //
  92. // We will re-read the DS every so often (adjustable with registry)
  93. //
  94. ULONG DsPollingInterval;
  95. ULONG DsPollingShortInterval;
  96. ULONG DsPollingLongInterval;
  97. //
  98. // Don't use a noisy DS; wait until it settles out
  99. //
  100. ULONGLONG ThisChange;
  101. ULONGLONG LastChange;
  102. //
  103. // Dont't bother processing the same topology again
  104. //
  105. ULONGLONG NextChange;
  106. ULONGLONG ActiveChange;
  107. //
  108. // Try to keep the same binding forever
  109. //
  110. PLDAP gLdap = NULL;
  111. HANDLE DsHandle = NULL;
  112. PWCHAR SitesDn = NULL;
  113. PWCHAR ServicesDn = NULL;
  114. PWCHAR SystemDn = NULL;
  115. PWCHAR ComputersDn = NULL;
  116. PWCHAR DomainControllersDn = NULL;
  117. PWCHAR DefaultNcDn = NULL;
  118. BOOL DsBindingsAreValid = FALSE;
  119. BOOL DsCreateSysVolsHasRun = FALSE;
  120. //
  121. // Globals for the comm test
  122. //
  123. PWCHAR DsDomainControllerName;
  124. //
  125. // Have we initialized the rest of the service?
  126. //
  127. extern BOOL MainInitHasRun;
  128. //
  129. // Directory and file filter lists from registry.
  130. //
  131. extern PWCHAR RegistryFileExclFilterList;
  132. extern PWCHAR RegistryFileInclFilterList;
  133. extern PWCHAR RegistryDirExclFilterList;
  134. extern PWCHAR RegistryDirInclFilterList;
  135. //
  136. // Stop polling the DS
  137. //
  138. BOOL DsIsShuttingDown;
  139. HANDLE DsShutDownComplete;
  140. //
  141. // Remember the computer's DN to save calls to GetComputerObjectName().
  142. //
  143. PWCHAR ComputerCachedFqdn;
  144. PGEN_TABLE SubscriberTable = NULL;
  145. PGEN_TABLE SetTable = NULL;
  146. PGEN_TABLE CxtionTable = NULL;
  147. PGEN_TABLE AllCxtionsTable = NULL;
  148. PGEN_TABLE PartnerComputerTable = NULL;
  149. PGEN_TABLE MemberTable = NULL;
  150. PGEN_TABLE VolSerialNumberToDriveTable = NULL; // Mapping of VolumeSerial Number to drive.
  151. PWCHAR MemberSearchFilter = NULL;
  152. //
  153. // Collect the errors encountered during polling in this buffer and write it to the eventlog at the
  154. // end of poll.
  155. //
  156. PWCHAR DsPollSummaryBuf = NULL;
  157. DWORD DsPollSummaryBufLen = 0;
  158. DWORD DsPollSummaryMaxBufLen = 0;
  159. //
  160. // Role information
  161. //
  162. PWCHAR Roles[DsRole_RolePrimaryDomainController + 1] = {
  163. L"DsRole_RoleStandaloneWorkstation",
  164. L"DsRole_RoleMemberWorkstation",
  165. L"DsRole_RoleStandaloneServer",
  166. L"DsRole_RoleMemberServer",
  167. L"DsRole_RoleBackupDomainController",
  168. L"DsRole_RolePrimaryDomainController"
  169. };
  170. //
  171. // Flags to passed into DsGetDcName (see sdk\inc\dsgetdc.h)
  172. //
  173. FLAG_NAME_TABLE DsGetDcNameFlagNameTable[] = {
  174. {DS_FORCE_REDISCOVERY , "FORCE_REDISCOVERY " },
  175. {DS_DIRECTORY_SERVICE_REQUIRED , "DIRECTORY_SERVICE_REQUIRED " },
  176. {DS_DIRECTORY_SERVICE_PREFERRED , "DIRECTORY_SERVICE_PREFERRED " },
  177. {DS_GC_SERVER_REQUIRED , "GC_SERVER_REQUIRED " },
  178. {DS_PDC_REQUIRED , "PDC_REQUIRED " },
  179. {DS_BACKGROUND_ONLY , "DS_BACKGROUND_ONLY " },
  180. {DS_IP_REQUIRED , "IP_REQUIRED " },
  181. {DS_KDC_REQUIRED , "KDC_REQUIRED " },
  182. {DS_TIMESERV_REQUIRED , "TIMESERV_REQUIRED " },
  183. {DS_WRITABLE_REQUIRED , "WRITABLE_REQUIRED " },
  184. {DS_GOOD_TIMESERV_PREFERRED , "GOOD_TIMESERV_PREFERRED " },
  185. {DS_AVOID_SELF , "AVOID_SELF " },
  186. {DS_ONLY_LDAP_NEEDED , "ONLY_LDAP_NEEDED " },
  187. {DS_IS_FLAT_NAME , "IS_FLAT_NAME " },
  188. {DS_IS_DNS_NAME , "IS_DNS_NAME " },
  189. {DS_RETURN_DNS_NAME , "RETURN_DNS_NAME " },
  190. {DS_RETURN_FLAT_NAME , "RETURN_FLAT_NAME " },
  191. {0, NULL}
  192. };
  193. //
  194. // return flags from DsGetDCInfo() & DsGetDcName() too?
  195. //
  196. FLAG_NAME_TABLE DsGetDcInfoFlagNameTable[] = {
  197. {DS_PDC_FLAG , "DCisPDCofDomain " },
  198. {DS_GC_FLAG , "DCIsGCofForest " },
  199. {DS_LDAP_FLAG , "ServerSupportsLDAP_Server " },
  200. {DS_DS_FLAG , "DCSupportsDSAndIsA_DC " },
  201. {DS_KDC_FLAG , "DCIsRunningKDCSvc " },
  202. {DS_TIMESERV_FLAG , "DCIsRunningTimeSvc " },
  203. {DS_CLOSEST_FLAG , "DCIsInClosestSiteToClient " },
  204. {DS_WRITABLE_FLAG , "DCHasWritableDS " },
  205. {DS_GOOD_TIMESERV_FLAG , "DCRunningTimeSvcWithClockHW " },
  206. {DS_DNS_CONTROLLER_FLAG , "DCNameIsDNSName " },
  207. {DS_DNS_DOMAIN_FLAG , "DomainNameIsDNSName " },
  208. {DS_DNS_FOREST_FLAG , "DnsForestNameIsDNSName " },
  209. {0, NULL}
  210. };
  211. //
  212. // Flags from Options Attribute in NTDS-Connection object.
  213. //
  214. FLAG_NAME_TABLE CxtionOptionsFlagNameTable[] = {
  215. {NTDSCONN_OPT_IS_GENERATED , "AutoGenCxtion " },
  216. {NTDSCONN_OPT_TWOWAY_SYNC , "TwoWaySync " },
  217. {NTDSCONN_OPT_OVERRIDE_NOTIFY_DEFAULT , "OverrideNotifyDefault " },
  218. // {NTDSCONN_OPT_DISABLE_INTERSITE_COMPRESSION , "DisableIntersiteCompress " },
  219. // {NTDSCONN_OPT_USER_OWNED_SCHEDULE , "UserOwnedSchedule " },
  220. {NTDSCONN_OPT_IGNORE_SCHEDULE_MASK , "IgnoreSchedOnInitSync " },
  221. {0, NULL}
  222. };
  223. //
  224. // Name strings for config node object types. NOTE: Order must match enum in frs.h
  225. //
  226. PWCHAR DsConfigTypeName[] = {
  227. L" ",
  228. L"NTDS-Connection (in)",
  229. L"NTFRS-Member",
  230. L"NTFRS-Replica-Set",
  231. L"NTFRS-Settings",
  232. L"NTDS-Settings",
  233. L"NTFRS-Subscriber",
  234. L"NTFRS-Subscriptions",
  235. L"NTDS-DSA",
  236. L"COMPUTER",
  237. L"USER",
  238. L"SERVER",
  239. L"<<SERVICES_ROOT>>",
  240. L"<<Connection (Out)>>"
  241. };
  242. //
  243. // Client side ldap_connect timeout in seconds. Reg value "Ldap Bind Timeout In Seconds". Default is 30 seconds.
  244. //
  245. extern DWORD LdapBindTimeoutInSeconds;
  246. /******************************************************************************
  247. *******************************************************************************
  248. ** **
  249. ** **
  250. ** F R S _ L D A P _ S E A R C H _ C O N T E X T **
  251. ** **
  252. ** **
  253. *******************************************************************************
  254. ******************************************************************************/
  255. //
  256. // Client side ldap search timeout in minutes. Reg value "Ldap Search Timeout In Minutes". Default is 10 minutes.
  257. //
  258. extern DWORD LdapSearchTimeoutInMinutes;
  259. //
  260. // Ldap client timeout structure. Value is overwritten by the value of LdapSearchTimeoutInMinutes.
  261. //
  262. LDAP_TIMEVAL LdapTimeout = { 10 * 60 * 60, 0 }; //Default ldap timeout value. Overridden by registry param Ldap Search Timeout Value In Minutes
  263. #define FRS_LDAP_SEARCH_PAGESIZE 1000
  264. typedef struct _FRS_LDAP_SEARCH_CONTEXT {
  265. ULONG EntriesInPage; // Number of entries in the current page.
  266. ULONG CurrentEntry; // Location of the pointer into the page.
  267. LDAPMessage * LdapMsg; // Returned from ldap_search_ext_s()
  268. LDAPMessage * CurrentLdapMsg; // Current entry from current page.
  269. PWCHAR Filter; // Filter to add to the DS query.
  270. PWCHAR BaseDn; // Dn to start the query from.
  271. DWORD Scope; // Scope of the search.
  272. PWCHAR * Attrs; // Attributes requested by the search.
  273. } FRS_LDAP_SEARCH_CONTEXT, *PFRS_LDAP_SEARCH_CONTEXT;
  274. //
  275. // Registry Command codes for FrsDsEnumerateSysVolKeys()
  276. //
  277. #define REGCMD_CREATE_PRIMARY_DOMAIN (1)
  278. #define REGCMD_CREATE_MEMBERS (2)
  279. #define REGCMD_DELETE_MEMBERS (3)
  280. #define REGCMD_DELETE_KEYS (4)
  281. #define MK_ATTRS_1(_attr_, _a1) \
  282. _attr_[0] = _a1; _attr_[1] = NULL;
  283. #define MK_ATTRS_2(_attr_, _a1, _a2) \
  284. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = NULL;
  285. #define MK_ATTRS_3(_attr_, _a1, _a2, _a3) \
  286. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = NULL;
  287. #define MK_ATTRS_4(_attr_, _a1, _a2, _a3, _a4) \
  288. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = _a4; \
  289. _attr_[4] = NULL;
  290. #define MK_ATTRS_5(_attr_, _a1, _a2, _a3, _a4, _a5) \
  291. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = _a4; \
  292. _attr_[4] = _a5; _attr_[5] = NULL;
  293. #define MK_ATTRS_6(_attr_, _a1, _a2, _a3, _a4, _a5, _a6) \
  294. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = _a4; \
  295. _attr_[4] = _a5; _attr_[5] = _a6; _attr_[6] = NULL;
  296. #define MK_ATTRS_7(_attr_, _a1, _a2, _a3, _a4, _a5, _a6, _a7) \
  297. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = _a4; \
  298. _attr_[4] = _a5; _attr_[5] = _a6; _attr_[6] = _a7; _attr_[7] = NULL;
  299. #define MK_ATTRS_8(_attr_, _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8) \
  300. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = _a4; \
  301. _attr_[4] = _a5; _attr_[5] = _a6; _attr_[6] = _a7; _attr_[7] = _a8; \
  302. _attr_[8] = NULL;
  303. #define MK_ATTRS_9(_attr_, _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8, _a9) \
  304. _attr_[0] = _a1; _attr_[1] = _a2; _attr_[2] = _a3; _attr_[3] = _a4; \
  305. _attr_[4] = _a5; _attr_[5] = _a6; _attr_[6] = _a7; _attr_[7] = _a8; \
  306. _attr_[8] = _a9; _attr_[9] = NULL;
  307. //
  308. // Merging the information from the Ds with the active replicas.
  309. //
  310. CRITICAL_SECTION MergingReplicasWithDs;
  311. ULONG
  312. FrsProcessBackupRestore(
  313. VOID
  314. );
  315. RcsSetSysvolReady(
  316. IN DWORD NewSysvolReady
  317. );
  318. LONG
  319. PmInitPerfmonRegistryKeys (
  320. VOID
  321. );
  322. VOID
  323. DbgQueryDynamicConfigParams(
  324. );
  325. DWORD
  326. FrsDsGetRole(
  327. VOID
  328. );
  329. VOID
  330. FrsDsAddToPollSummary3ws(
  331. IN DWORD idsCode,
  332. IN PWCHAR WStr1,
  333. IN PWCHAR WStr2,
  334. IN PWCHAR WStr3
  335. )
  336. /*++
  337. Routine Description:
  338. Add to the poll summary event log.
  339. Arguments:
  340. idsCode - Code of data string from string.rc
  341. WStr1 - Argument1
  342. WStr2 - Argument2
  343. WStr3 - Argument3
  344. Return Value:
  345. None.
  346. --*/
  347. {
  348. #undef DEBSUB
  349. #define DEBSUB "FrsDsAddToPollSummary3ws:"
  350. PWCHAR ResStr = NULL;
  351. PWCHAR tempMessage = NULL;
  352. DWORD tempMessageLen = 0;
  353. ResStr = FrsGetResourceStr(idsCode);
  354. tempMessageLen = (wcslen(ResStr) - wcslen(L"%ws%ws%ws") +
  355. wcslen(WStr1) + wcslen(WStr2) +
  356. wcslen(WStr3) + 1) * sizeof(WCHAR);
  357. tempMessage = FrsAlloc(tempMessageLen);
  358. wsprintf(tempMessage, ResStr, WStr1, WStr2, WStr3);
  359. //
  360. // Don't want to copy the trailing null to the event log buffer or else
  361. // the next message will not be printed.
  362. //
  363. FRS_DS_ADD_TO_POLL_SUMMARY(DsPollSummaryBuf, tempMessage, tempMessageLen - 2);
  364. FrsFree(ResStr);
  365. FrsFree(tempMessage);
  366. return;
  367. }
  368. VOID
  369. FrsDsAddToPollSummary1ws(
  370. IN DWORD idsCode,
  371. IN PWCHAR WStr1
  372. )
  373. /*++
  374. Routine Description:
  375. Add to the poll summary event log.
  376. Arguments:
  377. idsCode - Code of data string from string.rc
  378. WStr1 - Argument1
  379. Return Value:
  380. None.
  381. --*/
  382. {
  383. #undef DEBSUB
  384. #define DEBSUB "FrsDsAddToPollSummary1ws:"
  385. PWCHAR ResStr = NULL;
  386. PWCHAR tempMessage = NULL;
  387. DWORD tempMessageLen = 0;
  388. ResStr = FrsGetResourceStr(idsCode);
  389. tempMessageLen = (wcslen(ResStr) - wcslen(L"%ws") +
  390. wcslen(WStr1) + 1) * sizeof(WCHAR);
  391. tempMessage = FrsAlloc(tempMessageLen);
  392. wsprintf(tempMessage, ResStr, WStr1);
  393. //
  394. // Don't want to copy the trailing null to the event log buffer or else
  395. // the next message will not be printed.
  396. //
  397. FRS_DS_ADD_TO_POLL_SUMMARY(DsPollSummaryBuf, tempMessage, tempMessageLen - 2);
  398. FrsFree(ResStr);
  399. FrsFree(tempMessage);
  400. return;
  401. }
  402. VOID
  403. FrsDsAddToPollSummary(
  404. IN DWORD idsCode
  405. )
  406. /*++
  407. Routine Description:
  408. Add to the poll summary event log.
  409. Arguments:
  410. idsCode - Code of data string from string.rc
  411. Return Value:
  412. None.
  413. --*/
  414. {
  415. #undef DEBSUB
  416. #define DEBSUB "FrsDsAddToPollSummary:"
  417. PWCHAR ResStr = NULL;
  418. ResStr = FrsGetResourceStr(idsCode);
  419. //
  420. // Don't want to copy the trailing null to the event log buffer or else
  421. // the next message will not be printed.
  422. //
  423. FRS_DS_ADD_TO_POLL_SUMMARY(DsPollSummaryBuf, ResStr, wcslen(ResStr) * sizeof(WCHAR));
  424. FrsFree(ResStr);
  425. return;
  426. }
  427. PVOID *
  428. FrsDsFindValues(
  429. IN PLDAP Ldap,
  430. IN PLDAPMessage Entry,
  431. IN PWCHAR DesiredAttr,
  432. IN BOOL DoBerVals
  433. )
  434. /*++
  435. Routine Description:
  436. Return the DS values for one attribute in an entry.
  437. Arguments:
  438. Ldap - An open, bound Ldap port.
  439. Entry - An Ldap entry returned by Ldap_search_s()
  440. DesiredAttr - Return values for this attribute.
  441. DoBerVals - Return the bervals (for binary data, v.s. WCHAR data)
  442. Return Value:
  443. An array of char pointers that represents the values for the attribute.
  444. The caller must free the array with LDAP_FREE_VALUES().
  445. NULL if unsuccessful.
  446. --*/
  447. {
  448. #undef DEBSUB
  449. #define DEBSUB "FrsDsFindValues:"
  450. PWCHAR Attr; // Retrieved from an Ldap entry
  451. BerElement *Ber; // Needed for scanning attributes
  452. //
  453. // Search the entry for the desired attribute
  454. //
  455. for (Attr = ldap_first_attribute(Ldap, Entry, &Ber);
  456. Attr != NULL;
  457. Attr = ldap_next_attribute(Ldap, Entry, Ber)) {
  458. if (WSTR_EQ(DesiredAttr, Attr)) {
  459. //
  460. // Return the values for DesiredAttr
  461. //
  462. if (DoBerVals) {
  463. return ldap_get_values_len(Ldap, Entry, Attr);
  464. } else {
  465. return ldap_get_values(Ldap, Entry, Attr);
  466. }
  467. }
  468. }
  469. return NULL;
  470. }
  471. PWCHAR
  472. FrsDsFindValue(
  473. IN PLDAP Ldap,
  474. IN PLDAPMessage Entry,
  475. IN PWCHAR DesiredAttr
  476. )
  477. /*++
  478. Routine Description:
  479. Return a copy of the first DS value for one attribute in an entry.
  480. Arguments:
  481. ldap - An open, bound ldap port.
  482. Entry - An ldap entry returned by ldap_search_s()
  483. DesiredAttr - Return values for this attribute.
  484. Return Value:
  485. A zero-terminated string or NULL if the attribute or its value
  486. doesn't exist. The string is freed with FREE_NO_HEADER().
  487. --*/
  488. {
  489. #undef DEBSUB
  490. #define DEBSUB "FrsDsFindValue:"
  491. PWCHAR Val;
  492. PWCHAR *Values;
  493. // Get ldap's array of values
  494. Values = (PWCHAR *)FrsDsFindValues(Ldap, Entry, DesiredAttr, FALSE);
  495. // Copy the first value (if any)
  496. Val = (Values) ? FrsWcsDup(Values[0]) : NULL;
  497. // Free ldap's array of values
  498. LDAP_FREE_VALUES(Values);
  499. return Val;
  500. }
  501. GUID *
  502. FrsDsFindGuid(
  503. IN PLDAP Ldap,
  504. IN PLDAPMessage LdapEntry
  505. )
  506. /*++
  507. Routine Description:
  508. Return a copy of the object's guid
  509. Arguments:
  510. ldap - An open, bound ldap port.
  511. Entry - An ldap entry returned by ldap_search_s()
  512. Return Value:
  513. The address of a guid or NULL. Free with FrsFree().
  514. --*/
  515. {
  516. #undef DEBSUB
  517. #define DEBSUB "FrsDsFindGuid:"
  518. GUID *Guid;
  519. PLDAP_BERVAL *Values;
  520. // Get ldap's array of values
  521. Values = (PLDAP_BERVAL *)FrsDsFindValues(Ldap, LdapEntry, ATTR_OBJECT_GUID, TRUE);
  522. // Copy the first value (if any)
  523. Guid = (Values) ? FrsDupGuid((GUID *)Values[0]->bv_val) : NULL;
  524. // Free ldap's array of values
  525. LDAP_FREE_BER_VALUES(Values);
  526. return Guid;
  527. }
  528. PSCHEDULE
  529. FrsDsFindSchedule(
  530. IN PLDAP Ldap,
  531. IN PLDAPMessage LdapEntry,
  532. OUT PULONG Len
  533. )
  534. /*++
  535. Routine Description:
  536. Return a copy of the object's schedule
  537. Arguments:
  538. Ldap - An open, bound ldap port.
  539. LdapEntry - An ldap entry returned by ldap_search_s()
  540. Len - length of schedule blob
  541. Return Value:
  542. The address of a schedule or NULL. Free with FrsFree().
  543. --*/
  544. {
  545. #undef DEBSUB
  546. #define DEBSUB "FrsDsFindSchedule:"
  547. PLDAP_BERVAL *Values;
  548. PSCHEDULE Schedule;
  549. //
  550. // Get ldap's array of values
  551. //
  552. Values = (PLDAP_BERVAL *)FrsDsFindValues(Ldap, LdapEntry, ATTR_SCHEDULE, TRUE);
  553. if (!Values)
  554. return NULL;
  555. //
  556. // Return a copy of the schedule
  557. //
  558. *Len = Values[0]->bv_len;
  559. if (*Len) {
  560. //
  561. // Need to check if *Len == 0 as FrsAlloc asserts if called with 0 as the first parameter (prefix fix).
  562. //
  563. Schedule = FrsAlloc(*Len);
  564. CopyMemory(Schedule, Values[0]->bv_val, *Len);
  565. } else {
  566. Schedule = NULL;
  567. }
  568. LDAP_FREE_BER_VALUES(Values);
  569. return Schedule;
  570. }
  571. BOOL
  572. FrsDsLdapSearch(
  573. IN PLDAP Ldap,
  574. IN PWCHAR Base,
  575. IN ULONG Scope,
  576. IN PWCHAR Filter,
  577. IN PWCHAR Attrs[],
  578. IN ULONG AttrsOnly,
  579. IN LDAPMessage **Msg
  580. )
  581. /*++
  582. Routine Description:
  583. Issue the ldap ldap_search_s call, check for errors, and check for
  584. a shutdown in progress.
  585. Arguments:
  586. ldap Session handle to Ldap server.
  587. Base The distinguished name of the entry at which to start the search
  588. Scope
  589. LDAP_SCOPE_BASE Search the base entry only.
  590. LDAP_SCOPE_ONELEVEL Search the base entry and all entries in the first
  591. level below the base.
  592. LDAP_SCOPE_SUBTREE Search the base entry and all entries in the tree
  593. below the base.
  594. Filter The search filter.
  595. Attrs A null-terminated array of strings indicating the attributes
  596. to return for each matching entry. Pass NULL to retrieve all
  597. available attributes.
  598. AttrsOnly A boolean value that should be zero if both attribute types
  599. and values are to be returned, nonzero if only types are wanted.
  600. mSG Contains the results of the search upon completion of the call.
  601. The ldap array of values or NULL if the Base, DesiredAttr, or its
  602. values does not exist.
  603. The ldap array is freed with LDAP_FREE_VALUES().
  604. Return Value:
  605. TRUE if not shutting down.
  606. --*/
  607. {
  608. #undef DEBSUB
  609. #define DEBSUB "FrsDsLdapSearch:"
  610. DWORD LStatus;
  611. *Msg = NULL;
  612. //
  613. // Increment the DS Searches counter
  614. //
  615. PM_INC_CTR_SERVICE(PMTotalInst, DSSearches, 1);
  616. //
  617. // Issue the ldap search
  618. //
  619. // LStatus = ldap_search_s(Ldap, Base, Scope, Filter, Attrs, AttrsOnly, Msg);
  620. LStatus = ldap_search_ext_s(Ldap,
  621. Base,
  622. Scope,
  623. Filter,
  624. Attrs,
  625. AttrsOnly,
  626. NULL,
  627. NULL,
  628. &LdapTimeout,
  629. 0,
  630. Msg);
  631. //
  632. // Check for errors
  633. //
  634. if (LStatus != LDAP_SUCCESS) {
  635. PWCHAR ldapErrorString = NULL;
  636. DPRINT2_LS(1, ":DS: WARN - Error searching %ws for %ws;", Base, Filter, LStatus);
  637. //
  638. // Increment the DS Searches in Error counter
  639. //
  640. PM_INC_CTR_SERVICE(PMTotalInst, DSSearchesError, 1);
  641. //
  642. // Add to the poll summary event log.
  643. //
  644. ldapErrorString = ldap_err2string(LStatus);
  645. if(ldapErrorString) {
  646. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_SEARCH_ERROR, Filter, Base,
  647. ldapErrorString);
  648. }
  649. LDAP_FREE_MSG(*Msg);
  650. return FALSE;
  651. }
  652. //
  653. // Return FALSE if shutting down.
  654. //
  655. if (FrsIsShuttingDown || DsIsShuttingDown) {
  656. LDAP_FREE_MSG(*Msg);
  657. return FALSE;
  658. }
  659. return TRUE;
  660. }
  661. BOOL
  662. FrsDsLdapSearchInit(
  663. PLDAP ldap,
  664. PWCHAR Base,
  665. ULONG Scope,
  666. PWCHAR Filter,
  667. PWCHAR Attrs[],
  668. ULONG AttrsOnly,
  669. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  670. )
  671. /*++
  672. Routine Description:
  673. Issue the ldap_create_page_control and ldap_search_ext_s calls,
  674. FrsDsLdapSearchInit(), and FrsDsLdapSearchNext() APIs are used to
  675. make ldap queries and retrieve the results in paged form.
  676. Arguments:
  677. ldap Session handle to Ldap server.
  678. Base The distinguished name of the entry at which to start the search.
  679. A copy of base is kept in the context.
  680. Scope
  681. LDAP_SCOPE_BASE Search the base entry only.
  682. LDAP_SCOPE_ONELEVEL Search the base entry and all entries in the first
  683. level below the base.
  684. LDAP_SCOPE_SUBTREE Search the base entry and all entries in the tree
  685. below the base.
  686. Filter The search filter. A copy of filter is kept in the context.
  687. Attrs A null-terminated array of strings indicating the attributes
  688. to return for each matching entry. Pass NULL to retrieve all
  689. available attributes.
  690. AttrsOnly A boolean value that should be zero if both attribute types
  691. and values are to be returned, nonzero if only types are wanted.
  692. FrsSearchContext
  693. An opaques structure that links the FrsDsLdapSearchInit() and
  694. FrsDsLdapSearchNext() calls together. The structure contains
  695. the information required to retrieve query results across pages.
  696. Return Value:
  697. BOOL result.
  698. --*/
  699. {
  700. #undef DEBSUB
  701. #define DEBSUB "FrsDsLdapSearchInit:"
  702. DWORD LStatus = LDAP_SUCCESS;
  703. PLDAPControl ServerControls[2];
  704. PLDAPControl ServerControl = NULL;
  705. UINT i;
  706. LDAP_BERVAL cookie1 = { 0, NULL };
  707. FrsSearchContext->LdapMsg = NULL;
  708. FrsSearchContext->CurrentLdapMsg = NULL;
  709. FrsSearchContext->EntriesInPage = 0;
  710. FrsSearchContext->CurrentEntry = 0;
  711. FrsSearchContext->BaseDn = FrsWcsDup(Base);
  712. FrsSearchContext->Filter = FrsWcsDup(Filter);
  713. FrsSearchContext->Scope = Scope;
  714. FrsSearchContext->Attrs = Attrs;
  715. LStatus = ldap_create_page_control(ldap,
  716. FRS_LDAP_SEARCH_PAGESIZE,
  717. &cookie1,
  718. FALSE, // is critical
  719. &ServerControl
  720. );
  721. ServerControls[0] = ServerControl;
  722. ServerControls[1] = NULL;
  723. if (LStatus != LDAP_SUCCESS) {
  724. DPRINT2_LS(2, ":DS: WARN - Error creating page control %ws for %ws;", Base, Filter, LStatus);
  725. FrsSearchContext->BaseDn = FrsFree(FrsSearchContext->BaseDn);
  726. FrsSearchContext->Filter = FrsFree(FrsSearchContext->Filter);
  727. return FALSE;
  728. }
  729. LStatus = ldap_search_ext_s(ldap,
  730. FrsSearchContext->BaseDn,
  731. FrsSearchContext->Scope,
  732. FrsSearchContext->Filter,
  733. FrsSearchContext->Attrs,
  734. FALSE,
  735. ServerControls,
  736. NULL,
  737. &LdapTimeout,
  738. 0,
  739. &FrsSearchContext->LdapMsg);
  740. ldap_control_free(ServerControl);
  741. if (LStatus == LDAP_SUCCESS) {
  742. FrsSearchContext->EntriesInPage = ldap_count_entries(ldap, FrsSearchContext->LdapMsg);
  743. FrsSearchContext->CurrentEntry = 0;
  744. }
  745. if (LStatus != LDAP_SUCCESS) {
  746. DPRINT2_LS(2, ":DS: WARN - Error searching %ws for %ws;", Base, Filter, LStatus);
  747. FrsSearchContext->BaseDn = FrsFree(FrsSearchContext->BaseDn);
  748. FrsSearchContext->Filter = FrsFree(FrsSearchContext->Filter);
  749. return FALSE;
  750. }
  751. return TRUE;
  752. }
  753. PLDAPMessage
  754. FrsDsLdapSearchGetNextEntry(
  755. PLDAP ldap,
  756. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  757. )
  758. /*++
  759. Routine Description:
  760. Get the next entry form the current page of the results
  761. returned. This call is only made if there is a entry
  762. in the current page.
  763. Arguments:
  764. ldap Session handle to Ldap server.
  765. FrsSearchContext
  766. An opaques structure that links the FrsDsLdapSearchInit() and
  767. FrsDsLdapSearchNext() calls together. The structure contains
  768. the information required to retrieve query results across pages.
  769. Return Value:
  770. The first or the next entry from the current page.
  771. --*/
  772. {
  773. #undef DEBSUB
  774. #define DEBSUB "FrsDsLdapSearchGetNextEntry:"
  775. FrsSearchContext->CurrentEntry += 1;
  776. if ( FrsSearchContext->CurrentEntry == 1 ) {
  777. FrsSearchContext->CurrentLdapMsg = ldap_first_entry(ldap ,FrsSearchContext->LdapMsg);
  778. } else {
  779. FrsSearchContext->CurrentLdapMsg = ldap_next_entry(ldap ,FrsSearchContext->CurrentLdapMsg);
  780. }
  781. return FrsSearchContext->CurrentLdapMsg;
  782. }
  783. DWORD
  784. FrsDsLdapSearchGetNextPage(
  785. PLDAP ldap,
  786. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  787. )
  788. /*++
  789. Routine Description:
  790. Get the next page from the results returned by ldap_search_ext_s..
  791. Arguments:
  792. ldap Session handle to Ldap server.
  793. FrsSearchContext
  794. An opaques structure that links the FrsDsLdapSearchInit() and
  795. FrsDsLdapSearchNext() calls together. The structure contains
  796. the information required to retrieve query results across pages.
  797. Return Value:
  798. WINSTATUS
  799. --*/
  800. {
  801. #undef DEBSUB
  802. #define DEBSUB "FrsDsLdapSearchGetNextPage:"
  803. DWORD LStatus = LDAP_SUCCESS;
  804. LDAP_BERVAL * CurrCookie = NULL;
  805. PLDAPControl * CurrControls = NULL;
  806. ULONG retcode = 0;
  807. ULONG TotalEntries = 0;
  808. PLDAPControl ServerControls[2];
  809. PLDAPControl ServerControl= NULL;
  810. // Get the server control from the message, and make a new control with the cookie from the server
  811. LStatus = ldap_parse_result(ldap, FrsSearchContext->LdapMsg, &retcode,NULL,NULL,NULL,&CurrControls,FALSE);
  812. LDAP_FREE_MSG(FrsSearchContext->LdapMsg);
  813. if (LStatus != LDAP_SUCCESS) {
  814. DPRINT2_LS(2, ":DS: WARN - Error in ldap_parse_result %ws for %ws;", FrsSearchContext->BaseDn, FrsSearchContext->Filter, LStatus);
  815. return LdapMapErrorToWin32(LStatus);
  816. }
  817. LStatus = ldap_parse_page_control(ldap, CurrControls, &TotalEntries, &CurrCookie);
  818. if (LStatus != LDAP_SUCCESS) {
  819. DPRINT2_LS(2, ":DS: WARN - Error in ldap_parse_page_control %ws for %ws;", FrsSearchContext->BaseDn, FrsSearchContext->Filter, LStatus);
  820. return LdapMapErrorToWin32(LStatus);
  821. }
  822. if ( CurrCookie->bv_len == 0 && CurrCookie->bv_val == 0 ) {
  823. LStatus = LDAP_CONTROL_NOT_FOUND;
  824. ldap_controls_free(CurrControls);
  825. ber_bvfree(CurrCookie);
  826. return LdapMapErrorToWin32(LStatus);
  827. }
  828. LStatus = ldap_create_page_control(ldap,
  829. FRS_LDAP_SEARCH_PAGESIZE,
  830. CurrCookie,
  831. FALSE,
  832. &ServerControl);
  833. ServerControls[0] = ServerControl;
  834. ServerControls[1] = NULL;
  835. ldap_controls_free(CurrControls);
  836. CurrControls = NULL;
  837. ber_bvfree(CurrCookie);
  838. CurrCookie = NULL;
  839. if (LStatus != LDAP_SUCCESS) {
  840. DPRINT2_LS(2, ":DS: WARN - Error in ldap_parse_page_control %ws for %ws;", FrsSearchContext->BaseDn, FrsSearchContext->Filter, LStatus);
  841. return LdapMapErrorToWin32(LStatus);
  842. }
  843. // continue the search with the new cookie
  844. LStatus = ldap_search_ext_s(ldap,
  845. FrsSearchContext->BaseDn,
  846. FrsSearchContext->Scope,
  847. FrsSearchContext->Filter,
  848. FrsSearchContext->Attrs,
  849. FALSE,
  850. ServerControls,
  851. NULL,
  852. &LdapTimeout,
  853. 0,
  854. &FrsSearchContext->LdapMsg);
  855. ldap_control_free(ServerControl);
  856. //
  857. // LDAP_CONTROL_NOT_FOUND means that we have reached the end of the search results
  858. //
  859. if ( (LStatus != LDAP_SUCCESS) && (LStatus != LDAP_CONTROL_NOT_FOUND) ) {
  860. DPRINT2_LS(2, ":DS: WARN - Error searching %ws for %ws;", FrsSearchContext->BaseDn, FrsSearchContext->Filter, LStatus);
  861. }
  862. if (LStatus == LDAP_SUCCESS) {
  863. FrsSearchContext->EntriesInPage = ldap_count_entries(ldap, FrsSearchContext->LdapMsg);
  864. FrsSearchContext->CurrentEntry = 0;
  865. }
  866. return LdapMapErrorToWin32(LStatus);
  867. }
  868. PLDAPMessage
  869. FrsDsLdapSearchNext(
  870. PLDAP ldap,
  871. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  872. )
  873. /*++
  874. Routine Description:
  875. Get the next entry form the current page of the results
  876. returned or from the next page if we are at the end of the.
  877. current page.
  878. Arguments:
  879. ldap Session handle to Ldap server.
  880. FrsSearchContext
  881. An opaques structure that links the FrsDsLdapSearchInit() and
  882. FrsDsLdapSearchNext() calls together. The structure contains
  883. the information required to retrieve query results across pages.
  884. Return Value:
  885. The next entry on this page or the first entry from the next page.
  886. NULL if there are no more entries to return.
  887. --*/
  888. {
  889. #undef DEBSUB
  890. #define DEBSUB "FrsDsLdapSearchNext:"
  891. DWORD WStatus = ERROR_SUCCESS;
  892. PLDAPMessage NextEntry = NULL;
  893. if (FrsSearchContext->EntriesInPage > FrsSearchContext->CurrentEntry )
  894. {
  895. // return the next entry from the current page
  896. return FrsDsLdapSearchGetNextEntry(ldap, FrsSearchContext);
  897. }
  898. else
  899. {
  900. // see if there are more pages of results to get
  901. WStatus = FrsDsLdapSearchGetNextPage(ldap, FrsSearchContext);
  902. if (WStatus == ERROR_SUCCESS)
  903. {
  904. return FrsDsLdapSearchGetNextEntry(ldap, FrsSearchContext);
  905. }
  906. }
  907. return NextEntry;
  908. }
  909. VOID
  910. FrsDsLdapSearchClose(
  911. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  912. )
  913. /*++
  914. Routine Description:
  915. The search is complete. Free the elemetns of the context and reset
  916. them so the same context can be used for another search.
  917. Arguments:
  918. FrsSearchContext
  919. An opaques structure that links the FrsDsLdapSearchInit() and
  920. FrsDsLdapSearchNext() calls together. The structure contains
  921. the information required to retrieve query results across pages.
  922. Return Value:
  923. NONE
  924. --*/
  925. {
  926. #undef DEBSUB
  927. #define DEBSUB "FrsDsLdapSearchClose:"
  928. FrsSearchContext->EntriesInPage = 0;
  929. FrsSearchContext->CurrentEntry = 0;
  930. FrsSearchContext->BaseDn = FrsFree(FrsSearchContext->BaseDn);
  931. FrsSearchContext->Filter = FrsFree(FrsSearchContext->Filter);
  932. LDAP_FREE_MSG(FrsSearchContext->LdapMsg);
  933. }
  934. PWCHAR *
  935. FrsDsGetValues(
  936. IN PLDAP Ldap,
  937. IN PWCHAR Base,
  938. IN PWCHAR DesiredAttr
  939. )
  940. /*++
  941. Routine Description:
  942. Return all of the DS values for one attribute in an object.
  943. Arguments:
  944. ldap - An open, bound ldap port.
  945. Base - The "pathname" of a DS object.
  946. DesiredAttr - Return values for this attribute.
  947. Return Value:
  948. The ldap array of values or NULL if the Base, DesiredAttr, or its values
  949. does not exist. The ldap array is freed with LDAP_FREE_VALUES().
  950. --*/
  951. {
  952. #undef DEBSUB
  953. #define DEBSUB "FrsDsGetValues:"
  954. PLDAPMessage Msg = NULL; // Opaque stuff from ldap subsystem
  955. PWCHAR *Values; // Array of values for desired attribute
  956. //
  957. // Search Base for all of this attribute + values (objectCategory=*)
  958. //
  959. if (!FrsDsLdapSearch(Ldap, Base, LDAP_SCOPE_BASE, CATEGORY_ANY,
  960. NULL, 0, &Msg)) {
  961. return NULL;
  962. }
  963. //
  964. // Return the values for the desired attribute
  965. //
  966. Values = (PWCHAR *)FrsDsFindValues(Ldap,
  967. ldap_first_entry(Ldap, Msg),
  968. DesiredAttr,
  969. FALSE);
  970. LDAP_FREE_MSG(Msg);
  971. return Values;
  972. }
  973. PWCHAR
  974. FrsDsExtendDn(
  975. IN PWCHAR Dn,
  976. IN PWCHAR Cn
  977. )
  978. /*++
  979. Routine Description:
  980. Extend an existing DN with a new CN= component.
  981. Arguments:
  982. Dn - distinguished name
  983. Cn - common name
  984. Return Value:
  985. CN=Cn,Dn
  986. --*/
  987. {
  988. #undef DEBSUB
  989. #define DEBSUB "FrsDsExtendDn:"
  990. ULONG Len;
  991. PWCHAR NewDn;
  992. if ((Dn == NULL) || (Cn == NULL)) {
  993. return NULL;
  994. }
  995. Len = wcslen(L"CN=,") + wcslen(Dn) + wcslen(Cn) + 1;
  996. NewDn = (PWCHAR)FrsAlloc(Len * sizeof(WCHAR));
  997. wcscpy(NewDn, L"CN=");
  998. wcscat(NewDn, Cn);
  999. wcscat(NewDn, L",");
  1000. wcscat(NewDn, Dn);
  1001. return NewDn;
  1002. }
  1003. PWCHAR
  1004. FrsDsExtendDnOu(
  1005. IN PWCHAR Dn,
  1006. IN PWCHAR Ou
  1007. )
  1008. /*++
  1009. Routine Description:
  1010. Extend an existing DN with a new OU= component.
  1011. Arguments:
  1012. Dn - distinguished name
  1013. Ou - orginizational name
  1014. Return Value:
  1015. OU=Ou,Dn
  1016. --*/
  1017. {
  1018. #undef DEBSUB
  1019. #define DEBSUB "FrsDsExtendDnOu:"
  1020. ULONG Len;
  1021. PWCHAR NewDn;
  1022. if ((Dn == NULL) || (Ou == NULL)) {
  1023. return NULL;
  1024. }
  1025. Len = wcslen(L"OU=,") + wcslen(Dn) + wcslen(Ou) + 1;
  1026. NewDn = (PWCHAR)FrsAlloc(Len * sizeof(WCHAR));
  1027. wcscpy(NewDn, L"OU=");
  1028. wcscat(NewDn, Ou);
  1029. wcscat(NewDn, L",");
  1030. wcscat(NewDn, Dn);
  1031. return NewDn;
  1032. }
  1033. PWCHAR
  1034. FrsDsMakeRdn(
  1035. IN PWCHAR DN
  1036. )
  1037. /*++
  1038. Routine Description:
  1039. Extract the base component (relative distinguished name) from a
  1040. distinguished name. The distinguished name is assumed to be in
  1041. DS format (CN=xyz,CN=next one,...). In this case, the returned
  1042. RDN is "xyz".
  1043. Arguments:
  1044. DN - distinguished name
  1045. Return Value:
  1046. A zero-terminated string. The string is freed with FrsFree().
  1047. --*/
  1048. {
  1049. #undef DEBSUB
  1050. #define DEBSUB "FrsDsMakeRdn:"
  1051. DWORD RDNLen;
  1052. PWCHAR RDN;
  1053. if (DN == NULL) {
  1054. return NULL;
  1055. }
  1056. //
  1057. // Skip the first CN=; if any
  1058. //
  1059. RDN = wcsstr(DN, L"cn=");
  1060. if (RDN == DN) {
  1061. DN += 3;
  1062. }
  1063. // Return the string up to the first delimiter or EOS
  1064. RDNLen = wcscspn(DN, L",");
  1065. RDN = (PWCHAR)FrsAlloc(sizeof(WCHAR) * (RDNLen + 1));
  1066. wcsncpy(RDN, DN, RDNLen);
  1067. RDN[RDNLen] = L'\0';
  1068. return _wcsupr(RDN);
  1069. }
  1070. VOID
  1071. FrsDsCloseDs(
  1072. VOID
  1073. )
  1074. /*++
  1075. Routine Description:
  1076. Unbind from the DS.
  1077. Arguments:
  1078. None.
  1079. Return Value:
  1080. None.
  1081. --*/
  1082. {
  1083. #undef DEBSUB
  1084. #define DEBSUB "FrsDsCloseDs:"
  1085. DsBindingsAreValid = FALSE;
  1086. if (gLdap) {
  1087. ldap_unbind_s(gLdap);
  1088. gLdap = NULL;
  1089. }
  1090. if (HANDLE_IS_VALID(DsHandle)) {
  1091. DsUnBind(&DsHandle);
  1092. DsHandle = NULL;
  1093. }
  1094. SitesDn = FrsFree(SitesDn);
  1095. ServicesDn = FrsFree(ServicesDn);
  1096. SystemDn = FrsFree(SystemDn);
  1097. ComputersDn = FrsFree(ComputersDn);
  1098. DomainControllersDn = FrsFree(DomainControllersDn);
  1099. DefaultNcDn = FrsFree(DefaultNcDn);
  1100. }
  1101. DWORD
  1102. FrsDsGetDcInfo(
  1103. IN PDOMAIN_CONTROLLER_INFO *DcInfo,
  1104. IN DWORD Flags
  1105. )
  1106. /*++
  1107. Routine Description:
  1108. Open and bind to a dc
  1109. Arguments:
  1110. DcInfo - Dc Info
  1111. Flags - DsGetDcName(Flags)
  1112. Return Value:
  1113. DsGetDcName
  1114. --*/
  1115. {
  1116. #undef DEBSUB
  1117. #define DEBSUB "FrsDsGetDcInfo:"
  1118. DWORD WStatus;
  1119. PWCHAR DcName;
  1120. DWORD InfoFlags;
  1121. CHAR FlagBuffer[220];
  1122. FrsFlagsToStr(Flags, DsGetDcNameFlagNameTable, sizeof(FlagBuffer), FlagBuffer);
  1123. DPRINT2(4, ":DS: DsGetDcName (%08x) Flags [%s]\n", Flags, FlagBuffer);
  1124. WStatus = DsGetDcName(NULL, // Computer to remote to
  1125. NULL, // Domain - use our own
  1126. NULL, // Domain Guid
  1127. NULL, // Site Guid
  1128. Flags,
  1129. DcInfo); // Return info
  1130. CLEANUP1_WS(0, ":DS: ERROR - Could not get DC Info for %ws;",
  1131. ComputerName, WStatus, RETURN);
  1132. DcName = (*DcInfo)->DomainControllerName;
  1133. FrsFlagsToStr(Flags, DsGetDcNameFlagNameTable, sizeof(FlagBuffer), FlagBuffer);
  1134. DPRINT2(4, ":DS: DcInfo (flags are %08x) Flags [%s]\n", Flags, FlagBuffer);
  1135. DPRINT1(4, ":DS: DomainControllerName : %ws\n", DcName);
  1136. DPRINT1(4, ":DS: DomainControllerAddress: %ws\n", (*DcInfo)->DomainControllerAddress);
  1137. DPRINT1(4, ":DS: DomainControllerType : %08x\n",(*DcInfo)->DomainControllerAddressType);
  1138. DPRINT1(4, ":DS: DomainName : %ws\n", (*DcInfo)->DomainName);
  1139. DPRINT1(4, ":DS: DnsForestName : %ws\n", (*DcInfo)->DnsForestName);
  1140. DPRINT1(4, ":DS: DcSiteName : %ws\n", (*DcInfo)->DcSiteName);
  1141. DPRINT1(4, ":DS: ClientSiteName : %ws\n", (*DcInfo)->ClientSiteName);
  1142. InfoFlags = (*DcInfo)->Flags;
  1143. FrsFlagsToStr(InfoFlags, DsGetDcInfoFlagNameTable, sizeof(FlagBuffer), FlagBuffer);
  1144. DPRINT2(4, ":DS: InfoFlags : %08x Flags [%s]\n",InfoFlags, FlagBuffer);
  1145. DsDomainControllerName = FrsFree(DsDomainControllerName);
  1146. DsDomainControllerName = FrsWcsDup(DcName);
  1147. //
  1148. // DCs should bind to the local DS to avoid ACL problems.
  1149. //
  1150. if (IsADc && DcName && (wcslen(DcName) > 2) &&
  1151. _wcsnicmp(&DcName[2], ComputerName, wcslen(ComputerName))) {
  1152. DPRINT3(0, ":DS: ERROR - The DC %ws is using the DS on DC %ws "
  1153. "Some of the information in the DS"
  1154. " may be unavailable to %ws; possibly disabling "
  1155. "replication with some partners.\n",
  1156. ComputerName, &DcName[2], ComputerName);
  1157. }
  1158. RETURN:
  1159. return WStatus;
  1160. }
  1161. VOID
  1162. FrsDsRegisterSpn(
  1163. IN PLDAP Ldap,
  1164. IN PCONFIG_NODE Computer
  1165. )
  1166. /*++
  1167. Routine Description:
  1168. Register the NtFrs SPN so that authenticated RPC calls can
  1169. use SPN/FQDN as the target principal name
  1170. Arguments:
  1171. Computer - Computer node from the ds
  1172. Return Value:
  1173. None.
  1174. --*/
  1175. {
  1176. #undef DEBSUB
  1177. #define DEBSUB "FrsDsRegisterSpn:"
  1178. DWORD WStatus;
  1179. PWCHAR Spn = NULL;
  1180. PWCHAR SpnPrefix= NULL;
  1181. PWCHAR *SpnList = NULL;
  1182. DWORD SpnNum = 0;
  1183. static BOOL RegisteredSpn = FALSE;
  1184. //
  1185. // No Ds binding or no computer or already registered
  1186. //
  1187. if (RegisteredSpn ||
  1188. (ComputerDnsName[0] == L'\0') ||
  1189. !DsBindingsAreValid ||
  1190. !Computer ||
  1191. !Computer->Dn) {
  1192. return;
  1193. }
  1194. //
  1195. // Register the NtFrs SPN so that authenticated RPC calls can
  1196. // use SPN/FQDN as the target principal name
  1197. //
  1198. Spn = FrsAlloc((wcslen(ComputerDnsName) + wcslen(SERVICE_PRINCIPAL_NAME) + 2) * sizeof(WCHAR));
  1199. wcscpy(Spn, SERVICE_PRINCIPAL_NAME);
  1200. wcscat(Spn, L"/");
  1201. wcscat(Spn, ComputerDnsName);
  1202. SpnPrefix = FrsAlloc((wcslen(SERVICE_PRINCIPAL_NAME) + 1) * sizeof(WCHAR));
  1203. wcscpy(SpnPrefix, SERVICE_PRINCIPAL_NAME);
  1204. SpnList = FrsDsGetValues(Ldap, Computer->Dn, ATTR_SERVICE_PRINCIPAL_NAME);
  1205. SpnNum=0;
  1206. while ((SpnList != NULL)&& (SpnList[SpnNum] != NULL)) {
  1207. DPRINT2(5, "Spn list from DS[%d] = %ws\n", SpnNum, SpnList[SpnNum]);
  1208. if (!_wcsicmp(SpnList[SpnNum], Spn)) {
  1209. // Spn found for NtFrs.
  1210. DPRINT1(4, "SPN already registered for Ntfrs: %ws\n", SpnList[SpnNum]);
  1211. RegisteredSpn = TRUE;
  1212. } else if (!_wcsnicmp(SpnList[SpnNum], SpnPrefix, wcslen(SpnPrefix))) {
  1213. //
  1214. // An older SPN exists. Delete it.
  1215. //
  1216. DPRINT1(4, "Deleting stale SPN for Ntfrs: %ws\n", SpnList[SpnNum]);
  1217. WStatus = DsWriteAccountSpn(DsHandle, DS_SPN_DELETE_SPN_OP, Computer->Dn, 1, &SpnList[SpnNum]);
  1218. if (!WIN_SUCCESS(WStatus)) {
  1219. DPRINT2_WS(1, "WARN - Delete DsWriteAccountSpn(%ws, %ws);", SpnList[SpnNum], Computer->Dn, WStatus);
  1220. } else {
  1221. DPRINT2(5, "Delete DsWriteAccountSpn(%ws, %ws); success\n", SpnList[SpnNum], Computer->Dn);
  1222. }
  1223. }
  1224. ++SpnNum;
  1225. }
  1226. if (!RegisteredSpn) {
  1227. DPRINT1(4, "Registering SPN for Ntfrs; %ws\n", Spn);
  1228. WStatus = DsWriteAccountSpn(DsHandle, DS_SPN_ADD_SPN_OP, Computer->Dn, 1, &Spn);
  1229. if (!WIN_SUCCESS(WStatus)) {
  1230. DPRINT2_WS(1, "WARN - Add DsWriteAccountSpn(%ws, %ws);", Spn, Computer->Dn, WStatus);
  1231. } else {
  1232. DPRINT2(5, "Add DsWriteAccountSpn(%ws, %ws); success\n", Spn, Computer->Dn);
  1233. RegisteredSpn = TRUE;
  1234. }
  1235. }
  1236. FrsFree(Spn);
  1237. FrsFree(SpnPrefix);
  1238. //
  1239. // Free ldap's array of values
  1240. //
  1241. LDAP_FREE_VALUES(SpnList);
  1242. }
  1243. BOOL
  1244. FrsDsBindDs(
  1245. IN DWORD Flags
  1246. )
  1247. /*++
  1248. Routine Description:
  1249. Open and bind to a domain controller.
  1250. Arguments:
  1251. Flags - For FrsDsGetDcInfo()
  1252. Return Value:
  1253. None. Sets global handles for FrsDsOpenDs().
  1254. --*/
  1255. {
  1256. #undef DEBSUB
  1257. #define DEBSUB "FrsDsBindDs:"
  1258. DWORD WStatus;
  1259. DWORD LStatus = LDAP_SUCCESS;
  1260. PWCHAR DcAddr;
  1261. PWCHAR DcName;
  1262. PWCHAR DcDnsName;
  1263. BOOL Bound = FALSE;
  1264. PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
  1265. struct l_timeval Timeout;
  1266. #define MAX_DC_NAMELIST 8
  1267. ULONG NameListx, i;
  1268. PWCHAR NameList[MAX_DC_NAMELIST];
  1269. ULONG ulOptions;
  1270. //
  1271. // Bind to the local DS if this computer is a DC
  1272. //
  1273. gLdap = NULL;
  1274. if (IsADc) {
  1275. DcAddr = NULL;
  1276. DcName = ComputerName;
  1277. DcDnsName = ComputerDnsName;
  1278. } else {
  1279. //
  1280. // Not a DC; find any DC for this domain
  1281. //
  1282. WStatus = FrsDsGetDcInfo(&DcInfo, Flags);
  1283. CLEANUP2_WS(0, ":DS: ERROR - FrsDsGetDcInfo(%08x, %ws);",
  1284. Flags, ComputerName, WStatus, CLEANUP);
  1285. //
  1286. // Binding address
  1287. //
  1288. DcAddr = DcInfo->DomainControllerAddress;
  1289. DcName = NULL;
  1290. DcDnsName = DcInfo->DomainControllerName;
  1291. }
  1292. FRS_ASSERT(DcDnsName || DcName || DcAddr);
  1293. //
  1294. // Open the ldap server using various forms of the DC's name
  1295. //
  1296. NameListx = 0;
  1297. if (DcDnsName &&
  1298. (wcslen(DcDnsName) > 2) && DcDnsName[0] == L'\\' && DcDnsName[1] == L'\\') {
  1299. // Trim the "\\"
  1300. NameList[NameListx++] = DcDnsName + 2;
  1301. }
  1302. if (DcAddr &&
  1303. (wcslen(DcAddr) > 2) && DcAddr[0] == L'\\' && DcAddr[1] == L'\\') {
  1304. // Trim the "\\"
  1305. NameList[NameListx++] = DcAddr + 2;
  1306. }
  1307. NameList[NameListx++] = DcDnsName;
  1308. NameList[NameListx++] = DcName;
  1309. NameList[NameListx++] = DcAddr;
  1310. FRS_ASSERT(NameListx <= MAX_DC_NAMELIST);
  1311. ulOptions = PtrToUlong(LDAP_OPT_ON);
  1312. Timeout.tv_sec = LdapBindTimeoutInSeconds;
  1313. Timeout.tv_usec = 0;
  1314. for (i=0; i<NameListx; i++) {
  1315. if (NameList[i] != NULL) {
  1316. //
  1317. // if ldap_open is called with a server name the api will call DsGetDcName
  1318. // passing the server name as the domainname parm...bad, because
  1319. // DsGetDcName will make a load of DNS queries based on the server name,
  1320. // it is designed to construct these queries from a domain name...so all
  1321. // these queries will be bogus, meaning they will waste network bandwidth,
  1322. // time to fail, and worst case cause expensive on demand links to come up
  1323. // as referrals/forwarders are contacted to attempt to resolve the bogus
  1324. // names. By setting LDAP_OPT_AREC_EXCLUSIVE to on using ldap_set_option
  1325. // after the ldap_init but before any other operation using the ldap
  1326. // handle from ldap_init, the delayed connection setup will not call
  1327. // DsGetDcName, just gethostbyname, or if an IP is passed, the ldap client
  1328. // will detect that and use the address directly.
  1329. //
  1330. // gLdap = ldap_open(NameList[i], LDAP_PORT);
  1331. gLdap = ldap_init(NameList[i], LDAP_PORT);
  1332. if (gLdap != NULL) {
  1333. ldap_set_option(gLdap, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions);
  1334. LStatus = ldap_connect(gLdap, &Timeout);
  1335. if (LStatus != LDAP_SUCCESS) {
  1336. DPRINT1_LS(1, ":DS: WARN - ldap_connect(%ws);", NameList[i], LStatus);
  1337. ldap_unbind_s(gLdap);
  1338. gLdap = NULL;
  1339. } else {
  1340. //
  1341. // Successfully connected.
  1342. //
  1343. DPRINT1(5, ":DS: ldap_connect(%ws) succeeded\n", NameList[i]);
  1344. break;
  1345. }
  1346. }
  1347. }
  1348. }
  1349. //
  1350. // Whatever it is, we can't find it.
  1351. //
  1352. if (!gLdap) {
  1353. // DPRINT3_WS(0, ":DS: ERROR - ldap_open(DNS %ws, BIOS %ws, IP %ws);",
  1354. // DcDnsName, DcName, DcAddr, WStatus);
  1355. DPRINT3_LS(0, ":DS: ERROR - ldap_init(DNS %ws, BIOS %ws, IP %ws);",
  1356. DcDnsName, DcName, DcAddr, LStatus);
  1357. goto CLEANUP;
  1358. }
  1359. //
  1360. // Bind to the ldap server
  1361. //
  1362. LStatus = ldap_bind_s(gLdap, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  1363. CLEANUP_LS(0, ":DS: ERROR - Binding to DS.", LStatus, CLEANUP);
  1364. //
  1365. // Bind to the Ds (for various Ds calls such as DsCrackName())
  1366. //
  1367. NameListx = 0;
  1368. NameList[NameListx++] = DcDnsName;
  1369. NameList[NameListx++] = DcName;
  1370. NameList[NameListx++] = DcAddr;
  1371. FRS_ASSERT(NameListx <= MAX_DC_NAMELIST);
  1372. WStatus = ERROR_RETRY;
  1373. for (i=0; i<NameListx; i++) {
  1374. if (NameList[i] != NULL) {
  1375. WStatus = DsBind(NameList[i], NULL, &DsHandle);
  1376. if (!WIN_SUCCESS(WStatus)) {
  1377. DsHandle = NULL;
  1378. DPRINT1_WS(1, ":DS: WARN - DsBind(%ws);", NameList[i], WStatus);
  1379. } else {
  1380. DPRINT1(5, ":DS: DsBind(%ws) succeeded\n", NameList[i]);
  1381. break;
  1382. }
  1383. }
  1384. }
  1385. //
  1386. // Whatever it is, we can't find it
  1387. //
  1388. CLEANUP3_WS(0, ":DS: ERROR - DsBind(DNS %ws, BIOS %ws, IP %ws);",
  1389. DcDnsName, DcName, DcAddr, WStatus, CLEANUP);
  1390. //
  1391. // SUCCESS
  1392. //
  1393. Bound = TRUE;
  1394. CLEANUP:
  1395. //
  1396. // Cleanup
  1397. //
  1398. if (!Bound) {
  1399. //
  1400. // Close the connection to release resources if the above failed.
  1401. //
  1402. if (gLdap) {
  1403. ldap_unbind_s(gLdap);
  1404. gLdap = NULL;
  1405. }
  1406. }
  1407. if (DcInfo) {
  1408. NetApiBufferFree(DcInfo);
  1409. DcInfo = NULL;
  1410. }
  1411. return Bound;
  1412. }
  1413. BOOL
  1414. FrsDsOpenDs(
  1415. VOID
  1416. )
  1417. /*++
  1418. Routine Description:
  1419. Open and bind to a primary domain controller. The DN of the
  1420. sites container is a sideeffect.
  1421. Arguments:
  1422. DefaultDn
  1423. Return Value:
  1424. Bound ldap structure or NULL
  1425. Sets the following globals -
  1426. SitesDn
  1427. ServicesDn
  1428. SystemDn
  1429. ComputersDn
  1430. DomainControllersDn
  1431. DefaultNcDn
  1432. --*/
  1433. {
  1434. #undef DEBSUB
  1435. #define DEBSUB "FrsDsOpenDs:"
  1436. DWORD WStatus;
  1437. DWORD LStatus;
  1438. DWORD NumVals;
  1439. PWCHAR Config;
  1440. PLDAPMessage LdapEntry;
  1441. PLDAPMessage LdapMsg = NULL;
  1442. PWCHAR *Values = NULL;
  1443. PWCHAR Attrs[3];
  1444. //
  1445. // Time to clean up and exit
  1446. //
  1447. if (FrsIsShuttingDown || DsIsShuttingDown) {
  1448. goto ERROR_BINDING;
  1449. }
  1450. //
  1451. // Use existing bindings if possible
  1452. //
  1453. if (DsBindingsAreValid) {
  1454. return TRUE;
  1455. }
  1456. //
  1457. // The previous poll might have set DsBindingsAreValid to FALSE because one
  1458. // of the handle became invalid. In that case the other handles still need to be
  1459. // closed to prevent leak.
  1460. //
  1461. FrsDsCloseDs();
  1462. //
  1463. // Increment the DS Bindings counter
  1464. //
  1465. PM_INC_CTR_SERVICE(PMTotalInst, DSBindings, 1);
  1466. //
  1467. // Bind to a DS.
  1468. //
  1469. // Note the behavior of DsGetDcName for the following four flag combinations.
  1470. //
  1471. // DS_BACKGROUND_ONLY (as of 10/10/99)
  1472. // DS_FORCE_REDISCOVERY
  1473. // Zero Zero Netlogon will attempt to satisfy the request via
  1474. // cached info, negative cache entries will be
  1475. // returned if they are less than 5 minutes old.
  1476. // If it can't it will do a full discovery (dns
  1477. // queries, udp pings, poss netbt queries,
  1478. // mailslot datagrams, etc)
  1479. //
  1480. // Zero One Netlogon will do a full discovery.
  1481. //
  1482. // One Zero Netlogon will satisfy from the cache, unless the
  1483. // backoff routine allows for a retry at this time
  1484. // *and* the cache is insufficient.
  1485. //
  1486. // One One The DS_BACKGROUND_ONY flag is ignored, treated
  1487. // as a FORCE call.
  1488. //
  1489. if (!FrsDsBindDs(DS_DIRECTORY_SERVICE_REQUIRED |
  1490. DS_WRITABLE_REQUIRED |
  1491. DS_BACKGROUND_ONLY)) {
  1492. //
  1493. // Flush the cache and try again
  1494. //
  1495. DPRINT(1, ":DS: WARN - FrsDsBindDs(no force) failed\n");
  1496. //
  1497. // Because of the use of Dial-up lines at sites without a DC we don't
  1498. // want to use DS_FORCE_REDISCOVERY since that will defeat the generic
  1499. // DC discovery backoff algorithm thus causing FRS to constantly bring
  1500. // up the line. Bug 412620.
  1501. //FrsDsCloseDs(); // close ldap handle before doing reopen.
  1502. //if (!FrsDsBindDs(DS_DIRECTORY_SERVICE_REQUIRED |
  1503. // DS_WRITABLE_REQUIRED |
  1504. // DS_FORCE_REDISCOVERY)) {
  1505. // DPRINT(1, ":DS: WARN - FrsDsBindDs(force) failed\n");
  1506. goto ERROR_BINDING;
  1507. //}
  1508. }
  1509. DPRINT(4, ":DS: FrsDsBindDs() succeeded\n");
  1510. //
  1511. // Find the naming contexts and the default naming context (objectCategory=*)
  1512. //
  1513. MK_ATTRS_2(Attrs, ATTR_NAMING_CONTEXTS, ATTR_DEFAULT_NAMING_CONTEXT);
  1514. if (!FrsDsLdapSearch(gLdap, CN_ROOT, LDAP_SCOPE_BASE, CATEGORY_ANY,
  1515. Attrs, 0, &LdapMsg)) {
  1516. goto ERROR_BINDING;
  1517. }
  1518. LdapEntry = ldap_first_entry(gLdap, LdapMsg);
  1519. if (LdapEntry == NULL) {
  1520. goto ERROR_BINDING;
  1521. }
  1522. Values = (PWCHAR *)FrsDsFindValues(gLdap, LdapEntry, ATTR_NAMING_CONTEXTS, FALSE);
  1523. if (Values == NULL) {
  1524. goto ERROR_BINDING;
  1525. }
  1526. //
  1527. // Now, find the naming context that begins with "CN=Configuration"
  1528. //
  1529. NumVals = ldap_count_values(Values);
  1530. while (NumVals--) {
  1531. FRS_WCSLWR(Values[NumVals]);
  1532. Config = wcsstr(Values[NumVals], CONFIG_NAMING_CONTEXT);
  1533. if (Config && Config == Values[NumVals]) {
  1534. //
  1535. // Build the pathname for "configuration\sites & services"
  1536. //
  1537. SitesDn = FrsDsExtendDn(Config, CN_SITES);
  1538. ServicesDn = FrsDsExtendDn(Config, CN_SERVICES);
  1539. break;
  1540. }
  1541. }
  1542. LDAP_FREE_VALUES(Values);
  1543. //
  1544. // Finally, find the default naming context
  1545. //
  1546. Values = (PWCHAR *)FrsDsFindValues(gLdap, LdapEntry, ATTR_DEFAULT_NAMING_CONTEXT, FALSE);
  1547. if (Values == NULL) {
  1548. goto ERROR_BINDING;
  1549. }
  1550. DefaultNcDn = FrsWcsDup(Values[0]);
  1551. ComputersDn = FrsDsExtendDn(DefaultNcDn, CN_COMPUTERS);
  1552. SystemDn = FrsDsExtendDn(DefaultNcDn, CN_SYSTEM);
  1553. DomainControllersDn = FrsDsExtendDnOu(DefaultNcDn, CN_DOMAIN_CONTROLLERS);
  1554. LDAP_FREE_VALUES(Values);
  1555. LDAP_FREE_MSG(LdapMsg);
  1556. //
  1557. // Polling the ds requires all these distinguished names
  1558. //
  1559. if ((SitesDn == NULL) || (ServicesDn == NULL) || (SystemDn == NULL) ||
  1560. (DefaultNcDn == NULL) || (ComputersDn == NULL) || (DomainControllersDn == NULL)) {
  1561. goto ERROR_BINDING;
  1562. }
  1563. //
  1564. // SUCCESS
  1565. //
  1566. DsBindingsAreValid = TRUE;
  1567. return TRUE;
  1568. ERROR_BINDING:
  1569. //
  1570. // avoid extraneous error messages during shutdown
  1571. //
  1572. if (!FrsIsShuttingDown && !DsIsShuttingDown) {
  1573. DPRINT(0, ":DS: ERROR - Could not open the DS\n");
  1574. }
  1575. //
  1576. // Cleanup
  1577. //
  1578. LDAP_FREE_VALUES(Values);
  1579. LDAP_FREE_MSG(LdapMsg);
  1580. //
  1581. // No ds bindings
  1582. //
  1583. FrsDsCloseDs();
  1584. //
  1585. // Increment the DS Bindings in Error counter
  1586. //
  1587. PM_INC_CTR_SERVICE(PMTotalInst, DSBindingsError, 1);
  1588. return FALSE;
  1589. }
  1590. #if DBG
  1591. #define FRS_PRINT_TREE(_Hdr_, _Sites_) FrsDsFrsPrintTree(_Hdr_, _Sites_)
  1592. VOID
  1593. FrsDsFrsPrintTree(
  1594. IN PWCHAR Hdr,
  1595. IN PCONFIG_NODE Sites
  1596. )
  1597. /*++
  1598. Routine Description:
  1599. print the tree.
  1600. Arguments:
  1601. Hdr - prettyprint
  1602. Sites
  1603. Return Value:
  1604. None.
  1605. --*/
  1606. {
  1607. #undef DEBSUB
  1608. #define DEBSUB "FrsDsFrsPrintTree:"
  1609. PCONFIG_NODE Site;
  1610. PCONFIG_NODE Settings;
  1611. PCONFIG_NODE Set;
  1612. PCONFIG_NODE Server;
  1613. PCONFIG_NODE Cxtion;
  1614. CHAR Guid[GUID_CHAR_LEN + 1];
  1615. if (Sites == NULL) {
  1616. return;
  1617. }
  1618. if (Hdr) {
  1619. DPRINT1(5, ":DS: %ws\n", Hdr);
  1620. }
  1621. //
  1622. // Print the tree
  1623. //
  1624. for (Site = Sites; Site; Site = Site->Peer) {
  1625. GuidToStr(Site->Name->Guid, Guid);
  1626. DPRINT2(5, ":DS: %ws (%ws)\n", Site->Name->Name,
  1627. (Site->Consistent) ? L"Consistent" : L"InConsistent");
  1628. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  1629. if (Settings->Name) {
  1630. GuidToStr(Settings->Name->Guid, Guid);
  1631. DPRINT2(5, ":DS: %ws (%ws)\n", Settings->Name->Name,
  1632. (Settings->Consistent) ? L"Consistent" : L"InConsistent");
  1633. } else {
  1634. DPRINT(5, ":DS: nTDSSettings\n");
  1635. }
  1636. for (Set = Settings->Children; Set; Set = Set->Peer) {
  1637. GuidToStr(Set->Name->Guid, Guid);
  1638. DPRINT2(5, ":DS: %ws (%ws)\n", Set->Name->Name,
  1639. (Set->Consistent) ? L"Consistent" : L"InConsistent");
  1640. for (Server = Set->Children; Server; Server = Server->Peer) {
  1641. GuidToStr(Server->Name->Guid, Guid);
  1642. DPRINT3(5, ":DS: %ws %ws (%ws)\n",
  1643. Server->Name->Name, Server->Root,
  1644. (Server->Consistent) ? L"Consistent" : L"InConsistent");
  1645. for (Cxtion = Server->Children; Cxtion; Cxtion = Cxtion->Peer) {
  1646. GuidToStr(Cxtion->Name->Guid, Guid);
  1647. DPRINT4(5, ":DS: %ws %ws %ws) (%ws)\n",
  1648. Cxtion->Name->Name,
  1649. (Cxtion->Inbound) ? L"IN (From" : L"OUT (To",
  1650. Cxtion->PartnerName->Name,
  1651. (Cxtion->Consistent) ? L"Consistent" : L"InConsistent");
  1652. }
  1653. }
  1654. }
  1655. }
  1656. }
  1657. if (Hdr) {
  1658. DPRINT1(5, ":DS: %ws DONE\n", Hdr);
  1659. } else {
  1660. DPRINT(5, ":DS: DONE\n");
  1661. }
  1662. }
  1663. #else DBG
  1664. #define FRS_PRINT_TREE(_Hdr_, _Sites_)
  1665. #endif DBG
  1666. VOID
  1667. FrsDsTreeLink(
  1668. IN PCONFIG_NODE Parent,
  1669. IN PCONFIG_NODE Node
  1670. )
  1671. /*++
  1672. Routine Description:
  1673. Link the node into the tree and keep a running "change checksum"
  1674. to compare with the previous tree. We don't use a DS that is in
  1675. flux. We wait until two polling cycles return the same "change
  1676. checksum" before using the DS data.
  1677. Arguments:
  1678. Entry - Current entry from the DS
  1679. Parent - Container which contains Base
  1680. Return Value:
  1681. None.
  1682. --*/
  1683. {
  1684. #undef DEBSUB
  1685. #define DEBSUB "FrsDsTreeLink:"
  1686. ULONG i;
  1687. ULONG LenChanged; // length of Changed
  1688. DPRINT3(5, ":DS: Linking node type %ws, node name %ws to parent %ws\n",
  1689. DsConfigTypeName[Node->DsObjectType],
  1690. (Node->Name) ? Node->Name->Name : L"null",
  1691. (Parent->Name) ? Parent->Name->Name : L"null");
  1692. //
  1693. // Link into config
  1694. //
  1695. ++Parent->NumChildren;
  1696. Node->Parent = Parent;
  1697. Node->Peer = Parent->Children;
  1698. Parent->Children = Node;
  1699. //
  1700. // Some indication that the DS is stable
  1701. //
  1702. if (Node->UsnChanged) {
  1703. LenChanged = wcslen(Node->UsnChanged);
  1704. for (i = 0; i < LenChanged; ++i) {
  1705. ThisChange += *(Node->UsnChanged + i); // sum
  1706. NextChange += ThisChange; // sum of sums (order dependent)
  1707. }
  1708. }
  1709. }
  1710. PCONFIG_NODE
  1711. FrsDsAllocBasicNode(
  1712. IN PLDAP Ldap,
  1713. IN PLDAPMessage LdapEntry,
  1714. IN ULONG NodeType
  1715. )
  1716. /*++
  1717. Routine Description:
  1718. Allocate a Node and fill in the fields common to all or most nodes.
  1719. (guid, name, dn, schedule, and usnchanged)
  1720. Arguments:
  1721. Ldap - opened and bound ldap connection
  1722. LdapEntry - from ldap_first/next_entry
  1723. NodeType - Internal type code for the object represented by this node.
  1724. Return Value:
  1725. NULL if basic node cannot be allocated
  1726. --*/
  1727. {
  1728. #undef DEBSUB
  1729. #define DEBSUB "FrsDsAllocBasicNode:"
  1730. PCONFIG_NODE Node;
  1731. //
  1732. // Increment the DS Objects counter
  1733. //
  1734. PM_INC_CTR_SERVICE(PMTotalInst, DSObjects, 1);
  1735. //
  1736. // Initially, the node is assumed to be consistent
  1737. //
  1738. Node = FrsAllocType(CONFIG_NODE_TYPE);
  1739. Node->Consistent = TRUE;
  1740. Node->DsObjectType = NodeType;
  1741. //
  1742. // A dummy entry can be created by passing NULL for LdapEntry.
  1743. //
  1744. if (LdapEntry == NULL) {
  1745. return Node;
  1746. }
  1747. //
  1748. // Distinguished name
  1749. //
  1750. Node->Dn = FrsDsFindValue(Ldap, LdapEntry, ATTR_DN);
  1751. FRS_WCSLWR(Node->Dn);
  1752. //
  1753. // Name = RDN + Object Guid
  1754. //
  1755. Node->Name = FrsBuildGName(FrsDsFindGuid(Ldap, LdapEntry),
  1756. FrsDsMakeRdn(Node->Dn));
  1757. //
  1758. // Schedule, if any
  1759. //
  1760. Node->Schedule = FrsDsFindSchedule(Ldap, LdapEntry, &Node->ScheduleLength);
  1761. //
  1762. // USN Changed
  1763. //
  1764. Node->UsnChanged = FrsDsFindValue(Ldap, LdapEntry, ATTR_USN_CHANGED);
  1765. if (!Node->Dn || !Node->Name->Name || !Node->Name->Guid) {
  1766. //
  1767. // Increment the DS Objects in Error counter
  1768. //
  1769. PM_INC_CTR_SERVICE(PMTotalInst, DSObjectsError, 1);
  1770. DPRINT3(0, ":DS: ERROR - Ignoring node; lacks dn (%08x), rdn (%08x), or guid (%08x)\n",
  1771. Node->Dn, Node->Name->Name, Node->Name->Guid);
  1772. Node = FrsFreeType(Node);
  1773. }
  1774. return Node;
  1775. }
  1776. #define NUM_EQUALS (4)
  1777. ULONG
  1778. FrsDsSameSite(
  1779. IN PWCHAR NtDsSettings1,
  1780. IN PWCHAR NtDsSettings2
  1781. )
  1782. /*++
  1783. Routine Description:
  1784. Are the ntds settings in the same site?
  1785. Arguments:
  1786. NtDsSettings1 - NtDs Settings FQDN
  1787. NtDsSettings2 - NtDs Settings FQDN
  1788. Return Value:
  1789. TRUE - Same site
  1790. FALSE - Not
  1791. --*/
  1792. {
  1793. #undef DEBSUB
  1794. #define DEBSUB "FrsDsSameSite:"
  1795. PWCHAR Equal1 = NULL;
  1796. PWCHAR Equal2 = NULL;
  1797. DWORD EqualsFound;
  1798. if (!NtDsSettings1 || !NtDsSettings2) {
  1799. return TRUE;
  1800. }
  1801. //
  1802. // Forth equals sign
  1803. //
  1804. for (EqualsFound = 0; *NtDsSettings1 != L'\0'; ++NtDsSettings1) {
  1805. if (*NtDsSettings1 != L'=') {
  1806. continue;
  1807. }
  1808. if (++EqualsFound == NUM_EQUALS) {
  1809. Equal1 = NtDsSettings1;
  1810. break;
  1811. }
  1812. }
  1813. //
  1814. // Forth equals sign
  1815. //
  1816. for (EqualsFound = 0; *NtDsSettings2 != L'\0'; ++NtDsSettings2) {
  1817. if (*NtDsSettings2 != L'=') {
  1818. continue;
  1819. }
  1820. if (++EqualsFound == NUM_EQUALS) {
  1821. Equal2 = NtDsSettings2;
  1822. break;
  1823. }
  1824. }
  1825. //
  1826. // Not the same length
  1827. //
  1828. if (!Equal1 || !Equal2) {
  1829. return TRUE;
  1830. }
  1831. //
  1832. // Compare up to the first comma
  1833. //
  1834. while (*Equal1 == *Equal2 && (*Equal1 && *Equal1 != L',')) {
  1835. ++Equal1;
  1836. ++Equal2;
  1837. }
  1838. DPRINT3(4, ":DS: %s: %ws %ws\n",
  1839. (*Equal1 == *Equal2) ? "SAME SITE" : "DIFF SITE", Equal1, Equal2);
  1840. return (*Equal1 == *Equal2);
  1841. }
  1842. DWORD
  1843. FrsDsResolveCxtionConflict(
  1844. IN PCONFIG_NODE OldCxtion,
  1845. IN PCONFIG_NODE NewCxtion,
  1846. IN PCONFIG_NODE *Winner,
  1847. IN PCONFIG_NODE *Loser
  1848. )
  1849. /*++
  1850. Routine Description:
  1851. Resolve the connection conflict.
  1852. Arguments:
  1853. OldCxtion
  1854. NewCxtion
  1855. Winner
  1856. Loser
  1857. Return Value:
  1858. WIN32 Status
  1859. --*/
  1860. {
  1861. //
  1862. // Compare the guids and pick a connection. This ensures that both the members
  1863. // at the ends of each connection pick the same one.
  1864. //
  1865. if ((OldCxtion != NULL) && (NewCxtion != NULL) &&
  1866. (OldCxtion->Name != NULL) && (NewCxtion->Name != NULL) &&
  1867. (memcmp(OldCxtion->Name->Guid, NewCxtion->Name->Guid, sizeof(GUID)) > 0) ) {
  1868. *Winner = NewCxtion;
  1869. *Loser = OldCxtion;
  1870. } else {
  1871. *Winner = OldCxtion;
  1872. *Loser = NewCxtion;
  1873. }
  1874. //
  1875. // Add to the poll summary event log.
  1876. //
  1877. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_CXTION_CONFLICT, (*Winner)->Dn,
  1878. (*Loser)->Dn, (*Winner)->Dn);
  1879. return ERROR_SUCCESS;
  1880. }
  1881. DWORD
  1882. FrsDsResolveSubscriberConflict(
  1883. IN PCONFIG_NODE OldSubscriber,
  1884. IN PCONFIG_NODE NewSubscriber,
  1885. IN PCONFIG_NODE *Winner,
  1886. IN PCONFIG_NODE *Loser
  1887. )
  1888. /*++
  1889. Routine Description:
  1890. Resolve the subscriber conflict.
  1891. Arguments:
  1892. OldSubscriber
  1893. NewSubscriber
  1894. Winner
  1895. Loser
  1896. Return Value:
  1897. WIN32 Status
  1898. --*/
  1899. {
  1900. *Winner = OldSubscriber;
  1901. *Loser = NewSubscriber;
  1902. //
  1903. // Add to the poll summary event log.
  1904. //
  1905. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_SUBSCRIBER_CONFLICT, (*Winner)->Dn,
  1906. (*Loser)->Dn, (*Winner)->Dn);
  1907. return ERROR_SUCCESS;
  1908. }
  1909. ULONG
  1910. FrsDsGetNonSysvolInboundCxtions(
  1911. IN PLDAP Ldap,
  1912. IN PWCHAR SetDn,
  1913. IN PWCHAR MemberRef
  1914. )
  1915. /*++
  1916. Routine Description:
  1917. Fetch the non-sysvol inbound connections and add them
  1918. to the CxtionTable. Check for multiple connections between the
  1919. same partners and resolve the conflict.
  1920. Part of NewDs poll APIs.
  1921. Arguments:
  1922. ldap - opened and bound ldap connection.
  1923. SetDn - Dn of the set being processed.
  1924. MemberRef - Member reference from the subscriber object.
  1925. Return Value:
  1926. ERROR_SUCCESS - config fetched successfully
  1927. Otherwise - couldn't get the DS config
  1928. --*/
  1929. {
  1930. #undef DEBSUB
  1931. #define DEBSUB "FrsDsGetNonSysvolInboundCxtions:"
  1932. PWCHAR Attrs[8];
  1933. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  1934. PCONFIG_NODE Node; // generic node for the tree
  1935. PWCHAR TempFilter = NULL;
  1936. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  1937. PWCHAR PartnerCn = NULL;
  1938. PGEN_ENTRY ConflictingNodeEntry = NULL;
  1939. PCONFIG_NODE ConflictingNode = NULL;
  1940. PCONFIG_NODE Winner = NULL;
  1941. PCONFIG_NODE Loser = NULL;
  1942. BOOL Inbound;
  1943. PWCHAR Options = NULL;
  1944. //
  1945. // Look for all the connections under our member object.
  1946. //
  1947. MK_ATTRS_7(Attrs, ATTR_DN, ATTR_SCHEDULE, ATTR_FROM_SERVER, ATTR_OBJECT_GUID,
  1948. ATTR_USN_CHANGED, ATTR_ENABLED_CXTION, ATTR_OPTIONS);
  1949. if (!FrsDsLdapSearchInit(Ldap, MemberRef, LDAP_SCOPE_ONELEVEL, CATEGORY_CXTION,
  1950. Attrs, 0, &FrsSearchContext)) {
  1951. return ERROR_ACCESS_DENIED;
  1952. }
  1953. if (FrsSearchContext.EntriesInPage == 0) {
  1954. DPRINT1(1, ":DS: WARN - There are no connection objects in %ws!\n", MemberRef);
  1955. }
  1956. //
  1957. // Scan the entries returned from ldap_search
  1958. //
  1959. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  1960. Entry != NULL;
  1961. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  1962. //
  1963. // Basic node info (guid, name, dn, schedule, and usnchanged)
  1964. //
  1965. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_IN_CXTION);
  1966. if (!Node) {
  1967. DPRINT(4, ":DS: Cxtion lacks basic info; skipping\n");
  1968. continue;
  1969. }
  1970. Node->EnabledCxtion = FrsDsFindValue(Ldap, Entry, ATTR_ENABLED_CXTION);
  1971. if (Node->EnabledCxtion && WSTR_EQ(Node->EnabledCxtion, ATTR_FALSE)) {
  1972. DPRINT2(1, ":DS: WARN - enabledConnection set to %ws; Ignoring %ws\n",
  1973. Node->EnabledCxtion, Node->Name->Name);
  1974. Node = FrsFreeType(Node);
  1975. continue;
  1976. }
  1977. //
  1978. // Read the options value on the connection object.
  1979. // We are intersted in the NTDSCONN_OPT_TWOWAY_SYNC flag and the
  1980. // priority on connections.
  1981. //
  1982. Options = FrsDsFindValue(Ldap, Entry, ATTR_OPTIONS);
  1983. if (Options != NULL) {
  1984. Node->CxtionOptions = _wtoi(Options);
  1985. Options = FrsFree(Options);
  1986. } else {
  1987. Node->CxtionOptions = 0;
  1988. }
  1989. //
  1990. // These are inbound connections.
  1991. //
  1992. Node->Inbound = TRUE;
  1993. //
  1994. // Node's partner's name.
  1995. //
  1996. Node->PartnerDn = FrsDsFindValue(Ldap, Entry, ATTR_FROM_SERVER);
  1997. FRS_WCSLWR(Node->PartnerDn);
  1998. //
  1999. // Add the Inbound cxtion to the cxtion table.
  2000. //
  2001. ConflictingNodeEntry = GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2002. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2003. if (ConflictingNodeEntry) {
  2004. ConflictingNode = ConflictingNodeEntry->Data;
  2005. FrsDsResolveCxtionConflict(ConflictingNode, Node, &Winner, &Loser);
  2006. if (WSTR_EQ(Winner->Dn, Node->Dn)) {
  2007. //
  2008. // The new one is the winner. Remove old one and insert new one.
  2009. //
  2010. GTabDelete(CxtionTable,ConflictingNodeEntry->Key1,ConflictingNodeEntry->Key2, NULL);
  2011. GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2012. GTabDelete(AllCxtionsTable,ConflictingNode->PartnerDn, (PVOID)&ConflictingNode->Inbound, NULL);
  2013. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2014. FrsFreeType(ConflictingNode);
  2015. } else {
  2016. //
  2017. // The old one is the winner. Leave it in the table.
  2018. //
  2019. FrsFreeType(Node);
  2020. continue;
  2021. }
  2022. } else {
  2023. //
  2024. // If there is no conflict then we need to add this Member to the MemberSearchFilter
  2025. // if it is not already there. It could have been added while processing the oubound connections.
  2026. //
  2027. Inbound = FALSE;
  2028. if (GTabLookupTableString(CxtionTable, Node->PartnerDn, (PWCHAR)&Inbound) == NULL) {
  2029. PartnerCn = FrsDsMakeRdn(Node->PartnerDn);
  2030. if (MemberSearchFilter != NULL) {
  2031. TempFilter = FrsAlloc((wcslen(MemberSearchFilter) + wcslen(L"(=)" ATTR_CN) +
  2032. wcslen(PartnerCn) + 1 ) * sizeof(WCHAR));
  2033. wcscpy(TempFilter, MemberSearchFilter);
  2034. wcscat(TempFilter, L"(" ATTR_CN L"=" );
  2035. wcscat(TempFilter, PartnerCn);
  2036. wcscat(TempFilter, L")");
  2037. FrsFree(MemberSearchFilter);
  2038. MemberSearchFilter = TempFilter;
  2039. TempFilter = NULL;
  2040. } else {
  2041. MemberSearchFilter = FrsAlloc((wcslen(L"(|(=)" ATTR_CN) +
  2042. wcslen(PartnerCn) + 1 ) * sizeof(WCHAR));
  2043. wcscpy(MemberSearchFilter, L"(|(" ATTR_CN L"=" );
  2044. wcscat(MemberSearchFilter, PartnerCn);
  2045. wcscat(MemberSearchFilter, L")");
  2046. }
  2047. FrsFree(PartnerCn);
  2048. }
  2049. }
  2050. }
  2051. FrsDsLdapSearchClose(&FrsSearchContext);
  2052. return ERROR_SUCCESS;
  2053. }
  2054. ULONG
  2055. FrsDsGetNonSysvolOutboundCxtions(
  2056. IN PLDAP Ldap,
  2057. IN PWCHAR SetDn,
  2058. IN PWCHAR MemberRef
  2059. )
  2060. /*++
  2061. Routine Description:
  2062. Fetch the non-sysvol outbound connections and add them
  2063. to the CxtionTable. Check for multiple connections between the
  2064. same partners and resolve the conflict.
  2065. Part of NewDs poll APIs.
  2066. Arguments:
  2067. ldap - opened and bound ldap connection.
  2068. SetDn - Dn of the set being processed.
  2069. MemberRef - Member reference from the subscriber object.
  2070. Return Value:
  2071. ERROR_SUCCESS - config fetched successfully
  2072. Otherwise - couldn't get the DS config
  2073. --*/
  2074. {
  2075. #undef DEBSUB
  2076. #define DEBSUB "FrsDsGetNonSysvolOutboundCxtions:"
  2077. PWCHAR Attrs[8];
  2078. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  2079. PCONFIG_NODE Node; // generic node for the tree
  2080. PWCHAR SearchFilter = NULL;
  2081. PWCHAR TempFilter = NULL;
  2082. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  2083. PWCHAR PartnerCn = NULL;
  2084. PGEN_ENTRY ConflictingNodeEntry = NULL;
  2085. PCONFIG_NODE ConflictingNode = NULL;
  2086. PCONFIG_NODE Winner = NULL;
  2087. PCONFIG_NODE Loser = NULL;
  2088. PWCHAR Options = NULL;
  2089. //
  2090. // Look for all the connections that have our member as the from server.
  2091. // Filter will look like (&(objectCategory=nTDSConnection)(fromServer=cn=member1,cn=set1,...))
  2092. //
  2093. MK_ATTRS_7(Attrs, ATTR_DN, ATTR_SCHEDULE, ATTR_FROM_SERVER, ATTR_OBJECT_GUID,
  2094. ATTR_USN_CHANGED, ATTR_ENABLED_CXTION, ATTR_OPTIONS);
  2095. SearchFilter = FrsAlloc((wcslen(L"(&(=))" CATEGORY_CXTION ATTR_FROM_SERVER) +
  2096. wcslen(MemberRef) + 1) * sizeof(WCHAR));
  2097. wcscpy(SearchFilter,L"(&" CATEGORY_CXTION L"(" ATTR_FROM_SERVER L"=" );
  2098. wcscat(SearchFilter,MemberRef);
  2099. wcscat(SearchFilter,L"))");
  2100. if (!FrsDsLdapSearchInit(Ldap, SetDn, LDAP_SCOPE_SUBTREE, SearchFilter,
  2101. Attrs, 0, &FrsSearchContext)) {
  2102. SearchFilter = FrsFree(SearchFilter);
  2103. return ERROR_ACCESS_DENIED;
  2104. }
  2105. if (FrsSearchContext.EntriesInPage == 0) {
  2106. DPRINT1(1, ":DS: WARN - No outbound connections found for member %ws!\n", MemberRef);
  2107. }
  2108. SearchFilter = FrsFree(SearchFilter);
  2109. //
  2110. // Scan the entries returned from ldap_search
  2111. //
  2112. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  2113. Entry != NULL;
  2114. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  2115. //
  2116. // Basic node info (guid, name, dn, schedule, and usnchanged)
  2117. //
  2118. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_IN_CXTION);
  2119. if (!Node) {
  2120. DPRINT(4, ":DS: Cxtion lacks basic info; skipping\n");
  2121. continue;
  2122. }
  2123. Node->EnabledCxtion = FrsDsFindValue(Ldap, Entry, ATTR_ENABLED_CXTION);
  2124. if (Node->EnabledCxtion && WSTR_EQ(Node->EnabledCxtion, ATTR_FALSE)) {
  2125. DPRINT2(1, ":DS: WARN - enabledConnection set to %ws; Ignoring %ws\n",
  2126. Node->EnabledCxtion, Node->Name->Name);
  2127. Node = FrsFreeType(Node);
  2128. continue;
  2129. }
  2130. //
  2131. // Read the options value on the connection object.
  2132. // We are only intersted in the NTDSCONN_OPT_TWOWAY_SYNC flag.
  2133. //
  2134. Options = FrsDsFindValue(Ldap, Entry, ATTR_OPTIONS);
  2135. if (Options != NULL) {
  2136. Node->CxtionOptions = _wtoi(Options);
  2137. Options = FrsFree(Options);
  2138. } else {
  2139. Node->CxtionOptions = 0;
  2140. }
  2141. //
  2142. // These are outbound connections.
  2143. //
  2144. Node->Inbound = FALSE;
  2145. //
  2146. // Node's partner's name. This is an outbound connection. Get the
  2147. // partners Dn by going one level up from the connection to the
  2148. // member Dn.
  2149. //
  2150. Node->PartnerDn = FrsWcsDup(wcsstr(Node->Dn + 3, L"cn="));
  2151. FRS_WCSLWR(Node->PartnerDn);
  2152. //
  2153. // Add the outbound cxtion to the cxtion table.
  2154. //
  2155. ConflictingNodeEntry = GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2156. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2157. if (ConflictingNodeEntry) {
  2158. ConflictingNode = ConflictingNodeEntry->Data;
  2159. FrsDsResolveCxtionConflict(ConflictingNode, Node, &Winner, &Loser);
  2160. if (WSTR_EQ(Winner->Dn, Node->Dn)) {
  2161. //
  2162. // The new one is the winner. Remove old one and insert new one.
  2163. //
  2164. GTabDelete(CxtionTable,ConflictingNodeEntry->Key1,ConflictingNodeEntry->Key2, NULL);
  2165. GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2166. GTabDelete(AllCxtionsTable,ConflictingNode->PartnerDn, (PVOID)&ConflictingNode->Inbound, NULL);
  2167. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2168. FrsFreeType(ConflictingNode);
  2169. } else {
  2170. //
  2171. // The old one is the winner. Leave it in the table.
  2172. //
  2173. FrsFreeType(Node);
  2174. continue;
  2175. }
  2176. } else {
  2177. //
  2178. // If there is no conflict then we need to add this Member to the MemberSearchFilter.
  2179. //
  2180. PartnerCn = FrsDsMakeRdn(Node->PartnerDn);
  2181. if (MemberSearchFilter != NULL) {
  2182. TempFilter = FrsAlloc((wcslen(MemberSearchFilter) + wcslen(L"(=)" ATTR_CN) +
  2183. wcslen(PartnerCn) + 1 ) * sizeof(WCHAR));
  2184. wcscpy(TempFilter, MemberSearchFilter);
  2185. wcscat(TempFilter, L"(" ATTR_CN L"=");
  2186. wcscat(TempFilter, PartnerCn);
  2187. wcscat(TempFilter, L")");
  2188. FrsFree(MemberSearchFilter);
  2189. MemberSearchFilter = TempFilter;
  2190. TempFilter = NULL;
  2191. } else {
  2192. MemberSearchFilter = FrsAlloc((wcslen(L"(|(=)" ATTR_CN) +
  2193. wcslen(PartnerCn) + 1 ) * sizeof(WCHAR));
  2194. wcscpy(MemberSearchFilter, L"(|(" ATTR_CN L"=");
  2195. wcscat(MemberSearchFilter, PartnerCn);
  2196. wcscat(MemberSearchFilter, L")");
  2197. }
  2198. FrsFree(PartnerCn);
  2199. }
  2200. }
  2201. FrsDsLdapSearchClose(&FrsSearchContext);
  2202. return ERROR_SUCCESS;
  2203. }
  2204. ULONG
  2205. FrsDsGetSysvolInboundCxtions(
  2206. IN PLDAP Ldap,
  2207. IN PWCHAR SettingsDn
  2208. )
  2209. /*++
  2210. Routine Description:
  2211. Fetch the sysvol inbound connections and add them
  2212. to the CxtionTable. Check for multiple connections between the
  2213. same partners and resolve the conflict.
  2214. Part of NewDs poll APIs.
  2215. Arguments:
  2216. ldap - opened and bound ldap connection.
  2217. SettingsDn - server reference from the member object.
  2218. Return Value:
  2219. ERROR_SUCCESS - config fetched successfully
  2220. Otherwise - couldn't get the DS config
  2221. --*/
  2222. {
  2223. #undef DEBSUB
  2224. #define DEBSUB "FrsDsGetSysvolInboundCxtions:"
  2225. PWCHAR Attrs[7];
  2226. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  2227. PCONFIG_NODE Node; // generic node for the tree
  2228. PWCHAR TempFilter = NULL;
  2229. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  2230. PGEN_ENTRY ConflictingNodeEntry = NULL;
  2231. PCONFIG_NODE ConflictingNode = NULL;
  2232. PCONFIG_NODE Winner = NULL;
  2233. PCONFIG_NODE Loser = NULL;
  2234. BOOL Inbound;
  2235. //
  2236. // Look for all the connections under our member object.
  2237. //
  2238. MK_ATTRS_6(Attrs, ATTR_DN, ATTR_SCHEDULE, ATTR_FROM_SERVER, ATTR_OBJECT_GUID,
  2239. ATTR_USN_CHANGED, ATTR_ENABLED_CXTION);
  2240. if (!FrsDsLdapSearchInit(Ldap, SettingsDn, LDAP_SCOPE_ONELEVEL, CATEGORY_CXTION,
  2241. Attrs, 0, &FrsSearchContext)) {
  2242. return ERROR_ACCESS_DENIED;
  2243. }
  2244. if (FrsSearchContext.EntriesInPage == 0) {
  2245. DPRINT1(1, ":DS: WARN - No sysvol inbound connections found for object %ws!\n", SettingsDn);
  2246. }
  2247. //
  2248. // Scan the entries returned from ldap_search
  2249. //
  2250. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  2251. Entry != NULL;
  2252. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  2253. //
  2254. // Basic node info (guid, name, dn, schedule, and usnchanged)
  2255. //
  2256. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_IN_CXTION);
  2257. if (!Node) {
  2258. DPRINT(4, ":DS: Cxtion lacks basic info; skipping\n");
  2259. continue;
  2260. }
  2261. Node->EnabledCxtion = FrsDsFindValue(Ldap, Entry, ATTR_ENABLED_CXTION);
  2262. if (Node->EnabledCxtion && WSTR_EQ(Node->EnabledCxtion, ATTR_FALSE)) {
  2263. DPRINT2(1, ":DS: WARN - enabledConnection set to %ws; Ignoring %ws\n",
  2264. Node->EnabledCxtion, Node->Name->Name);
  2265. Node = FrsFreeType(Node);
  2266. continue;
  2267. }
  2268. //
  2269. // These are inbound connections.
  2270. //
  2271. Node->Inbound = TRUE;
  2272. //
  2273. // Node's partner's name.
  2274. //
  2275. Node->PartnerDn = FrsDsFindValue(Ldap, Entry, ATTR_FROM_SERVER);
  2276. FRS_WCSLWR(Node->PartnerDn);
  2277. //
  2278. // Add the Inbound cxtion to the cxtion table.
  2279. //
  2280. ConflictingNodeEntry = GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2281. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2282. if (ConflictingNodeEntry) {
  2283. ConflictingNode = ConflictingNodeEntry->Data;
  2284. FrsDsResolveCxtionConflict(ConflictingNode, Node, &Winner, &Loser);
  2285. if (WSTR_EQ(Winner->Dn, Node->Dn)) {
  2286. //
  2287. // The new one is the winner. Remove old one and insert new one.
  2288. //
  2289. GTabDelete(CxtionTable,ConflictingNodeEntry->Key1,ConflictingNodeEntry->Key2, NULL);
  2290. GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2291. GTabDelete(AllCxtionsTable,ConflictingNode->PartnerDn, (PVOID)&ConflictingNode->Inbound, NULL);
  2292. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2293. FrsFreeType(ConflictingNode);
  2294. } else {
  2295. //
  2296. // The old one is the winner. Leave it in the table.
  2297. //
  2298. FrsFreeType(Node);
  2299. continue;
  2300. }
  2301. } else {
  2302. //
  2303. // If there is no conflict then we need to add this Member to the MemberSearchFilter
  2304. // if it is not already there. It could have been added while processing the oubound connections.
  2305. //
  2306. Inbound = FALSE;
  2307. if (GTabLookupTableString(CxtionTable, Node->PartnerDn, (PWCHAR)&Inbound) == NULL) {
  2308. if (MemberSearchFilter != NULL) {
  2309. TempFilter = FrsAlloc((wcslen(MemberSearchFilter) + wcslen(L"(=)" ATTR_SERVER_REF) +
  2310. wcslen(Node->PartnerDn) + 1 ) * sizeof(WCHAR));
  2311. wcscpy(TempFilter, MemberSearchFilter);
  2312. wcscat(TempFilter, L"(" ATTR_SERVER_REF L"=");
  2313. wcscat(TempFilter, Node->PartnerDn);
  2314. wcscat(TempFilter, L")");
  2315. FrsFree(MemberSearchFilter);
  2316. MemberSearchFilter = TempFilter;
  2317. TempFilter = NULL;
  2318. } else {
  2319. MemberSearchFilter = FrsAlloc((wcslen(L"(|(=)" ATTR_SERVER_REF) +
  2320. wcslen(Node->PartnerDn) + 1 ) * sizeof(WCHAR));
  2321. wcscpy(MemberSearchFilter, L"(|(" ATTR_SERVER_REF L"=");
  2322. wcscat(MemberSearchFilter, Node->PartnerDn);
  2323. wcscat(MemberSearchFilter, L")");
  2324. }
  2325. }
  2326. }
  2327. //
  2328. // If sysvol, always on within a site
  2329. // Trigger schedule otherwise.
  2330. //
  2331. Node->SameSite = FrsDsSameSite(SettingsDn, Node->PartnerDn);
  2332. if (Node->SameSite) {
  2333. Node->Schedule = FrsFree(Node->Schedule);
  2334. }
  2335. }
  2336. FrsDsLdapSearchClose(&FrsSearchContext);
  2337. return ERROR_SUCCESS;
  2338. }
  2339. ULONG
  2340. FrsDsGetSysvolOutboundCxtions(
  2341. IN PLDAP Ldap,
  2342. IN PWCHAR SettingsDn
  2343. )
  2344. /*++
  2345. Routine Description:
  2346. Fetch the sysvol outbound connections and add them
  2347. to the CxtionTable. Check for multiple connections between the
  2348. same partners and resolve the conflict.
  2349. Part of NewDs poll APIs.
  2350. Arguments:
  2351. ldap - opened and bound ldap connection.
  2352. SettingsDn - server reference from the member object.
  2353. Return Value:
  2354. ERROR_SUCCESS - config fetched successfully
  2355. Otherwise - couldn't get the DS config
  2356. --*/
  2357. {
  2358. #undef DEBSUB
  2359. #define DEBSUB "FrsDsGetSysvolOutboundCxtions:"
  2360. PWCHAR Attrs[7];
  2361. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  2362. PCONFIG_NODE Node; // generic node for the tree
  2363. PWCHAR SearchFilter = NULL;
  2364. PWCHAR TempFilter = NULL;
  2365. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  2366. PGEN_ENTRY ConflictingNodeEntry = NULL;
  2367. PCONFIG_NODE ConflictingNode = NULL;
  2368. PCONFIG_NODE Winner = NULL;
  2369. PCONFIG_NODE Loser = NULL;
  2370. //
  2371. // Look for all the connections that have our member as the from server.
  2372. // Filter will look like (&(objectCategory=nTDSConnection)(fromServer=cn=member1,cn=set1,...))
  2373. //
  2374. MK_ATTRS_6(Attrs, ATTR_DN, ATTR_SCHEDULE, ATTR_FROM_SERVER, ATTR_OBJECT_GUID,
  2375. ATTR_USN_CHANGED, ATTR_ENABLED_CXTION);
  2376. SearchFilter = FrsAlloc((wcslen(L"(&(=))" CATEGORY_CXTION ATTR_FROM_SERVER) +
  2377. wcslen(SettingsDn) + 1) * sizeof(WCHAR));
  2378. wcscpy(SearchFilter,L"(&" CATEGORY_CXTION L"(" ATTR_FROM_SERVER L"=");
  2379. wcscat(SearchFilter,SettingsDn);
  2380. wcscat(SearchFilter,L"))");
  2381. if (!FrsDsLdapSearchInit(Ldap, SitesDn, LDAP_SCOPE_SUBTREE, SearchFilter,
  2382. Attrs, 0, &FrsSearchContext)) {
  2383. SearchFilter = FrsFree(SearchFilter);
  2384. return ERROR_ACCESS_DENIED;
  2385. }
  2386. if (FrsSearchContext.EntriesInPage == 0) {
  2387. DPRINT1(1, ":DS: WARN - No sysvol outbound connections found for member %ws!\n", SettingsDn);
  2388. }
  2389. SearchFilter = FrsFree(SearchFilter);
  2390. //
  2391. // Scan the entries returned from ldap_search
  2392. //
  2393. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  2394. Entry != NULL;
  2395. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  2396. //
  2397. // Basic node info (guid, name, dn, schedule, and usnchanged)
  2398. //
  2399. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_IN_CXTION);
  2400. if (!Node) {
  2401. DPRINT(4, ":DS: Cxtion lacks basic info; skipping\n");
  2402. continue;
  2403. }
  2404. Node->EnabledCxtion = FrsDsFindValue(Ldap, Entry, ATTR_ENABLED_CXTION);
  2405. if (Node->EnabledCxtion && WSTR_EQ(Node->EnabledCxtion, ATTR_FALSE)) {
  2406. DPRINT2(1, ":DS: WARN - enabledConnection set to %ws; Ignoring %ws\n",
  2407. Node->EnabledCxtion, Node->Name->Name);
  2408. Node = FrsFreeType(Node);
  2409. continue;
  2410. }
  2411. //
  2412. // These are outbound connections.
  2413. //
  2414. Node->Inbound = FALSE;
  2415. //
  2416. // Node's partner's name. This is an outbound connection. Get the
  2417. // partners Dn by going one level up from the connection to the
  2418. // member Dn.
  2419. //
  2420. Node->PartnerDn = FrsWcsDup(wcsstr(Node->Dn + 3, L"cn="));
  2421. FRS_WCSLWR(Node->PartnerDn);
  2422. //
  2423. // Add the outbound cxtion to the cxtion table.
  2424. //
  2425. ConflictingNodeEntry = GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2426. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2427. if (ConflictingNodeEntry) {
  2428. ConflictingNode = ConflictingNodeEntry->Data;
  2429. FrsDsResolveCxtionConflict(ConflictingNode, Node, &Winner, &Loser);
  2430. if (WSTR_EQ(Winner->Dn, Node->Dn)) {
  2431. //
  2432. // The new one is the winner. Remove old one and insert new one.
  2433. //
  2434. GTabDelete(CxtionTable,ConflictingNodeEntry->Key1,ConflictingNodeEntry->Key2, NULL);
  2435. GTabInsertUniqueEntry(CxtionTable, Node, Node->PartnerDn, &Node->Inbound);
  2436. GTabDelete(AllCxtionsTable,ConflictingNode->PartnerDn, (PVOID)&ConflictingNode->Inbound, NULL);
  2437. GTabInsertUniqueEntry(AllCxtionsTable, Node, Node->PartnerDn, &Node->Inbound);
  2438. FrsFreeType(ConflictingNode);
  2439. } else {
  2440. //
  2441. // The old one is the winner. Leave it in the table.
  2442. //
  2443. FrsFreeType(Node);
  2444. continue;
  2445. }
  2446. } else {
  2447. //
  2448. // If there is no conflict then we need to add this Member to the MemberSearchFilter.
  2449. //
  2450. if (MemberSearchFilter != NULL) {
  2451. TempFilter = FrsAlloc((wcslen(MemberSearchFilter) + wcslen(L"(=)" ATTR_SERVER_REF) +
  2452. wcslen(Node->PartnerDn) + 1 ) * sizeof(WCHAR));
  2453. wcscpy(TempFilter, MemberSearchFilter);
  2454. wcscat(TempFilter, L"(" ATTR_SERVER_REF L"=");
  2455. wcscat(TempFilter, Node->PartnerDn);
  2456. wcscat(TempFilter, L")");
  2457. FrsFree(MemberSearchFilter);
  2458. MemberSearchFilter = TempFilter;
  2459. TempFilter = NULL;
  2460. } else {
  2461. MemberSearchFilter = FrsAlloc((wcslen(L"(|(=)" ATTR_SERVER_REF) +
  2462. wcslen(Node->PartnerDn) + 1 ) * sizeof(WCHAR));
  2463. wcscpy(MemberSearchFilter, L"(|(" ATTR_SERVER_REF L"=");
  2464. wcscat(MemberSearchFilter, Node->PartnerDn);
  2465. wcscat(MemberSearchFilter, L")");
  2466. }
  2467. }
  2468. //
  2469. // If sysvol, always on within a site
  2470. //
  2471. Node->SameSite = FrsDsSameSite(SettingsDn, Node->PartnerDn);
  2472. if (Node->SameSite) {
  2473. Node->Schedule = FrsFree(Node->Schedule);
  2474. }
  2475. }
  2476. FrsDsLdapSearchClose(&FrsSearchContext);
  2477. return ERROR_SUCCESS;
  2478. }
  2479. VOID
  2480. FrsDsMergeTwoWaySchedules(
  2481. IN PSCHEDULE *pISchedule,
  2482. IN DWORD *pIScheduleLen,
  2483. IN OUT PSCHEDULE *pOSchedule,
  2484. IN OUT DWORD *pOScheduleLen,
  2485. IN PSCHEDULE *pRSchedule
  2486. )
  2487. /*++
  2488. Routine Description:
  2489. Set the output schedule by merging the input schedule with the.
  2490. output schedule.
  2491. Schedules are merged to support NTDSCONN_OPT_TWOWAY_SYNC flag
  2492. on the connection object.
  2493. This function only merges the interval schedule (SCHEDULE_INTERVAL).
  2494. Other schedules are ignored and may be overwritten during merging.
  2495. Input Output Replica Resultant output schedule.
  2496. ----- ------ ----------------------------------
  2497. 0 0 0 Schedule is absent. Considered to be always on.
  2498. 0 0 1 Schedule is absent. Use replica sets schedule.
  2499. 0 1 0 Schedule is absent. Considered to be always on.
  2500. 0 1 1 Schedule is present.Merge replica set schedule with the schedule on the output.
  2501. 1 0 0 Schedule is present.Same as the one on input.
  2502. 1 0 1 Schedule is present.Merge replica set schedule with the schedule on the input.
  2503. 1 1 0 Schedule is present.Merge the input and output schedule.
  2504. 1 1 1 Schedule is present.Merge the input and output schedule.
  2505. Arguments:
  2506. pISchedule - Input schedule.
  2507. pIScheduleLen - Input schedule length.
  2508. pOSchedule - Resultant schedule.
  2509. pOScheduleLen - Resultant schedule length.
  2510. pRSchedule - Default replica set schedule.
  2511. Return Value:
  2512. NONE
  2513. --*/
  2514. {
  2515. #undef DEBSUB
  2516. #define DEBSUB "FrsDsMergeTwoWaySchedules:"
  2517. UINT i;
  2518. PUCHAR IScheduleData = NULL;
  2519. PUCHAR OScheduleData = NULL;
  2520. //
  2521. // Set the location of the data in the schedule structures that are
  2522. // non-null.
  2523. //
  2524. if (*pISchedule != NULL){
  2525. for (i=0; i< (*pISchedule)->NumberOfSchedules ; ++i) {
  2526. if ((*pISchedule)->Schedules[i].Type == SCHEDULE_INTERVAL) {
  2527. IScheduleData = ((PUCHAR)*pISchedule) + (*pISchedule)->Schedules[i].Offset;
  2528. break;
  2529. }
  2530. }
  2531. }
  2532. if (*pOSchedule != NULL){
  2533. for (i=0; i< (*pOSchedule)->NumberOfSchedules ; ++i) {
  2534. if ((*pOSchedule)->Schedules[i].Type == SCHEDULE_INTERVAL) {
  2535. OScheduleData = ((PUCHAR)*pOSchedule) + (*pOSchedule)->Schedules[i].Offset;
  2536. break;
  2537. }
  2538. }
  2539. }
  2540. //
  2541. // If there is no output schedule then copy the schedule
  2542. // from input to output if there is one on input. Now if there
  2543. // is a schedule on the replica set merge it with the new ouput
  2544. // schedule.
  2545. //
  2546. if (*pOSchedule == NULL || OScheduleData == NULL) {
  2547. if (*pISchedule == NULL) {
  2548. return;
  2549. }
  2550. *pOScheduleLen = *pIScheduleLen;
  2551. *pOSchedule = FrsAlloc(*pOScheduleLen);
  2552. CopyMemory(*pOSchedule, *pISchedule, *pOScheduleLen);
  2553. if (*pRSchedule == NULL) {
  2554. return;
  2555. }
  2556. //
  2557. // Update the location of output schedule data.
  2558. //
  2559. for (i=0; i< (*pOSchedule)->NumberOfSchedules ; ++i) {
  2560. if ((*pOSchedule)->Schedules[i].Type == SCHEDULE_INTERVAL) {
  2561. OScheduleData = ((PUCHAR)*pOSchedule) + (*pOSchedule)->Schedules[i].Offset;
  2562. break;
  2563. }
  2564. }
  2565. //
  2566. // Update the location of input schedule data.
  2567. //
  2568. for (i=0; i< (*pRSchedule)->NumberOfSchedules ; ++i) {
  2569. if ((*pRSchedule)->Schedules[i].Type == SCHEDULE_INTERVAL) {
  2570. IScheduleData = ((PUCHAR)*pRSchedule) + (*pRSchedule)->Schedules[i].Offset;
  2571. break;
  2572. }
  2573. }
  2574. }
  2575. //
  2576. // If there is no input schedule then check if there is a schedule
  2577. // on the replica set. If there is then merge that with the output schedule.
  2578. //
  2579. if ((*pISchedule == NULL || IScheduleData == NULL)) {
  2580. //
  2581. // Update the location of input schedule data. Pick it from replica set.
  2582. //
  2583. if (*pRSchedule != NULL) {
  2584. for (i=0; i< (*pRSchedule)->NumberOfSchedules ; ++i) {
  2585. if ((*pRSchedule)->Schedules[i].Type == SCHEDULE_INTERVAL) {
  2586. IScheduleData = ((PUCHAR)*pRSchedule) + (*pRSchedule)->Schedules[i].Offset;
  2587. break;
  2588. }
  2589. }
  2590. } else {
  2591. *pOSchedule = FrsFree(*pOSchedule);
  2592. *pOScheduleLen = 0;
  2593. return;
  2594. }
  2595. }
  2596. for (i=0 ; i<7*24 ; ++i) {
  2597. *(OScheduleData + i) = *(OScheduleData + i) | *(IScheduleData + i);
  2598. }
  2599. return;
  2600. }
  2601. DWORD
  2602. FrsDsGetSysvolCxtions(
  2603. IN PLDAP Ldap,
  2604. IN PWCHAR SetDn,
  2605. IN PWCHAR MemberRef,
  2606. IN PCONFIG_NODE Parent,
  2607. IN PCONFIG_NODE Computer
  2608. )
  2609. /*++
  2610. Routine Description:
  2611. Fetch the members for the replica set identified by Base.
  2612. Part of NewDs poll APIs.
  2613. Arguments:
  2614. ldap : Handle to DS.
  2615. SetDn : Dn of the set being processed.
  2616. MemberRef : MemberRef from the subscriber object.
  2617. Parent : Pointer to the set node in the config tree that is being built,
  2618. Return Value:
  2619. WIN32 Status
  2620. --*/
  2621. {
  2622. #undef DEBSUB
  2623. #define DEBSUB "FrsDsGetSysvolCxtions:"
  2624. PWCHAR Attrs[7];
  2625. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  2626. PCONFIG_NODE Node = NULL; // generic node for the tree
  2627. PCONFIG_NODE Subscriber;
  2628. PCONFIG_NODE PartnerNode = NULL;
  2629. PCONFIG_NODE MemberNode = NULL;
  2630. PCONFIG_NODE Cxtion = NULL;
  2631. DWORD WStatus = ERROR_SUCCESS;
  2632. PVOID Key = NULL;
  2633. PWCHAR TempFilter = NULL;
  2634. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  2635. PWCHAR SettingsDn = NULL;
  2636. MK_ATTRS_6(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  2637. ATTR_SERVER_REF, ATTR_COMPUTER_REF);
  2638. //
  2639. // Initialize the CxtionTable. We discard the table once we have
  2640. // loaded the replica set. We use the same variables for
  2641. // every replica set.
  2642. //
  2643. if (CxtionTable != NULL) {
  2644. CxtionTable = GTabFreeTable(CxtionTable, NULL);
  2645. }
  2646. CxtionTable = GTabAllocStringAndBoolTable();
  2647. //
  2648. // Initialize the MemberTable. We discard the table once we have
  2649. // loaded the replica set. We use the same variables for
  2650. // every replica set.
  2651. //
  2652. if (MemberTable != NULL) {
  2653. MemberTable = GTabFreeTable(MemberTable, NULL);
  2654. }
  2655. MemberTable = GTabAllocStringTable();
  2656. //
  2657. // We will form the MemberSearchFilter for this replica set.
  2658. //
  2659. if (MemberSearchFilter != NULL) {
  2660. MemberSearchFilter = FrsFree(MemberSearchFilter);
  2661. }
  2662. //
  2663. // We have to first get our member object to get the serverreference to
  2664. // know where to go to get the connections.
  2665. //
  2666. if (!FrsDsLdapSearchInit(Ldap, MemberRef, LDAP_SCOPE_BASE, CATEGORY_ANY,
  2667. Attrs, 0, &FrsSearchContext)) {
  2668. return ERROR_ACCESS_DENIED;
  2669. }
  2670. if (FrsSearchContext.EntriesInPage == 0) {
  2671. DPRINT1(1, ":DS: WARN - No member object found for member %ws!\n", MemberRef);
  2672. }
  2673. //
  2674. // Scan the entries returned from ldap_search
  2675. //
  2676. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  2677. Entry != NULL && WIN_SUCCESS(WStatus);
  2678. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  2679. //
  2680. // Basic node info (guid, name, dn, schedule, and usnchanged)
  2681. //
  2682. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_MEMBER);
  2683. if (!Node) {
  2684. DPRINT(0, ":DS: Member lacks basic info; skipping\n");
  2685. continue;
  2686. }
  2687. //
  2688. // NTDS Settings (DSA) Reference.
  2689. //
  2690. Node->SettingsDn = FrsDsFindValue(Ldap, Entry, ATTR_SERVER_REF);
  2691. if (Node->SettingsDn == NULL) {
  2692. DPRINT1(0, ":DS: WARN - Member (%ws) of sysvol replica set lacks server reference; skipping\n", Node->Dn);
  2693. Node->Consistent = FALSE;
  2694. //
  2695. // Add to the poll summary event log.
  2696. //
  2697. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_MEMBER,
  2698. Node->Dn, ATTR_SERVER_REF);
  2699. Node = FrsFreeType(Node);
  2700. continue;
  2701. }
  2702. FRS_WCSLWR(Node->SettingsDn);
  2703. FrsFree(SettingsDn);
  2704. SettingsDn = FrsWcsDup(Node->SettingsDn);
  2705. //
  2706. // Computer Reference
  2707. //
  2708. Node->ComputerDn = FrsDsFindValue(Ldap, Entry, ATTR_COMPUTER_REF);
  2709. if (Node->ComputerDn == NULL) {
  2710. DPRINT1(0, ":DS: WARN - Member (%ws) of sysvol replica set lacks computer reference; skipping\n", Node->Dn);
  2711. Node->Consistent = FALSE;
  2712. //
  2713. // Add to the poll summary event log.
  2714. //
  2715. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_MEMBER,
  2716. Node->Dn, ATTR_COMPUTER_REF);
  2717. Node = FrsFreeType(Node);
  2718. continue;
  2719. }
  2720. FRS_WCSLWR(Node->ComputerDn);
  2721. //
  2722. // Link into config and add to the running checksum
  2723. //
  2724. FrsDsTreeLink(Parent, Node);
  2725. //
  2726. // Insert the new member in the member table only if it is not there already.
  2727. // For sysvols insert the members with their settingsdn as the primary key
  2728. // because that is what is stored in the cxtion->PartnerDn structure at this time.
  2729. //
  2730. GTabInsertUniqueEntry(MemberTable, Node, Node->SettingsDn, NULL);
  2731. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeMember", Node);
  2732. }
  2733. FrsDsLdapSearchClose(&FrsSearchContext);
  2734. //
  2735. // We can't do any further processing if the Node is not consistent.
  2736. //
  2737. if (Node == NULL || !Node->Consistent) {
  2738. FrsFree(SettingsDn);
  2739. return ERROR_INVALID_DATA;
  2740. }
  2741. //
  2742. // Get the outbound connections.
  2743. //
  2744. WStatus = FrsDsGetSysvolOutboundCxtions(Ldap, SettingsDn);
  2745. if (!WIN_SUCCESS(WStatus)) {
  2746. FrsFree(SettingsDn);
  2747. return WStatus;
  2748. }
  2749. //
  2750. // Get the inbound connections.
  2751. //
  2752. WStatus = FrsDsGetSysvolInboundCxtions(Ldap, SettingsDn);
  2753. if (!WIN_SUCCESS(WStatus)) {
  2754. FrsFree(SettingsDn);
  2755. return WStatus;
  2756. }
  2757. //
  2758. // The above two calls build the MemberFilter.
  2759. // MemberFilter is used to search the DS for all the member objects of
  2760. // interest. If there are no connections from or to this member then
  2761. // the filter will be NULL.
  2762. //
  2763. if (MemberSearchFilter == NULL) {
  2764. //
  2765. // Is this member linked to this computer
  2766. //
  2767. MemberNode = Node;
  2768. Subscriber = GTabLookupTableString(SubscriberTable, MemberNode->Dn, NULL);
  2769. //
  2770. // Yep; have a suscriber
  2771. //
  2772. if (Subscriber != NULL) {
  2773. MemberNode->ThisComputer = TRUE;
  2774. MemberNode->Root = FrsWcsDup(Subscriber->Root);
  2775. MemberNode->Stage = FrsWcsDup(Subscriber->Stage);
  2776. FRS_WCSLWR(MemberNode->Root);
  2777. FRS_WCSLWR(MemberNode->Stage);
  2778. MemberNode->DnsName = FrsWcsDup(Computer->DnsName);
  2779. }
  2780. FrsFree(SettingsDn);
  2781. return ERROR_SUCCESS;
  2782. } else {
  2783. //
  2784. // Add the closing ')' to the MemberSearchFilter.
  2785. //
  2786. TempFilter = FrsAlloc((wcslen(MemberSearchFilter) + wcslen(L")") + 1 ) * sizeof(WCHAR));
  2787. wcscpy(TempFilter, MemberSearchFilter);
  2788. wcscat(TempFilter, L")");
  2789. FrsFree(MemberSearchFilter);
  2790. MemberSearchFilter = TempFilter;
  2791. TempFilter = NULL;
  2792. }
  2793. if (!FrsDsLdapSearchInit(Ldap, SetDn, LDAP_SCOPE_ONELEVEL, MemberSearchFilter,
  2794. Attrs, 0, &FrsSearchContext)) {
  2795. FrsFree(SettingsDn);
  2796. return ERROR_ACCESS_DENIED;
  2797. }
  2798. if (FrsSearchContext.EntriesInPage == 0) {
  2799. DPRINT1(1, ":DS: WARN - No member objects of interest found under %ws!\n", SetDn);
  2800. }
  2801. //
  2802. // Scan the entries returned from ldap_search
  2803. //
  2804. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  2805. Entry != NULL && WIN_SUCCESS(WStatus);
  2806. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  2807. //
  2808. // Basic node info (guid, name, dn, schedule, and usnchanged)
  2809. //
  2810. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_MEMBER);
  2811. if (!Node) {
  2812. DPRINT(0, ":DS: Member lacks basic info; skipping\n");
  2813. continue;
  2814. }
  2815. //
  2816. // NTDS Settings (DSA) Reference.
  2817. //
  2818. Node->SettingsDn = FrsDsFindValue(Ldap, Entry, ATTR_SERVER_REF);
  2819. if (Node->SettingsDn == NULL) {
  2820. DPRINT1(0, ":DS: WARN - Member (%ws) of sysvol replica set lacks server reference; skipping\n", Node->Dn);
  2821. Node->Consistent = FALSE;
  2822. //
  2823. // Add to the poll summary event log.
  2824. //
  2825. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_MEMBER,
  2826. Node->Dn, ATTR_SERVER_REF);
  2827. Node = FrsFreeType(Node);
  2828. continue;
  2829. }
  2830. FRS_WCSLWR(Node->SettingsDn);
  2831. //
  2832. // Computer Reference
  2833. //
  2834. Node->ComputerDn = FrsDsFindValue(Ldap, Entry, ATTR_COMPUTER_REF);
  2835. if (Node->ComputerDn == NULL) {
  2836. DPRINT1(0, ":DS: WARN - Member (%ws) of sysvol replica set lacks computer reference; skipping\n", Node->Dn);
  2837. //
  2838. // Add to the poll summary event log.
  2839. //
  2840. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_MEMBER,
  2841. Node->Dn, ATTR_COMPUTER_REF);
  2842. Node = FrsFreeType(Node);
  2843. continue;
  2844. }
  2845. FRS_WCSLWR(Node->ComputerDn);
  2846. //
  2847. // Link into config and add to the running checksum
  2848. //
  2849. FrsDsTreeLink(Parent, Node);
  2850. //
  2851. // Insert the new member in the member table only if it is not there already.
  2852. // For sysvols insert the members with their settingsdn as the primary key
  2853. // because that is what is stored in the cxtion->PartnerDn structure at this time.
  2854. //
  2855. GTabInsertUniqueEntry(MemberTable, Node, Node->SettingsDn, NULL);
  2856. //
  2857. // Make a table of computers of interest to us so we can search for all
  2858. // the computers of interest at one time after we have polled all
  2859. // replica sets. Put empty entries in the table at this point.
  2860. // Do not add our computer in this table as we already have info about
  2861. // our computer.
  2862. //
  2863. if (WSTR_NE(Node->ComputerDn, Computer->Dn)) {
  2864. //
  2865. // This is not our computer. Add it to the table if it isn't already in the table.
  2866. //
  2867. PartnerNode = GTabLookupTableString(PartnerComputerTable, Node->ComputerDn, NULL);
  2868. if (PartnerNode == NULL) {
  2869. //
  2870. // There are no duplicates so enter this computer name in the table.
  2871. //
  2872. PartnerNode = FrsDsAllocBasicNode(Ldap, NULL, CONFIG_TYPE_COMPUTER);
  2873. PartnerNode->Dn = FrsWcsDup(Node->ComputerDn);
  2874. PartnerNode->MemberDn = FrsWcsDup(Node->Dn);
  2875. GTabInsertUniqueEntry(PartnerComputerTable, PartnerNode, PartnerNode->Dn, NULL);
  2876. }
  2877. }
  2878. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeMember", Node);
  2879. }
  2880. FrsDsLdapSearchClose(&FrsSearchContext);
  2881. //
  2882. // Link the inbound and outbound connections to our member node.
  2883. //
  2884. MemberNode = GTabLookupTableString(MemberTable, SettingsDn, NULL);
  2885. if (MemberNode != NULL) {
  2886. //
  2887. // Is this member linked to this computer
  2888. //
  2889. Subscriber = GTabLookupTableString(SubscriberTable, MemberNode->Dn, NULL);
  2890. //
  2891. // Yep; have a suscriber
  2892. //
  2893. if (Subscriber != NULL) {
  2894. MemberNode->ThisComputer = TRUE;
  2895. MemberNode->Root = FrsWcsDup(Subscriber->Root);
  2896. MemberNode->Stage = FrsWcsDup(Subscriber->Stage);
  2897. FRS_WCSLWR(MemberNode->Root);
  2898. FRS_WCSLWR(MemberNode->Stage);
  2899. MemberNode->DnsName = FrsWcsDup(Computer->DnsName);
  2900. //
  2901. // This is us. Link all the cxtions to this Member.
  2902. //
  2903. if (CxtionTable != NULL) {
  2904. Key = NULL;
  2905. while ((Cxtion = GTabNextDatum(CxtionTable, &Key)) != NULL) {
  2906. //
  2907. // Get our Partners Node from the member table.
  2908. //
  2909. PartnerNode = GTabLookupTableString(MemberTable, Cxtion->PartnerDn, NULL);
  2910. if (PartnerNode != NULL) {
  2911. Cxtion->PartnerName = FrsDupGName(PartnerNode->Name);
  2912. Cxtion->PartnerCoDn = FrsWcsDup(PartnerNode->ComputerDn);
  2913. } else {
  2914. //
  2915. // This Cxtion does not have a valid member object for its
  2916. // partner. E.g. A sysvol topology that has connections under
  2917. // the NTDSSettings objects but there are no corresponding
  2918. // member objects.
  2919. //
  2920. DPRINT1(0, ":DS: Marking connection inconsistent.(%ws)\n",Cxtion->Dn);
  2921. Cxtion->Consistent = FALSE;
  2922. }
  2923. FrsDsTreeLink(MemberNode, Cxtion);
  2924. }
  2925. CxtionTable = GTabFreeTable(CxtionTable,NULL);
  2926. }
  2927. }
  2928. }
  2929. FrsFree(SettingsDn);
  2930. return WStatus;
  2931. }
  2932. DWORD
  2933. FrsDsGetNonSysvolCxtions(
  2934. IN PLDAP Ldap,
  2935. IN PWCHAR SetDn,
  2936. IN PWCHAR MemberRef,
  2937. IN PCONFIG_NODE Parent,
  2938. IN PCONFIG_NODE Computer
  2939. )
  2940. /*++
  2941. Routine Description:
  2942. Fetch the members and connections for the replica set identified by Base.
  2943. Part of NewDs poll APIs.
  2944. Arguments:
  2945. ldap : Handle to DS.
  2946. SetDn : Dn of the set being processed.
  2947. MemberRef : MemberRef from the subscriber object.
  2948. Parent : Pointer to the set node in the config tree that is being built,
  2949. Return Value:
  2950. WIN32 Status
  2951. --*/
  2952. {
  2953. #undef DEBSUB
  2954. #define DEBSUB "FrsDsGetNonSysvolCxtions:"
  2955. PWCHAR Attrs[7];
  2956. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  2957. PCONFIG_NODE Node; // generic node for the tree
  2958. PCONFIG_NODE Subscriber;
  2959. PCONFIG_NODE PartnerNode = NULL;
  2960. PCONFIG_NODE MemberNode = NULL;
  2961. PCONFIG_NODE Cxtion = NULL;
  2962. DWORD WStatus = ERROR_SUCCESS;
  2963. PVOID Key = NULL;
  2964. PWCHAR MemberCn = NULL;
  2965. PWCHAR TempFilter = NULL;
  2966. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  2967. //
  2968. // MemberRef must be non-NULL.
  2969. //
  2970. if(MemberRef == NULL) {
  2971. return ERROR_INVALID_PARAMETER;
  2972. }
  2973. //
  2974. // Initialize the CxtionTable. We discard the table once we have
  2975. // loaded the replica set. We use the same variables for
  2976. // every replica set.
  2977. //
  2978. if (CxtionTable != NULL) {
  2979. CxtionTable = GTabFreeTable(CxtionTable, NULL);
  2980. }
  2981. CxtionTable = GTabAllocStringAndBoolTable();
  2982. //
  2983. // Initialize the MemberTable. We discard the table once we have
  2984. // loaded the replica set. We use the same variables for
  2985. // every replica set.
  2986. //
  2987. if (MemberTable != NULL) {
  2988. MemberTable = GTabFreeTable(MemberTable, NULL);
  2989. }
  2990. MemberTable = GTabAllocStringTable();
  2991. //
  2992. // We will form the MemberSearchFilter for this replica set.
  2993. //
  2994. if (MemberSearchFilter != NULL) {
  2995. MemberSearchFilter = FrsFree(MemberSearchFilter);
  2996. }
  2997. //
  2998. // Add this members name to the member search filter.
  2999. //
  3000. MemberCn = FrsDsMakeRdn(MemberRef);
  3001. MemberSearchFilter = FrsAlloc((wcslen(L"(|(=)" ATTR_CN) +
  3002. wcslen(MemberCn) + 1 ) * sizeof(WCHAR));
  3003. wcscpy(MemberSearchFilter, L"(|(" ATTR_CN L"=");
  3004. wcscat(MemberSearchFilter, MemberCn);
  3005. wcscat(MemberSearchFilter, L")");
  3006. MemberCn = FrsFree(MemberCn);
  3007. //
  3008. // Get the outbound connections.
  3009. //
  3010. WStatus = FrsDsGetNonSysvolOutboundCxtions(Ldap, SetDn, MemberRef);
  3011. if (!WIN_SUCCESS(WStatus)) {
  3012. return WStatus;
  3013. }
  3014. //
  3015. // Get the inbound connections.
  3016. //
  3017. WStatus = FrsDsGetNonSysvolInboundCxtions(Ldap, SetDn, MemberRef);
  3018. if (!WIN_SUCCESS(WStatus)) {
  3019. return WStatus;
  3020. }
  3021. //
  3022. // The above twp calls build the MemberFilter.
  3023. // MemberFilter is used to search the DS for all the member objects of
  3024. // interest. If there are no connections from or to this member then
  3025. // the filter will will just have 1 entry.
  3026. //
  3027. //
  3028. // Add the closing ')' to the MemberSearchFilter.
  3029. //
  3030. TempFilter = FrsAlloc((wcslen(MemberSearchFilter) + wcslen(L")") + 1 ) * sizeof(WCHAR));
  3031. wcscpy(TempFilter, MemberSearchFilter);
  3032. wcscat(TempFilter, L")");
  3033. FrsFree(MemberSearchFilter);
  3034. MemberSearchFilter = TempFilter;
  3035. TempFilter = NULL;
  3036. MK_ATTRS_6(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  3037. ATTR_SERVER_REF, ATTR_COMPUTER_REF);
  3038. if (!FrsDsLdapSearchInit(Ldap, SetDn, LDAP_SCOPE_ONELEVEL, MemberSearchFilter,
  3039. Attrs, 0, &FrsSearchContext)) {
  3040. return ERROR_ACCESS_DENIED;
  3041. }
  3042. if (FrsSearchContext.EntriesInPage == 0) {
  3043. DPRINT1(1, ":DS: WARN - No member objects of interest found under %ws!\n", SetDn);
  3044. }
  3045. //
  3046. // Scan the entries returned from ldap_search
  3047. //
  3048. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  3049. Entry != NULL && WIN_SUCCESS(WStatus);
  3050. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  3051. //
  3052. // Basic node info (guid, name, dn, schedule, and usnchanged)
  3053. //
  3054. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_MEMBER);
  3055. if (!Node) {
  3056. DPRINT(4, ":DS: Member lacks basic info; skipping\n");
  3057. continue;
  3058. }
  3059. //
  3060. // Computer Reference
  3061. //
  3062. Node->ComputerDn = FrsDsFindValue(Ldap, Entry, ATTR_COMPUTER_REF);
  3063. if (Node->ComputerDn == NULL) {
  3064. DPRINT1(4, ":DS: WARN - Member (%ws) lacks computer reference; skipping\n", Node->Dn);
  3065. //
  3066. // Add to the poll summary event log.
  3067. //
  3068. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_MEMBER,
  3069. Node->Dn, ATTR_COMPUTER_REF);
  3070. Node = FrsFreeType(Node);
  3071. continue;
  3072. }
  3073. FRS_WCSLWR(Node->ComputerDn);
  3074. //
  3075. // Link into config and add to the running checksum
  3076. //
  3077. FrsDsTreeLink(Parent, Node);
  3078. //
  3079. // Insert the new member in the member table only if it is not there already.
  3080. //
  3081. GTabInsertUniqueEntry(MemberTable, Node, Node->Dn, NULL);
  3082. //
  3083. // Make a table of computers of interest to us so we can search for all
  3084. // the computers of interest at one time after we have polled all
  3085. // replica sets. Put empty entries in the table at this point.
  3086. // Do not add our computer in this table as we already have info about
  3087. // our computer.
  3088. //
  3089. if (WSTR_NE(Node->ComputerDn, Computer->Dn)) {
  3090. //
  3091. // This is not our computer. Add it to the table if it isn't already in the table.
  3092. //
  3093. PartnerNode = GTabLookupTableString(PartnerComputerTable, Node->ComputerDn, NULL);
  3094. if (PartnerNode == NULL) {
  3095. //
  3096. // There are no duplicates so enter this computer name in the table.
  3097. //
  3098. PartnerNode = FrsDsAllocBasicNode(Ldap, NULL, CONFIG_TYPE_COMPUTER);
  3099. PartnerNode->Dn = FrsWcsDup(Node->ComputerDn);
  3100. PartnerNode->MemberDn = FrsWcsDup(Node->Dn);
  3101. GTabInsertUniqueEntry(PartnerComputerTable, PartnerNode, PartnerNode->Dn, NULL);
  3102. }
  3103. }
  3104. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeMember", Node);
  3105. }
  3106. FrsDsLdapSearchClose(&FrsSearchContext);
  3107. //
  3108. // Link the inbound and outbound connections to our member node.
  3109. //
  3110. MemberNode = GTabLookupTableString(MemberTable, MemberRef, NULL);
  3111. if (MemberNode != NULL) {
  3112. //
  3113. // Is this member linked to this computer
  3114. //
  3115. Subscriber = GTabLookupTableString(SubscriberTable, MemberNode->Dn, NULL);
  3116. //
  3117. // Yep; have a suscriber
  3118. //
  3119. if (Subscriber != NULL) {
  3120. MemberNode->ThisComputer = TRUE;
  3121. MemberNode->Root = FrsWcsDup(Subscriber->Root);
  3122. MemberNode->Stage = FrsWcsDup(Subscriber->Stage);
  3123. FRS_WCSLWR(MemberNode->Root);
  3124. FRS_WCSLWR(MemberNode->Stage);
  3125. MemberNode->DnsName = FrsWcsDup(Computer->DnsName);
  3126. //
  3127. // This is us. Link all the cxtions to this Member.
  3128. //
  3129. if (CxtionTable != NULL) {
  3130. Key = NULL;
  3131. while ((Cxtion = GTabNextDatum(CxtionTable, &Key)) != NULL) {
  3132. //
  3133. // Get our Partners Node from the member table.
  3134. //
  3135. PartnerNode = GTabLookupTableString(MemberTable, Cxtion->PartnerDn, NULL);
  3136. if (PartnerNode != NULL) {
  3137. Cxtion->PartnerName = FrsDupGName(PartnerNode->Name);
  3138. Cxtion->PartnerCoDn = FrsWcsDup(PartnerNode->ComputerDn);
  3139. } else {
  3140. //
  3141. // This Cxtion does not have a valid member object for its
  3142. // partner. E.g. A sysvol topology that has connections under
  3143. // the NTDSSettings objects but there are no corresponding
  3144. // member objects.
  3145. //
  3146. DPRINT1(0, ":DS: Marking connection inconsistent.(%ws)\n",Cxtion->Dn);
  3147. Cxtion->Consistent = FALSE;
  3148. }
  3149. FrsDsTreeLink(MemberNode, Cxtion);
  3150. }
  3151. CxtionTable = GTabFreeTable(CxtionTable,NULL);
  3152. }
  3153. }
  3154. }
  3155. return WStatus;
  3156. }
  3157. DWORD
  3158. FrsDsGetSets(
  3159. IN PLDAP Ldap,
  3160. IN PWCHAR SetDnAddr,
  3161. IN PWCHAR MemberRef,
  3162. IN PCONFIG_NODE Parent,
  3163. IN PCONFIG_NODE Computer
  3164. )
  3165. /*++
  3166. Routine Description:
  3167. Recursively scan the DS tree beginning at
  3168. configuration\sites\settings\sets.
  3169. Part of NewDs poll APIs.
  3170. Arguments:
  3171. ldap - opened and bound ldap connection
  3172. SetDnAddr - From member reference from subscriber
  3173. Parent - Container which contains Base
  3174. Computer - for member back links
  3175. Return Value:
  3176. ERROR_SUCCESS - config fetched successfully
  3177. Otherwise - couldn't get the DS config
  3178. --*/
  3179. {
  3180. #undef DEBSUB
  3181. #define DEBSUB "FrsDsGetSets:"
  3182. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  3183. PCONFIG_NODE Node; // generic node for the tree
  3184. DWORD i;
  3185. DWORD WStatus = ERROR_SUCCESS;
  3186. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  3187. PWCHAR FlagsWStr = NULL;
  3188. PWCHAR Attrs[10];
  3189. //
  3190. // Have we processed this set before? If we have then don't process
  3191. // it again. This check prevents two subscribers to point to
  3192. // different member objects that are members of the same set.
  3193. //
  3194. Node = GTabLookupTableString(SetTable, SetDnAddr, NULL);
  3195. if (Node) {
  3196. return ERROR_SUCCESS;
  3197. }
  3198. //
  3199. // Search the DS beginning at Base for sets (objectCategory=nTFRSReplicaSet)
  3200. //
  3201. MK_ATTRS_9(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED, ATTR_FRS_FLAGS,
  3202. ATTR_SET_TYPE, ATTR_PRIMARY_MEMBER, ATTR_FILE_FILTER, ATTR_DIRECTORY_FILTER);
  3203. if (!FrsDsLdapSearchInit(Ldap, SetDnAddr, LDAP_SCOPE_BASE, CATEGORY_REPLICA_SET,
  3204. Attrs, 0, &FrsSearchContext)) {
  3205. return ERROR_ACCESS_DENIED;
  3206. }
  3207. if (FrsSearchContext.EntriesInPage == 0) {
  3208. DPRINT1(1, ":DS: WARN - No replica set objects found under %ws!\n", SetDnAddr);
  3209. }
  3210. //
  3211. // Scan the entries returned from ldap_search
  3212. //
  3213. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  3214. Entry != NULL && WIN_SUCCESS(WStatus);
  3215. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  3216. //
  3217. // Basic node info (guid, name, dn, schedule, and usnchanged)
  3218. //
  3219. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_REPLICA_SET);
  3220. if (!Node) {
  3221. DPRINT(4, ":DS: Set lacks basic info; skipping\n");
  3222. continue;
  3223. }
  3224. //
  3225. // Replica set type
  3226. //
  3227. Node->SetType = FrsDsFindValue(Ldap, Entry, ATTR_SET_TYPE);
  3228. //
  3229. // Check the set type. It has to be one that we recognize.
  3230. //
  3231. if ((Node->SetType == NULL) ||
  3232. (WSTR_NE(Node->SetType, FRS_RSTYPE_OTHERW) &&
  3233. WSTR_NE(Node->SetType, FRS_RSTYPE_DFSW) &&
  3234. WSTR_NE(Node->SetType, FRS_RSTYPE_DOMAIN_SYSVOLW) &&
  3235. WSTR_NE(Node->SetType, FRS_RSTYPE_ENTERPRISE_SYSVOLW))){
  3236. DPRINT1(4, ":DS: ERROR - Invalid Set type for (%ws)\n", Node->Dn);
  3237. //
  3238. // Add to the poll summary event log.
  3239. //
  3240. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_REPLICA_SET,
  3241. Node->Dn, ATTR_SET_TYPE);
  3242. Node = FrsFreeType(Node);
  3243. continue;
  3244. }
  3245. //
  3246. // Primary member
  3247. //
  3248. Node->MemberDn = FrsDsFindValue(Ldap, Entry, ATTR_PRIMARY_MEMBER);
  3249. //
  3250. // File filter
  3251. //
  3252. Node->FileFilterList = FrsDsFindValue(Ldap, Entry, ATTR_FILE_FILTER);
  3253. //
  3254. // Directory filter
  3255. //
  3256. Node->DirFilterList = FrsDsFindValue(Ldap, Entry, ATTR_DIRECTORY_FILTER);
  3257. //
  3258. // Read the FRS Flags value.
  3259. //
  3260. FlagsWStr = FrsDsFindValue(Ldap, Entry, ATTR_FRS_FLAGS);
  3261. if (FlagsWStr != NULL) {
  3262. Node->FrsRsoFlags = _wtoi(FlagsWStr);
  3263. FlagsWStr = FrsFree(FlagsWStr);
  3264. } else {
  3265. Node->FrsRsoFlags = 0;
  3266. }
  3267. //
  3268. // Link into config and add to the running checksum
  3269. //
  3270. FrsDsTreeLink(Parent, Node);
  3271. //
  3272. // Insert into the table of sets. We checked for duplicates above with
  3273. // GTabLookupTableString so there should not be any duplicates.
  3274. //
  3275. FRS_ASSERT(GTabInsertUniqueEntry(SetTable, Node, Node->Dn, NULL) == NULL);
  3276. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeSet", Node);
  3277. //
  3278. // Get the replica set topology. We have to look at different places
  3279. // in the DS depending on the type of replica set. The cxtions for sysvol
  3280. // replica set are generated by KCC and they reside under the server object
  3281. // for the DC. We use the serverReference from the member object to get
  3282. // there.
  3283. //
  3284. if (FRS_RSTYPE_IS_SYSVOLW(Node->SetType)) {
  3285. WStatus = FrsDsGetSysvolCxtions(Ldap, SetDnAddr, MemberRef, Node, Computer);
  3286. } else {
  3287. WStatus = FrsDsGetNonSysvolCxtions(Ldap, SetDnAddr, MemberRef, Node, Computer);
  3288. }
  3289. }
  3290. FrsDsLdapSearchClose(&FrsSearchContext);
  3291. return WStatus;
  3292. }
  3293. DWORD
  3294. FrsDsGetSettings(
  3295. IN PLDAP Ldap,
  3296. IN PWCHAR MemberRef,
  3297. IN PCONFIG_NODE Parent,
  3298. IN PCONFIG_NODE Computer
  3299. )
  3300. /*++
  3301. Routine Description:
  3302. Scan the DS tree for NTFRS-Settings objects and their servers
  3303. Part of NewDs poll APIs.
  3304. Arguments:
  3305. ldap - opened and bound ldap connection
  3306. MemberRef - From the subscriber member reference
  3307. Parent - Container which contains Base
  3308. Computer
  3309. Return Value:
  3310. WIN32 Status
  3311. --*/
  3312. {
  3313. #undef DEBSUB
  3314. #define DEBSUB "FrsDsGetSettings:"
  3315. PWCHAR Attrs[5];
  3316. PLDAPMessage Entry; // Opaque stuff from ldap subsystem
  3317. PCONFIG_NODE Node; // generic node for the tree
  3318. PWCHAR MemberDnAddr;
  3319. PWCHAR SetDnAddr;
  3320. PWCHAR SettingsDnAddr;
  3321. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  3322. DWORD WStatus = ERROR_SUCCESS;
  3323. //
  3324. // Find the member component
  3325. //
  3326. MemberDnAddr = wcsstr(MemberRef, L"cn=");
  3327. if (!MemberDnAddr) {
  3328. DPRINT1(0, ":DS: ERROR - Missing member component in %ws\n", MemberRef);
  3329. return ERROR_ACCESS_DENIED;
  3330. }
  3331. //
  3332. // Find the set component
  3333. //
  3334. SetDnAddr = wcsstr(MemberDnAddr + 3, L"cn=");
  3335. if (!SetDnAddr) {
  3336. DPRINT1(0, ":DS: ERROR - Missing set component in %ws\n", MemberRef);
  3337. return ERROR_ACCESS_DENIED;
  3338. }
  3339. //
  3340. // Find the settings component
  3341. //
  3342. SettingsDnAddr = wcsstr(SetDnAddr + 3, L"cn=");
  3343. if (!SettingsDnAddr) {
  3344. DPRINT1(0, ":DS: ERROR - Missing settings component in %ws\n", MemberRef);
  3345. return ERROR_ACCESS_DENIED;
  3346. }
  3347. //
  3348. // Have we processed this settings before?
  3349. //
  3350. for (Node = Parent->Children; Node; Node = Node->Peer) {
  3351. if (WSTR_EQ(Node->Dn, SettingsDnAddr)) {
  3352. DPRINT1(4, ":DS: Settings hit on %ws\n", MemberRef);
  3353. break;
  3354. }
  3355. }
  3356. //
  3357. // Yep; get the sets
  3358. //
  3359. if (Node) {
  3360. return FrsDsGetSets(Ldap, SetDnAddr, MemberRef, Node, Computer);
  3361. }
  3362. //
  3363. // Search the DS beginning at Base for settings (objectCategory=nTFRSSettings)
  3364. //
  3365. MK_ATTRS_4(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED);
  3366. if (!FrsDsLdapSearchInit(Ldap, SettingsDnAddr, LDAP_SCOPE_BASE, CATEGORY_NTFRS_SETTINGS,
  3367. Attrs, 0, &FrsSearchContext)) {
  3368. return ERROR_ACCESS_DENIED;
  3369. }
  3370. if (FrsSearchContext.EntriesInPage == 0) {
  3371. DPRINT1(1, ":DS: WARN - No NTFRSSettings objects found under %ws!\n", SettingsDnAddr);
  3372. }
  3373. //
  3374. // Scan the entries returned from ldap_search
  3375. //
  3376. for (Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  3377. Entry != NULL && WIN_SUCCESS(WStatus);
  3378. Entry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  3379. //
  3380. // Basic node info (guid, name, dn, schedule, and usnchanged)
  3381. //
  3382. Node = FrsDsAllocBasicNode(Ldap, Entry, CONFIG_TYPE_NTFRS_SETTINGS);
  3383. if (!Node) {
  3384. DPRINT(4, ":DS: Frs Settings lacks basic info; skipping\n");
  3385. continue;
  3386. }
  3387. //
  3388. // Link into config and add to the running checksum
  3389. //
  3390. FrsDsTreeLink(Parent, Node);
  3391. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeSettings", Node);
  3392. //
  3393. // Recurse to the next level in the DS hierarchy
  3394. //
  3395. WStatus = FrsDsGetSets(Ldap, SetDnAddr, MemberRef, Node, Computer);
  3396. }
  3397. FrsDsLdapSearchClose(&FrsSearchContext);
  3398. return WStatus;
  3399. }
  3400. DWORD
  3401. FrsDsGetServices(
  3402. IN PLDAP Ldap,
  3403. IN PCONFIG_NODE Computer,
  3404. OUT PCONFIG_NODE *Services
  3405. )
  3406. /*++
  3407. Routine Description:
  3408. Recursively scan the DS tree beginning at the settings from
  3409. the subscriber nodes.
  3410. The name is a misnomer because of evolution.
  3411. Part of NewDs poll APIs.
  3412. Arguments:
  3413. ldap - opened and bound ldap connection
  3414. Computer
  3415. Services - returned list of all Settings
  3416. Return Value:
  3417. WIN32 Status
  3418. --*/
  3419. {
  3420. #undef DEBSUB
  3421. #define DEBSUB "FrsDsGetServices:"
  3422. PCONFIG_NODE Node;
  3423. PCONFIG_NODE Subscriptions;
  3424. PCONFIG_NODE Subscriber;
  3425. PVOID SubKey = NULL;
  3426. DWORD WStatus = ERROR_SUCCESS;
  3427. *Services = NULL;
  3428. //
  3429. // Initialize the SubscriberTable.
  3430. //
  3431. if (SetTable != NULL) {
  3432. SetTable = GTabFreeTable(SetTable,NULL);
  3433. }
  3434. SetTable = GTabAllocStringTable();
  3435. //
  3436. // Initially, the node is assumed to be consistent
  3437. //
  3438. Node = FrsAllocType(CONFIG_NODE_TYPE);
  3439. Node->DsObjectType = CONFIG_TYPE_SERVICES_ROOT;
  3440. Node->Consistent = TRUE;
  3441. //
  3442. // Distinguished name
  3443. //
  3444. Node->Dn = FrsWcsDup(L"<<replica ds root>>");
  3445. FRS_WCSLWR(Node->Dn);
  3446. //
  3447. // Name = RDN + Object Guid
  3448. //
  3449. Node->Name = FrsBuildGName(FrsAlloc(sizeof(GUID)),
  3450. FrsWcsDup(L"<<replica ds root>>"));
  3451. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeService", Node);
  3452. SubKey = NULL;
  3453. while ((Subscriber = GTabNextDatum(SubscriberTable, &SubKey)) != NULL) {
  3454. //
  3455. // Recurse to the next level in the DS hierarchy
  3456. //
  3457. WStatus = FrsDsGetSettings(Ldap, Subscriber->MemberDn, Node, Computer);
  3458. DPRINT1_WS(2, ":DS: WARN - Error getting topology for replica root (%ws);", Subscriber->Root, WStatus);
  3459. }
  3460. *Services = Node;
  3461. return WStatus;
  3462. }
  3463. PWCHAR
  3464. FrsDsGetDnsName(
  3465. IN PLDAP Ldap,
  3466. IN PWCHAR Dn
  3467. )
  3468. /*++
  3469. Routine Description:
  3470. Read the dNSHostName attribute from Dn
  3471. Arguments:
  3472. Ldap - opened and bound ldap connection
  3473. Dn - Base Dn for search
  3474. Return Value:
  3475. WIN32 Status
  3476. --*/
  3477. {
  3478. #undef DEBSUB
  3479. #define DEBSUB "FrsDsGetDnsName:"
  3480. PLDAPMessage LdapMsg = NULL;
  3481. PLDAPMessage LdapEntry;
  3482. PWCHAR DnsName = NULL;
  3483. PWCHAR Attrs[2];
  3484. DWORD WStatus = ERROR_SUCCESS;
  3485. //
  3486. // Search the DS beginning at Base for the entries of class (objectCategory=*)
  3487. //
  3488. MK_ATTRS_1(Attrs, ATTR_DNS_HOST_NAME);
  3489. //
  3490. // Note: Is it safe to turn off referrals re: back links?
  3491. // if so, use ldap_get/set_option in winldap.h
  3492. //
  3493. if (!FrsDsLdapSearch(Ldap, Dn, LDAP_SCOPE_BASE, CATEGORY_ANY,
  3494. Attrs, 0, &LdapMsg)) {
  3495. goto CLEANUP;
  3496. }
  3497. //
  3498. // Scan the entries returned from ldap_search
  3499. //
  3500. LdapEntry = ldap_first_entry(Ldap, LdapMsg);
  3501. if (!LdapEntry) {
  3502. goto CLEANUP;
  3503. }
  3504. //
  3505. // DNS name
  3506. //
  3507. DnsName = FrsDsFindValue(Ldap, LdapEntry, ATTR_DNS_HOST_NAME);
  3508. CLEANUP:
  3509. LDAP_FREE_MSG(LdapMsg);
  3510. DPRINT2(4, ":DS: DN %ws -> DNS %ws\n", Dn, DnsName);
  3511. return DnsName;
  3512. }
  3513. PWCHAR
  3514. FrsDsGuessPrincName(
  3515. IN PWCHAR Dn
  3516. )
  3517. /*++
  3518. Routine Description:
  3519. Derive the NT4 account name for Dn. Dn should be the Dn
  3520. of a computer object.
  3521. Arguments:
  3522. Dn
  3523. Return Value:
  3524. NT4 Account Name or NULL
  3525. --*/
  3526. {
  3527. #undef DEBSUB
  3528. #define DEBSUB "FrsDsGuessPrincName:"
  3529. DWORD Len = 0;
  3530. WCHAR HackPrincName[MAX_PATH];
  3531. PWCHAR Rdn;
  3532. PWCHAR Dc;
  3533. DPRINT1(4, ":DS: WARN: Guess NT4 Account Name for %ws\n", Dn);
  3534. //
  3535. // Computer's Dn not available
  3536. //
  3537. if (!Dn) {
  3538. return NULL;
  3539. }
  3540. Dc = wcsstr(Dn, L"dc=");
  3541. //
  3542. // No DC=?
  3543. //
  3544. if (!Dc) {
  3545. DPRINT1(4, ":DS: No DC= in %ws\n", Dn);
  3546. return NULL;
  3547. }
  3548. //
  3549. // DC= at eol?
  3550. //
  3551. Dc += 3;
  3552. if (!*Dc) {
  3553. DPRINT1(4, ":DS: No DC= at eol in %ws\n", Dn);
  3554. return NULL;
  3555. }
  3556. while (*Dc && *Dc != L',') {
  3557. HackPrincName[Len++] = *Dc++;
  3558. }
  3559. HackPrincName[Len++] = L'\\';
  3560. HackPrincName[Len++] = L'\0';
  3561. Rdn = FrsDsMakeRdn(Dn);
  3562. wcscat(HackPrincName, Rdn);
  3563. wcscat(HackPrincName, L"$");
  3564. DPRINT1(4, ":DS: Guessing %ws\n", HackPrincName);
  3565. FrsFree(Rdn);
  3566. return FrsWcsDup(HackPrincName);
  3567. }
  3568. PWCHAR
  3569. FrsDsFormUPN(
  3570. IN PWCHAR NT4AccountName,
  3571. IN PWCHAR DomainDnsName
  3572. )
  3573. /*++
  3574. Routine Description:
  3575. Forms the User Principal Name by combining the
  3576. Sam account name and the domain dns name in the form
  3577. shown below.
  3578. <SamAccountName>@<DnsDomainName>
  3579. You can get <SamAccountName> from the string to the right of the "\"
  3580. of the NT4AccountName.
  3581. Arguments:
  3582. NT4AccountName - DS_NT4_ACCOUNT_NAME returned from DsCrackNames.
  3583. DomainDnsName - Dns name of the domain.
  3584. Return Value:
  3585. Copy of name in desired format; free with FrsFree()
  3586. --*/
  3587. {
  3588. #undef DEBSUB
  3589. #define DEBSUB "FrsDsFormUPN:"
  3590. PWCHAR SamBegin = NULL;
  3591. PWCHAR FormedUPN = NULL;
  3592. if ((NT4AccountName == NULL ) || (DomainDnsName == NULL)) {
  3593. return NULL;
  3594. }
  3595. //
  3596. // Find the sam account name.
  3597. //
  3598. for (SamBegin = NT4AccountName; *SamBegin && *SamBegin != L'\\'; ++SamBegin);
  3599. if (*SamBegin && *(SamBegin+1)) {
  3600. SamBegin++;
  3601. } else {
  3602. return NULL;
  3603. }
  3604. FormedUPN = FrsAlloc((wcslen(SamBegin) + wcslen(DomainDnsName) + 2) * sizeof(WCHAR));
  3605. wcscpy(FormedUPN, SamBegin);
  3606. wcscat(FormedUPN, L"@");
  3607. wcscat(FormedUPN, DomainDnsName);
  3608. DPRINT1(5, "UPN formed is %ws\n", FormedUPN);
  3609. return FormedUPN;
  3610. }
  3611. PWCHAR
  3612. FrsDsConvertName(
  3613. IN HANDLE Handle,
  3614. IN PWCHAR InputName,
  3615. IN DWORD InputFormat,
  3616. IN PWCHAR DomainDnsName,
  3617. IN DWORD DesiredFormat
  3618. )
  3619. /*++
  3620. Routine Description:
  3621. Translate the input name into the desired format.
  3622. Arguments:
  3623. Handle - From DsBind
  3624. InputName - Supplied name.
  3625. InputFormat - Format of the supplied name.
  3626. DomainDnsName - If !NULL, produce new local handle
  3627. DesiredFormat - desired format. Eg. DS_USER_PRINCIPAL_NAME
  3628. Return Value:
  3629. Copy of name in desired format; free with FrsFree()
  3630. --*/
  3631. {
  3632. #undef DEBSUB
  3633. #define DEBSUB "FrsDsConvertName:"
  3634. DWORD WStatus;
  3635. DS_NAME_RESULT *Cracked = NULL;
  3636. HANDLE LocalHandle = NULL;
  3637. PWCHAR CrackedName = NULL;
  3638. PWCHAR CrackedDomain = NULL;
  3639. PWCHAR CrackedUPN = NULL;
  3640. DWORD RequestedFormat = 0;
  3641. DPRINT3(4, ":DS: Convert Name %ws From %08x To %08x\n", InputName, InputFormat, DesiredFormat);
  3642. //
  3643. // Input name not available.
  3644. //
  3645. if (!InputName) {
  3646. return NULL;
  3647. }
  3648. //
  3649. // Need something to go on!
  3650. //
  3651. if (!HANDLE_IS_VALID(Handle) && !DomainDnsName) {
  3652. return NULL;
  3653. }
  3654. //
  3655. // Bind to Ds
  3656. //
  3657. if (DomainDnsName) {
  3658. DPRINT3(4, ":DS: Get %08x Name from %ws for %ws\n",
  3659. DesiredFormat, DomainDnsName, InputName);
  3660. WStatus = DsBind(NULL, DomainDnsName, &LocalHandle);
  3661. CLEANUP2_WS(0, ":DS: ERROR - DsBind(%ws, %08x);",
  3662. DomainDnsName, DesiredFormat, WStatus, RETURN);
  3663. Handle = LocalHandle;
  3664. }
  3665. //
  3666. // Crack the computer's distinguished name into its NT4 Account Name
  3667. //
  3668. // If the Desired format is DS_USER_PRINCIPAL_NAME then we form it by
  3669. // getting the name from DS_NT4_ACCOUNT_NAME and the dns domain name
  3670. // from the "Cracked->rItems->pDomain"
  3671. // We could ask for DS_USER_PRINCIPAL_NAME directly but we don't because.
  3672. // Object can have implicit or explicit UPNs. If the object has an explicit UPN,
  3673. // the DsCrackNames will work. If the object has an implicit UPN,
  3674. // then you need to build it.
  3675. //
  3676. if (DesiredFormat == DS_USER_PRINCIPAL_NAME) {
  3677. RequestedFormat = DS_NT4_ACCOUNT_NAME;
  3678. } else {
  3679. RequestedFormat = DesiredFormat;
  3680. }
  3681. WStatus = DsCrackNames(Handle, // in hDS,
  3682. DS_NAME_NO_FLAGS, // in flags,
  3683. InputFormat , // in formatOffered,
  3684. RequestedFormat, // in formatDesired,
  3685. 1, // in cNames,
  3686. &InputName, // in *rpNames,
  3687. &Cracked); // out *ppResult
  3688. if (!WIN_SUCCESS(WStatus)) {
  3689. DPRINT2_WS(0, ":DS: ERROR - DsCrackNames(%ws, %08x);", InputName, DesiredFormat, WStatus);
  3690. //
  3691. // Set DsBindingsAreValid to FALSE if the handle has become invalid.
  3692. // That will force us to rebind at the next poll. The guess below might still
  3693. // work so continue processing.
  3694. //
  3695. if (WStatus == ERROR_INVALID_HANDLE) {
  3696. DPRINT1(4, ":DS: Marking binding to %ws as invalid.\n",
  3697. (DsDomainControllerName) ? DsDomainControllerName : L"<null>");
  3698. DsBindingsAreValid = FALSE;
  3699. }
  3700. //
  3701. // What else can we do?
  3702. //
  3703. if (HANDLE_IS_VALID(LocalHandle)) {
  3704. DsUnBind(&LocalHandle);
  3705. LocalHandle = NULL;
  3706. }
  3707. if (DesiredFormat == DS_NT4_ACCOUNT_NAME) {
  3708. return FrsDsGuessPrincName(InputName);
  3709. } else {
  3710. return NULL;
  3711. }
  3712. }
  3713. //
  3714. // Might have it
  3715. //
  3716. if (Cracked && Cracked->cItems && Cracked->rItems) {
  3717. //
  3718. // Got it!
  3719. //
  3720. if (Cracked->rItems->status == DS_NAME_NO_ERROR) {
  3721. DPRINT1(4, ":DS: Cracked Domain : %ws\n", Cracked->rItems->pDomain);
  3722. DPRINT2(4, ":DS: Cracked Name : %08x %ws\n",
  3723. DesiredFormat, Cracked->rItems->pName);
  3724. CrackedDomain = FrsWcsDup(Cracked->rItems->pDomain);
  3725. CrackedName = FrsWcsDup(Cracked->rItems->pName);
  3726. //
  3727. // Only got the domain; rebind and try again
  3728. //
  3729. } else
  3730. if (Cracked->rItems->status == DS_NAME_ERROR_DOMAIN_ONLY) {
  3731. CrackedName = FrsDsConvertName(NULL, InputName, InputFormat, Cracked->rItems->pDomain, DesiredFormat);
  3732. } else {
  3733. DPRINT3(0, ":DS: ERROR - DsCrackNames(%ws, %08x); internal status %d\n",
  3734. InputName, DesiredFormat, Cracked->rItems->status);
  3735. if (DesiredFormat == DS_NT4_ACCOUNT_NAME) {
  3736. CrackedName = FrsDsGuessPrincName(InputName);
  3737. }
  3738. }
  3739. } else {
  3740. DPRINT2(0, ":DS: ERROR - DsCrackNames(%ws, %08x); no status\n",
  3741. InputName, DesiredFormat);
  3742. if (DesiredFormat == DS_NT4_ACCOUNT_NAME) {
  3743. CrackedName = FrsDsGuessPrincName(InputName);
  3744. }
  3745. }
  3746. if (Cracked) {
  3747. DsFreeNameResult(Cracked);
  3748. Cracked = NULL;
  3749. }
  3750. if (HANDLE_IS_VALID(LocalHandle)) {
  3751. DsUnBind(&LocalHandle);
  3752. LocalHandle = NULL;
  3753. }
  3754. RETURN:
  3755. if ((DesiredFormat == DS_USER_PRINCIPAL_NAME) && (CrackedName != NULL) && (CrackedDomain != NULL)) {
  3756. CrackedUPN = FrsDsFormUPN(CrackedName, CrackedDomain);
  3757. FrsFree(CrackedName);
  3758. FrsFree(CrackedDomain);
  3759. return CrackedUPN;
  3760. } else {
  3761. FrsFree(CrackedDomain);
  3762. return CrackedName;
  3763. }
  3764. }
  3765. PWCHAR
  3766. FrsDsGetName(
  3767. IN PWCHAR Dn,
  3768. IN HANDLE Handle,
  3769. IN PWCHAR DomainDnsName,
  3770. IN DWORD DesiredFormat
  3771. )
  3772. /*++
  3773. Routine Description:
  3774. Translate the Dn into the desired format. Dn should be the Dn
  3775. of a computer object.
  3776. Arguments:
  3777. Dn - Of computer object
  3778. Handle - From DsBind
  3779. DomainDnsName - If !NULL, produce new local handle
  3780. DesiredFormat - DS_NT4_ACCOUNT_NAME or DS_STRING_SID_NAME
  3781. Return Value:
  3782. Copy of name in desired format; free with FrsFree()
  3783. --*/
  3784. {
  3785. #undef DEBSUB
  3786. #define DEBSUB "FrsDsGetName:"
  3787. DWORD WStatus;
  3788. DS_NAME_RESULT *Cracked = NULL;
  3789. HANDLE LocalHandle = NULL;
  3790. PWCHAR CrackedName = NULL;
  3791. PWCHAR CrackedDomain = NULL;
  3792. PWCHAR CrackedUPN = NULL;
  3793. DWORD RequestedFormat = 0;
  3794. DPRINT2(4, ":DS: Get %08x Name for %ws\n", DesiredFormat, Dn);
  3795. //
  3796. // Computer's Dn not available
  3797. //
  3798. if (!Dn) {
  3799. return NULL;
  3800. }
  3801. //
  3802. // Need something to go on!
  3803. //
  3804. if (!HANDLE_IS_VALID(Handle) && !DomainDnsName) {
  3805. return NULL;
  3806. }
  3807. //
  3808. // Bind to Ds
  3809. //
  3810. if (DomainDnsName) {
  3811. DPRINT3(4, ":DS: Get %08x Name from %ws for %ws\n",
  3812. DesiredFormat, DomainDnsName, Dn);
  3813. WStatus = DsBind(NULL, DomainDnsName, &LocalHandle);
  3814. CLEANUP2_WS(0, ":DS: ERROR - DsBind(%ws, %08x);",
  3815. DomainDnsName, DesiredFormat, WStatus, RETURN);
  3816. Handle = LocalHandle;
  3817. }
  3818. //
  3819. // Crack the computer's distinguished name into its NT4 Account Name
  3820. //
  3821. // If the Desired format is DS_USER_PRINCIPAL_NAME then we form it by
  3822. // getting the name from DS_NT4_ACCOUNT_NAME and the dns domain name
  3823. // from the "Cracked->rItems->pDomain"
  3824. // We could ask for DS_USER_PRINCIPAL_NAME directly but we don't because.
  3825. // Object can have implicit or explicit UPNs. If the object has an explicit UPN,
  3826. // the DsCrackNames will work. If the object has an implicit UPN,
  3827. // then you need to build it.
  3828. //
  3829. if (DesiredFormat == DS_USER_PRINCIPAL_NAME) {
  3830. RequestedFormat = DS_NT4_ACCOUNT_NAME;
  3831. } else {
  3832. RequestedFormat = DesiredFormat;
  3833. }
  3834. WStatus = DsCrackNames(Handle, // in hDS,
  3835. DS_NAME_NO_FLAGS, // in flags,
  3836. DS_FQDN_1779_NAME, // in formatOffered,
  3837. RequestedFormat, // in formatDesired,
  3838. 1, // in cNames,
  3839. &Dn, // in *rpNames,
  3840. &Cracked); // out *ppResult
  3841. if (!WIN_SUCCESS(WStatus)) {
  3842. DPRINT2_WS(0, ":DS: ERROR - DsCrackNames(%ws, %08x);", Dn, DesiredFormat, WStatus);
  3843. //
  3844. // Set DsBindingsAreValid to FALSE if the handle has become invalid.
  3845. // That will force us to rebind at the next poll. The guess below might still
  3846. // work so continue processing.
  3847. //
  3848. if (WStatus == ERROR_INVALID_HANDLE) {
  3849. DPRINT1(4, ":DS: Marking binding to %ws as invalid.\n",
  3850. (DsDomainControllerName) ? DsDomainControllerName : L"<null>");
  3851. DsBindingsAreValid = FALSE;
  3852. }
  3853. //
  3854. // What else can we do?
  3855. //
  3856. if (HANDLE_IS_VALID(LocalHandle)) {
  3857. DsUnBind(&LocalHandle);
  3858. LocalHandle = NULL;
  3859. }
  3860. if (DesiredFormat == DS_NT4_ACCOUNT_NAME) {
  3861. return FrsDsGuessPrincName(Dn);
  3862. } else {
  3863. return NULL;
  3864. }
  3865. }
  3866. //
  3867. // Might have it
  3868. //
  3869. if (Cracked && Cracked->cItems && Cracked->rItems) {
  3870. //
  3871. // Got it!
  3872. //
  3873. if (Cracked->rItems->status == DS_NAME_NO_ERROR) {
  3874. DPRINT1(4, ":DS: Cracked Domain : %ws\n", Cracked->rItems->pDomain);
  3875. DPRINT2(4, ":DS: Cracked Name : %08x %ws\n",
  3876. DesiredFormat, Cracked->rItems->pName);
  3877. CrackedDomain = FrsWcsDup(Cracked->rItems->pDomain);
  3878. CrackedName = FrsWcsDup(Cracked->rItems->pName);
  3879. //
  3880. // Only got the domain; rebind and try again
  3881. //
  3882. } else
  3883. if (Cracked->rItems->status == DS_NAME_ERROR_DOMAIN_ONLY) {
  3884. CrackedName = FrsDsGetName(Dn, NULL, Cracked->rItems->pDomain, DesiredFormat);
  3885. } else {
  3886. DPRINT3(0, ":DS: ERROR - DsCrackNames(%ws, %08x); internal status %d\n",
  3887. Dn, DesiredFormat, Cracked->rItems->status);
  3888. if (DesiredFormat == DS_NT4_ACCOUNT_NAME) {
  3889. CrackedName = FrsDsGuessPrincName(Dn);
  3890. }
  3891. }
  3892. } else {
  3893. DPRINT2(0, ":DS: ERROR - DsCrackNames(%ws, %08x); no status\n",
  3894. Dn, DesiredFormat);
  3895. if (DesiredFormat == DS_NT4_ACCOUNT_NAME) {
  3896. CrackedName = FrsDsGuessPrincName(Dn);
  3897. }
  3898. }
  3899. if (Cracked) {
  3900. DsFreeNameResult(Cracked);
  3901. Cracked = NULL;
  3902. }
  3903. if (HANDLE_IS_VALID(LocalHandle)) {
  3904. DsUnBind(&LocalHandle);
  3905. LocalHandle = NULL;
  3906. }
  3907. RETURN:
  3908. if ((DesiredFormat == DS_USER_PRINCIPAL_NAME) && (CrackedName != NULL) && (CrackedDomain != NULL)) {
  3909. CrackedUPN = FrsDsFormUPN(CrackedName, CrackedDomain);
  3910. FrsFree(CrackedName);
  3911. FrsFree(CrackedDomain);
  3912. return CrackedUPN;
  3913. } else {
  3914. FrsFree(CrackedDomain);
  3915. return CrackedName;
  3916. }
  3917. }
  3918. VOID
  3919. FrsDsCreatePartnerPrincName(
  3920. IN PCONFIG_NODE Sites
  3921. )
  3922. /*++
  3923. Routine Description:
  3924. Construct the server principal names for our partners.
  3925. Part of NewDs poll APIs.
  3926. Arguments:
  3927. Sites
  3928. Return Value:
  3929. None.
  3930. --*/
  3931. {
  3932. #undef DEBSUB
  3933. #define DEBSUB "FrsDsCreatePartnerPrincName:"
  3934. PCONFIG_NODE Cxtion;
  3935. PCONFIG_NODE Partner;
  3936. PCONFIG_NODE Site;
  3937. PCONFIG_NODE Settings;
  3938. PCONFIG_NODE Set;
  3939. PCONFIG_NODE Server;
  3940. PVOID Key;
  3941. //
  3942. // Get all the required information for every computer in the PartnerComputerTable.
  3943. //
  3944. Key = NULL;
  3945. while ((Partner = GTabNextDatum(PartnerComputerTable, &Key)) != NULL) {
  3946. //
  3947. // Get the Server Principal Name.
  3948. //
  3949. if ((Partner->PrincName == NULL) ||
  3950. (*Partner->PrincName == UNICODE_NULL)) {
  3951. Partner->PrincName = FrsDsGetName(Partner->Dn, DsHandle, NULL, DS_NT4_ACCOUNT_NAME);
  3952. if ((Partner->PrincName == NULL) ||
  3953. (*Partner->PrincName == UNICODE_NULL)) {
  3954. //
  3955. // Setting active change to 0 will cause this code to be
  3956. // repeated at the next ds polling cycle. We do this because
  3957. // the partner's principal name may appear later.
  3958. //
  3959. ActiveChange = 0;
  3960. Partner->Consistent = FALSE;
  3961. continue;
  3962. }
  3963. }
  3964. //
  3965. // Get the partners dnsHostName.
  3966. //
  3967. if (!Partner->DnsName) {
  3968. Partner->DnsName = FrsDsGetDnsName(gLdap, Partner->Dn);
  3969. }
  3970. //
  3971. // Get the partners SID.
  3972. //
  3973. if (!Partner->Sid) {
  3974. Partner->Sid = FrsDsGetName(Partner->Dn, DsHandle, NULL, DS_STRING_SID_NAME);
  3975. }
  3976. }
  3977. //
  3978. // For every cxtion in every replica set.
  3979. //
  3980. Key = NULL;
  3981. while((Cxtion = GTabNextDatum(AllCxtionsTable, &Key)) != NULL) {
  3982. //
  3983. // Ignore inconsistent cxtions
  3984. //
  3985. if (!Cxtion->Consistent) {
  3986. continue;
  3987. }
  3988. //
  3989. // Look for the Cxtion's partner using the PartnerCoDn.
  3990. //
  3991. //
  3992. // Mark this connection inconsistent if it lacks a PartnerCoDn.
  3993. //
  3994. if (Cxtion->PartnerCoDn == NULL) {
  3995. Cxtion->Consistent = FALSE;
  3996. continue;
  3997. }
  3998. Partner = GTabLookupTableString(PartnerComputerTable, Cxtion->PartnerCoDn, NULL);
  3999. //
  4000. // Inconsistent partner; continue
  4001. //
  4002. if (Partner == NULL || !Partner->Consistent) {
  4003. Cxtion->Consistent = FALSE;
  4004. continue;
  4005. }
  4006. //
  4007. // Get out partner's server principal name
  4008. //
  4009. if (!Cxtion->PrincName) {
  4010. Cxtion->PrincName = FrsWcsDup(Partner->PrincName);
  4011. }
  4012. //
  4013. // Get our partner's dns name
  4014. //
  4015. if (!Cxtion->PartnerDnsName) {
  4016. //
  4017. // The partner's DNS name is not critical; we can fall
  4018. // back on our partner's NetBios name.
  4019. //
  4020. if (Partner->DnsName) {
  4021. Cxtion->PartnerDnsName = FrsWcsDup(Partner->DnsName);
  4022. }
  4023. }
  4024. //
  4025. // Get our partner's Sid
  4026. //
  4027. if (!Cxtion->PartnerSid) {
  4028. //
  4029. // The partner's DNS name is not critical; we can fall
  4030. // back on our partner's NetBios name.
  4031. //
  4032. if (Partner->Sid) {
  4033. Cxtion->PartnerSid = FrsWcsDup(Partner->Sid);
  4034. }
  4035. }
  4036. } // cxtion scan
  4037. }
  4038. ULONG
  4039. FrsHashCalcString (
  4040. PVOID Buffer,
  4041. PULONGLONG QKey
  4042. )
  4043. {
  4044. #undef DEBSUB
  4045. #define DEBSUB "FrsHashCalcString:"
  4046. PWCHAR Name = (PWCHAR) Buffer;
  4047. ULONG NameLength = 0;
  4048. PWCHAR p = NULL;
  4049. ULONG NameHashUL = 0;
  4050. ULONGLONG NameHashULL = QUADZERO;
  4051. ULONG Shift = 0;
  4052. FRS_ASSERT( Buffer != NULL );
  4053. FRS_ASSERT( QKey != NULL );
  4054. NameLength = wcslen(Name);
  4055. FRS_ASSERT( NameLength != 0 );
  4056. DPRINT1(0, "Name = %ws\n", Name);
  4057. //
  4058. // Combine each unicode character into the hash value, shifting 4 bits
  4059. // each time. Start at the end of the name so file names with different
  4060. // type codes will hash to different table offsets.
  4061. //
  4062. for( p = &Name[NameLength-1];
  4063. p >= Name;
  4064. p-- ) {
  4065. NameHashUL = NameHashUL ^ (((ULONG)towupper(*p)) << Shift);
  4066. NameHashULL = NameHashULL ^ (((ULONGLONG)towupper(*p)) << Shift);
  4067. Shift = (Shift < 16) ? Shift + 4 : 0;
  4068. }
  4069. *QKey = NameHashULL;
  4070. return NameHashUL;
  4071. }
  4072. ULONG
  4073. PrintPartnerTableEntry (
  4074. PQHASH_TABLE Table,
  4075. PQHASH_ENTRY BeforeNode,
  4076. PQHASH_ENTRY TargetNode,
  4077. PVOID Context
  4078. )
  4079. {
  4080. #undef DEBSUB
  4081. #define DEBSUB "PrintPartnerTableEntry: "
  4082. DPRINT1(0, "PartnerTableEntry: %ws\n", (PWCHAR)(TargetNode->Flags));
  4083. return FrsErrorSuccess;
  4084. }
  4085. BOOL
  4086. StringKeyMatch(
  4087. PVOID Buf,
  4088. PVOID QKey
  4089. )
  4090. {
  4091. #undef DEBSUB
  4092. #define DEBSUB "StringKeyMatch: "
  4093. PWCHAR String1 = (PWCHAR)Buf;
  4094. PWCHAR String2 = (PWCHAR)QKey;
  4095. return(_wcsicmp(String1, String2)?FALSE:TRUE);
  4096. }
  4097. VOID
  4098. FrsDsCreateNewValidPartnerTableStruct(
  4099. VOID
  4100. )
  4101. /*++
  4102. Routine Description:
  4103. Uses the AllCxtionsTable to build a new FRS_VALID_PARTNER_TABLE_STRUCT.
  4104. Swaps the new struct with the current one pointed to by the global var
  4105. pValidPartnerTableStruct.
  4106. The old struct gets put on the OldValidPartnerTableStructListHead list.
  4107. Items on the list are cleaned up by calling
  4108. FrsDsCleanupOldValidPartnerTableStructList.
  4109. Arguments
  4110. NONE
  4111. Return Value:
  4112. NONE
  4113. --*/
  4114. {
  4115. #undef DEBSUB
  4116. #define DEBSUB "FrsDsCreateNewValidPartnerTableStruct:"
  4117. PCXTION Cxtion = NULL;
  4118. PVOID Key = NULL;
  4119. PQHASH_TABLE pNewPartnerTable = NULL;
  4120. PQHASH_TABLE pNewPartnerConnectionTable = NULL;
  4121. PWCHAR PartnerSid = NULL;
  4122. PGNAME pGName = NULL;
  4123. GUID *pCxtionGuid = NULL;
  4124. PWCHAR NameEntry = NULL;
  4125. PFRS_VALID_PARTNER_TABLE_STRUCT pNewValidPartnerTableStruct = NULL;
  4126. PFRS_VALID_PARTNER_TABLE_STRUCT pOldValidPartnerTableStruct = NULL;
  4127. GHT_STATUS Status;
  4128. CHAR GuidStr[GUID_CHAR_LEN];
  4129. pNewPartnerTable = FrsAllocTypeSize(QHASH_TABLE_TYPE, PARTNER_NAME_TABLE_SIZE);
  4130. SET_QHASH_TABLE_FLAG(pNewPartnerTable, QHASH_FLAG_LARGE_KEY);
  4131. SET_QHASH_TABLE_HASH_CALC2(pNewPartnerTable, FrsHashCalcString);
  4132. SET_QHASH_TABLE_KEY_MATCH(pNewPartnerTable, StringKeyMatch);
  4133. SET_QHASH_TABLE_FREE(pNewPartnerTable, FrsFree);
  4134. pNewPartnerConnectionTable = FrsAllocTypeSize(QHASH_TABLE_TYPE, PARTNER_CONNECTION_TABLE_SIZE);
  4135. SET_QHASH_TABLE_FLAG(pNewPartnerConnectionTable, QHASH_FLAG_LARGE_KEY);
  4136. SET_QHASH_TABLE_HASH_CALC2(pNewPartnerConnectionTable, ActiveChildrenHashCalc);
  4137. SET_QHASH_TABLE_KEY_MATCH(pNewPartnerConnectionTable, ActiveChildrenKeyMatch);
  4138. SET_QHASH_TABLE_FREE(pNewPartnerConnectionTable, FrsFree);
  4139. //
  4140. // Check each active replica
  4141. //
  4142. ForEachListEntry( &ReplicaListHead, REPLICA, ReplicaList,
  4143. // Loop iterator pE is type PREPLICA.
  4144. //
  4145. // Need to lock Cxtion Table for enumeration.
  4146. // Don't need to hold replica lock - that only protects the filter list
  4147. //
  4148. LOCK_CXTION_TABLE(pE);
  4149. Key = NULL;
  4150. while((Cxtion = GTabNextDatumNoLock(pE->Cxtions, &Key)) != NULL) {
  4151. //
  4152. // Ignore the (local) journal connection.
  4153. //
  4154. if (Cxtion->JrnlCxtion) {
  4155. continue;
  4156. }
  4157. //
  4158. // Create an entry using partner's sid
  4159. //
  4160. PartnerSid = FrsWcsDup(Cxtion->PartnerSid);
  4161. QHashInsertLock(pNewPartnerTable,
  4162. PartnerSid,
  4163. NULL,
  4164. (ULONG_PTR)PartnerSid);
  4165. PartnerSid = FrsWcsDup(Cxtion->PartnerSid);
  4166. pCxtionGuid = FrsDupGuid(Cxtion->Name->Guid);
  4167. QHashInsertLock(pNewPartnerConnectionTable,
  4168. pCxtionGuid,
  4169. (PULONGLONG)&PartnerSid,
  4170. (ULONG_PTR)pCxtionGuid );
  4171. } // cxtion scan
  4172. UNLOCK_CXTION_TABLE(pE);
  4173. );
  4174. //
  4175. // also need to check replicas in error states
  4176. //
  4177. ForEachListEntry( &ReplicaFaultListHead, REPLICA, ReplicaList,
  4178. // Loop iterator pE is type PREPLICA.
  4179. //
  4180. // Need to lock Cxtion Table for enumeration.
  4181. // Don't need to hold replica lock - that only protects the filter list
  4182. //
  4183. LOCK_CXTION_TABLE(pE);
  4184. Key = NULL;
  4185. while((Cxtion = GTabNextDatumNoLock(pE->Cxtions, &Key)) != NULL) {
  4186. //
  4187. // Ignore the (local) journal connection.
  4188. //
  4189. if (Cxtion->JrnlCxtion) {
  4190. continue;
  4191. }
  4192. //
  4193. // Create an entry using partner's sid
  4194. //
  4195. PartnerSid = FrsWcsDup(Cxtion->PartnerSid);
  4196. QHashInsertLock(pNewPartnerTable,
  4197. PartnerSid,
  4198. NULL,
  4199. (ULONG_PTR)PartnerSid);
  4200. PartnerSid = FrsWcsDup(Cxtion->PartnerSid);
  4201. pCxtionGuid = FrsDupGuid(Cxtion->Name->Guid);
  4202. QHashInsertLock(pNewPartnerConnectionTable,
  4203. pCxtionGuid,
  4204. (PULONGLONG)&PartnerSid,
  4205. (ULONG_PTR)pCxtionGuid);
  4206. } // cxtion scan
  4207. UNLOCK_CXTION_TABLE(pE);
  4208. );
  4209. //
  4210. // Don't use stopped replicas. They have probably been deleted.
  4211. //
  4212. pNewValidPartnerTableStruct = FrsAlloc(sizeof(FRS_VALID_PARTNER_TABLE_STRUCT));
  4213. pNewValidPartnerTableStruct->pPartnerConnectionTable = pNewPartnerConnectionTable;
  4214. pNewValidPartnerTableStruct->pPartnerTable = pNewPartnerTable;
  4215. pNewValidPartnerTableStruct->ReferenceCount = 0;
  4216. pNewValidPartnerTableStruct->Next = NULL;
  4217. SWAP_VALID_PARTNER_TABLE_POINTER(pNewValidPartnerTableStruct,
  4218. &pOldValidPartnerTableStruct);
  4219. if (pOldValidPartnerTableStruct) {
  4220. EnterCriticalSection(&OldValidPartnerTableStructListHeadLock);
  4221. pOldValidPartnerTableStruct->Next = OldValidPartnerTableStructListHead;
  4222. OldValidPartnerTableStructListHead = pOldValidPartnerTableStruct;
  4223. LeaveCriticalSection(&OldValidPartnerTableStructListHeadLock);
  4224. }
  4225. }
  4226. VOID
  4227. FrsDsCleanupOldValidPartnerTableStructList(
  4228. VOID
  4229. )
  4230. /*++
  4231. Routine Description:
  4232. Cleanup items on the OldValidPartnerTableStructListHead list.
  4233. Items on the list are freed only if their reference count is at zero.
  4234. Arguments
  4235. NONE
  4236. Return Value:
  4237. NONE
  4238. --*/
  4239. {
  4240. PFRS_VALID_PARTNER_TABLE_STRUCT pListItem = NULL;
  4241. PFRS_VALID_PARTNER_TABLE_STRUCT pPreviousItem = NULL;
  4242. PFRS_VALID_PARTNER_TABLE_STRUCT pNextItem = NULL;
  4243. EnterCriticalSection(&OldValidPartnerTableStructListHeadLock);
  4244. pListItem = OldValidPartnerTableStructListHead;
  4245. while (pListItem != NULL) {
  4246. pNextItem = pListItem->Next;
  4247. if (pListItem->ReferenceCount == 0) {
  4248. // remove from list
  4249. if (pPreviousItem != NULL) {
  4250. pPreviousItem->Next = pNextItem;
  4251. } else {
  4252. OldValidPartnerTableStructListHead = pNextItem;
  4253. }
  4254. // cleanup
  4255. FREE_VALID_PARTNER_TABLE_STRUCT(pListItem);
  4256. } else {
  4257. pPreviousItem = pListItem;
  4258. }
  4259. pListItem = pNextItem;
  4260. }
  4261. LeaveCriticalSection(&OldValidPartnerTableStructListHeadLock);
  4262. }
  4263. BOOL
  4264. FrsDsDoesUserWantReplication(
  4265. IN PCONFIG_NODE Computer
  4266. )
  4267. /*++
  4268. Routine Description:
  4269. Does the topology imply that the user wants this server to replicate?
  4270. Part of NewDs poll APIs.
  4271. Arguments
  4272. Computer
  4273. Return Value:
  4274. TRUE - server may be replicating
  4275. FALSE - server is not replicating
  4276. --*/
  4277. {
  4278. #undef DEBSUB
  4279. #define DEBSUB "FrsDsDoesUserWantReplication:"
  4280. DWORD WStatus;
  4281. PCONFIG_NODE Subscriptions;
  4282. PCONFIG_NODE Subscriber;
  4283. //
  4284. // Ds polling thread is shutting down
  4285. //
  4286. if (DsIsShuttingDown) {
  4287. DPRINT(0, ":DS: Ds polling thread is shutting down\n");
  4288. return FALSE;
  4289. }
  4290. //
  4291. // Can't find our computer; something is wrong. Don't start
  4292. //
  4293. if (!Computer) {
  4294. DPRINT(0, ":DS: no computer\n");
  4295. return FALSE;
  4296. } else {
  4297. DPRINT(4, ":DS: have a computer\n");
  4298. }
  4299. //
  4300. // We need to process the topology further if there is at least
  4301. // 1 valid subscriber.
  4302. //
  4303. if (SubscriberTable != NULL) {
  4304. return TRUE;
  4305. }
  4306. //
  4307. // Database exists; once was a member of a replica set
  4308. //
  4309. WStatus = FrsDoesFileExist(JetFile);
  4310. if (WIN_SUCCESS(WStatus)) {
  4311. DPRINT(4, ":DS: database exists\n");
  4312. return TRUE;
  4313. } else {
  4314. DPRINT(4, ":DS: database does not exists\n");
  4315. }
  4316. DPRINT1(4, ":DS: Not starting on %ws; nothing to do\n", ComputerName);
  4317. return FALSE;
  4318. }
  4319. BOOL
  4320. FrsDsVerifyPath(
  4321. IN PWCHAR Path
  4322. )
  4323. /*++
  4324. Routine Description:
  4325. Verify the path syntax.
  4326. Arguments:
  4327. Path - Syntax is *<Drive Letter>:\*
  4328. Return Value:
  4329. None.
  4330. --*/
  4331. {
  4332. #undef DEBSUB
  4333. #define DEBSUB "FrsDsVerifyPath:"
  4334. PWCHAR Colon;
  4335. //
  4336. // Null path is obviously invalid
  4337. //
  4338. if (!Path) {
  4339. return FALSE;
  4340. }
  4341. //
  4342. // Find the :
  4343. //
  4344. for (Colon = Path; (*Colon != L':') && *Colon; ++Colon);
  4345. //
  4346. // No :
  4347. //
  4348. if (!*Colon) {
  4349. return FALSE;
  4350. }
  4351. //
  4352. // No drive letter
  4353. //
  4354. if (Colon == Path) {
  4355. return FALSE;
  4356. }
  4357. //
  4358. // No :\
  4359. //
  4360. if (*(Colon + 1) != L'\\') {
  4361. return FALSE;
  4362. }
  4363. //
  4364. // Path exists and is valid
  4365. //
  4366. return TRUE;
  4367. }
  4368. VOID
  4369. FrsDsCheckServerPaths(
  4370. IN PCONFIG_NODE Sites
  4371. )
  4372. /*++
  4373. Routine Description:
  4374. Look for nested paths and invalid path syntax.
  4375. Correct syntax is "*<drive letter>:\*".
  4376. Arguments:
  4377. Sites
  4378. Return Value:
  4379. None.
  4380. --*/
  4381. {
  4382. #undef DEBSUB
  4383. #define DEBSUB "FrsDsCheckServerPaths:"
  4384. DWORD WStatus;
  4385. PCONFIG_NODE Site;
  4386. PCONFIG_NODE Settings;
  4387. PCONFIG_NODE Set;
  4388. PCONFIG_NODE Server;
  4389. PCONFIG_NODE NSite;
  4390. PCONFIG_NODE NSettings;
  4391. PCONFIG_NODE NSet;
  4392. PCONFIG_NODE NServer;
  4393. DWORD FileAttributes = 0xFFFFFFFF;
  4394. for (Site = Sites; Site; Site = Site->Peer) {
  4395. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  4396. for (Set = Settings->Children; Set; Set = Set->Peer) {
  4397. for (Server = Set->Children; Server; Server = Server->Peer) {
  4398. //
  4399. // Not this computer; continue
  4400. //
  4401. if (!Server->ThisComputer) {
  4402. continue;
  4403. }
  4404. //
  4405. // Mark this server as processed. This forces the inner loop
  4406. // to skip this server node so that we don't end up comparing
  4407. // this node against itself. Also, this forces this node
  4408. // to be skipped in the inner loop to avoid unnecessary checks.
  4409. //
  4410. // In other words, set this field here, not later in the loop
  4411. // or in any other function.
  4412. //
  4413. Server->VerifiedOverlap = TRUE;
  4414. //
  4415. // Server is very inconsistent, ignore
  4416. //
  4417. if (!Server->Root || !Server->Stage) {
  4418. Server->Consistent = FALSE;
  4419. continue;
  4420. }
  4421. //
  4422. // Syntax of root path is invalid; continue
  4423. //
  4424. if (!FrsDsVerifyPath(Server->Root)) {
  4425. DPRINT2(3, ":DS: Invalid root %ws for %ws\n",
  4426. Server->Root, Set->Name->Name);
  4427. EPRINT1(EVENT_FRS_ROOT_NOT_VALID, Server->Root);
  4428. Server->Consistent = FALSE;
  4429. continue;
  4430. }
  4431. //
  4432. // Root does not exist or is inaccessable; continue
  4433. //
  4434. WStatus = FrsDoesDirectoryExist(Server->Root, &FileAttributes);
  4435. if (!WIN_SUCCESS(WStatus)) {
  4436. DPRINT2_WS(3, ":DS: Root path (%ws) for %ws does not exist;",
  4437. Server->Root, Set->Name->Name, WStatus);
  4438. EPRINT1(EVENT_FRS_ROOT_NOT_VALID, Server->Root);
  4439. Server->Consistent = FALSE;
  4440. continue;
  4441. }
  4442. //
  4443. // Does the volume exist and is it NTFS?
  4444. //
  4445. WStatus = FrsVerifyVolume(Server->Root,
  4446. Set->Name->Name,
  4447. FILE_PERSISTENT_ACLS | FILE_SUPPORTS_OBJECT_IDS);
  4448. if (!WIN_SUCCESS(WStatus)) {
  4449. DPRINT2_WS(3, ":DS: Root path Volume (%ws) for %ws does not exist or"
  4450. " does not support ACLs and Object IDs;",
  4451. Server->Root, Set->Name->Name, WStatus);
  4452. Server->Consistent = FALSE;
  4453. continue;
  4454. }
  4455. //
  4456. // Syntax of staging path is invalid; continue
  4457. //
  4458. if (!FrsDsVerifyPath(Server->Stage)) {
  4459. DPRINT2(3, ":DS: Invalid stage %ws for %ws\n",
  4460. Server->Stage, Set->Name->Name);
  4461. EPRINT2(EVENT_FRS_STAGE_NOT_VALID, Server->Root, Server->Stage);
  4462. Server->Consistent = FALSE;
  4463. continue;
  4464. }
  4465. //
  4466. // Stage does not exist or is inaccessable; continue
  4467. //
  4468. WStatus = FrsDoesDirectoryExist(Server->Stage, &FileAttributes);
  4469. if (!WIN_SUCCESS(WStatus)) {
  4470. DPRINT2_WS(3, ":DS: Stage path (%ws) for %ws does not exist;",
  4471. Server->Stage, Set->Name->Name, WStatus);
  4472. EPRINT2(EVENT_FRS_STAGE_NOT_VALID, Server->Root, Server->Stage);
  4473. Server->Consistent = FALSE;
  4474. continue;
  4475. }
  4476. //
  4477. // Does the staging volume exist and does it support ACLs?
  4478. // ACLs are required to protect against data theft/corruption
  4479. // in the staging dir.
  4480. //
  4481. WStatus = FrsVerifyVolume(Server->Stage,
  4482. Set->Name->Name,
  4483. FILE_PERSISTENT_ACLS);
  4484. if (!WIN_SUCCESS(WStatus)) {
  4485. DPRINT2_WS(3, ":DS: Stage path Volume (%ws) for %ws does not exist or does not support ACLs;",
  4486. Server->Stage, Set->Name->Name, WStatus);
  4487. Server->Consistent = FALSE;
  4488. continue;
  4489. }
  4490. //
  4491. // End of outer loop
  4492. //
  4493. } } } }
  4494. }
  4495. DWORD
  4496. FrsDsStartPromotionSeeding(
  4497. IN BOOL Inbound,
  4498. IN PWCHAR ReplicaSetName,
  4499. IN PWCHAR ReplicaSetType,
  4500. IN PWCHAR CxtionName,
  4501. IN PWCHAR PartnerName,
  4502. IN PWCHAR PartnerPrincName,
  4503. IN PWCHAR PartnerSid,
  4504. IN ULONG PartnerAuthLevel,
  4505. IN ULONG GuidSize,
  4506. IN UCHAR *CxtionGuid,
  4507. IN UCHAR *PartnerGuid,
  4508. OUT UCHAR *ParentGuid
  4509. )
  4510. /*++
  4511. Routine Description:
  4512. Start the promotion process by seeding the indicated sysvol.
  4513. Arguments:
  4514. Inbound - Inbound cxtion?
  4515. ReplicaSetName - Replica set name
  4516. ReplicaSetType - Replica set type
  4517. CxtionName - printable name for cxtion
  4518. PartnerName - RPC bindable name
  4519. PartnerPrincName - Server principal name for kerberos
  4520. PartnerAuthLevel - Authentication type and level
  4521. GuidSize - sizeof array addressed by Guid
  4522. CxtionGuid - temporary: used for volatile cxtion
  4523. PartnerGuid - temporary: used to find set on partner
  4524. ParentGuid - Used as partner guid on inbound cxtion
  4525. Return Value:
  4526. Win32 Status
  4527. --*/
  4528. {
  4529. #undef DEBSUB
  4530. #define DEBSUB "FrsDsStartPromotionSeeding:"
  4531. DWORD WStatus;
  4532. PREPLICA DbReplica;
  4533. PCXTION Cxtion = NULL;
  4534. //
  4535. // The caller has verified that the replica set exists, the
  4536. // active replication subsystem is active, and that some of
  4537. // the parameters are okay. Verify the rest.
  4538. //
  4539. if (!CxtionName ||
  4540. !PartnerName ||
  4541. !PartnerPrincName ||
  4542. !CxtionGuid ||
  4543. !PartnerGuid ||
  4544. !ParentGuid ||
  4545. (PartnerAuthLevel != CXTION_AUTH_KERBEROS_FULL &&
  4546. PartnerAuthLevel != CXTION_AUTH_NONE)) {
  4547. WStatus = ERROR_INVALID_PARAMETER;
  4548. goto CLEANUP;
  4549. }
  4550. //
  4551. // Find the sysvol
  4552. //
  4553. DbReplica = RcsFindSysVolByName(ReplicaSetName);
  4554. if (!DbReplica) {
  4555. DPRINT1(4, ":DS: Promotion failed; could not find %ws\n", ReplicaSetName);
  4556. WStatus = ERROR_INVALID_PARAMETER;
  4557. goto CLEANUP;
  4558. }
  4559. //
  4560. // To be used in the caller's cxtion
  4561. //
  4562. COPY_GUID(ParentGuid, DbReplica->ReplicaName->Guid);
  4563. //
  4564. // PRETEND WE ARE THE DS POLLING THREAD AND ARE ADDING A
  4565. // A CXTION TO AN EXISTING REPLICA.
  4566. //
  4567. // Create the volatile cxtion
  4568. // Set the state to "promoting" at this time because the
  4569. // seeding operation may finish and the state set to
  4570. // NTFRSAPI_SERVICE_DONE before the return of the
  4571. // call to RcsSubmitReplicaSync().
  4572. //
  4573. DbReplica->NtFrsApi_ServiceState = NTFRSAPI_SERVICE_PROMOTING;
  4574. Cxtion = FrsAllocType(CXTION_TYPE);
  4575. Cxtion->Inbound = Inbound;
  4576. SetCxtionFlag(Cxtion, CXTION_FLAGS_CONSISTENT | CXTION_FLAGS_VOLATILE);
  4577. Cxtion->Name = FrsBuildGName(FrsDupGuid((GUID *)CxtionGuid),
  4578. FrsWcsDup(CxtionName));
  4579. Cxtion->Partner = FrsBuildGName(FrsDupGuid((GUID *)PartnerGuid),
  4580. FrsWcsDup(PartnerName));
  4581. Cxtion->PartnerSid = FrsWcsDup(PartnerSid);
  4582. Cxtion->PartSrvName = FrsWcsDup(PartnerPrincName);
  4583. Cxtion->PartnerDnsName = FrsWcsDup(PartnerName);
  4584. Cxtion->PartnerAuthLevel = PartnerAuthLevel;
  4585. Cxtion->PartnerPrincName = FrsWcsDup(PartnerPrincName);
  4586. SetCxtionState(Cxtion, CxtionStateUnjoined);
  4587. WStatus = RcsSubmitReplicaSync(DbReplica, NULL, Cxtion, CMD_START);
  4588. //
  4589. // The active replication subsystem owns the cxtion, now
  4590. //
  4591. Cxtion = NULL;
  4592. CLEANUP1_WS(0, ":DS: ERROR - Creating cxtion for %ws;",
  4593. ReplicaSetName, WStatus, SYNC_FAIL);
  4594. //
  4595. // Submit a command to periodically check the promotion activity.
  4596. // If nothing has happened in awhile, stop the promotion process.
  4597. //
  4598. if (Inbound) {
  4599. DbReplica->NtFrsApi_HackCount++; // != 0
  4600. RcsSubmitReplica(DbReplica, NULL, CMD_CHECK_PROMOTION);
  4601. }
  4602. //
  4603. // SUCCESS
  4604. //
  4605. WStatus = ERROR_SUCCESS;
  4606. goto CLEANUP;
  4607. SYNC_FAIL:
  4608. DbReplica->NtFrsApi_ServiceState = NTFRSAPI_SERVICE_STATE_IS_UNKNOWN;
  4609. //
  4610. // CLEANUP
  4611. //
  4612. CLEANUP:
  4613. FrsFreeType(Cxtion);
  4614. return WStatus;
  4615. }
  4616. DWORD
  4617. FrsDsVerifyPromotionParent(
  4618. IN PWCHAR ReplicaSetName,
  4619. IN PWCHAR ReplicaSetType
  4620. )
  4621. /*++
  4622. Routine Description:
  4623. Start the promotion process by seeding the indicated sysvol.
  4624. Arguments:
  4625. ReplicaSetName - Replica set name
  4626. ReplicaSetType - Type of set (Enterprise or Domain)
  4627. Return Value:
  4628. Win32 Status
  4629. --*/
  4630. {
  4631. #undef DEBSUB
  4632. #define DEBSUB "FrsDsVerifyPromotionParent:"
  4633. DWORD WStatus;
  4634. PREPLICA DbReplica;
  4635. //
  4636. // This parent must be a Dc
  4637. //
  4638. FrsDsGetRole();
  4639. if (!IsADc) {
  4640. DPRINT1(0, ":S: Promotion aborted: %ws is not a dc.\n", ComputerName);
  4641. WStatus = ERROR_SERVICE_SPECIFIC_ERROR;
  4642. goto CLEANUP;
  4643. }
  4644. //
  4645. // WAIT FOR THE ACTIVE REPLICATION SUBSYSTEM TO START
  4646. //
  4647. MainInit();
  4648. if (!MainInitHasRun) {
  4649. WStatus = ERROR_SERVICE_NOT_ACTIVE;
  4650. goto CLEANUP;
  4651. }
  4652. //
  4653. // Let dcpromo determine the timeout
  4654. //
  4655. DPRINT(4, ":S: Waiting for replica command server to start.\n");
  4656. WStatus = WaitForSingleObject(ReplicaEvent, 10 * 60 * 1000);
  4657. CHECK_WAIT_ERRORS(3, WStatus, 1, ACTION_RETURN);
  4658. //
  4659. // Is the service shutting down?
  4660. //
  4661. if (FrsIsShuttingDown) {
  4662. WStatus = ERROR_SERVICE_NOT_ACTIVE;
  4663. goto CLEANUP;
  4664. }
  4665. //
  4666. // Verify the existence of the set
  4667. //
  4668. DbReplica = RcsFindSysVolByName(ReplicaSetName);
  4669. if (DbReplica && IS_TIME_ZERO(DbReplica->MembershipExpires)) {
  4670. //
  4671. // Sysvol exists; make sure it is the right type
  4672. //
  4673. if (_wcsicmp(ReplicaSetType, NTFRSAPI_REPLICA_SET_TYPE_ENTERPRISE)) {
  4674. if (DbReplica->ReplicaSetType != FRS_RSTYPE_DOMAIN_SYSVOL) {
  4675. DPRINT3(0, ":S: ERROR - %ws's type is %d; not %d\n",
  4676. ReplicaSetName, DbReplica->ReplicaSetType,
  4677. FRS_RSTYPE_DOMAIN_SYSVOL);
  4678. WStatus = ERROR_NOT_FOUND;
  4679. goto CLEANUP;
  4680. }
  4681. } else if (DbReplica->ReplicaSetType != FRS_RSTYPE_ENTERPRISE_SYSVOL) {
  4682. DPRINT3(0, ":S: ERROR - %ws's type is %d; not %d\n",
  4683. ReplicaSetName, DbReplica->ReplicaSetType,
  4684. FRS_RSTYPE_ENTERPRISE_SYSVOL);
  4685. WStatus = ERROR_NOT_FOUND;
  4686. goto CLEANUP;
  4687. }
  4688. } else {
  4689. DPRINT2(0, ":S: ERROR - %ws does not exist on %ws!\n",
  4690. ReplicaSetName, ComputerName);
  4691. WStatus = ERROR_NOT_FOUND;
  4692. goto CLEANUP;
  4693. }
  4694. //
  4695. // SUCCESS
  4696. //
  4697. WStatus = ERROR_SUCCESS;
  4698. //
  4699. // CLEANUP
  4700. //
  4701. CLEANUP:
  4702. return WStatus;
  4703. }
  4704. VOID
  4705. FrsDsVerifySchedule(
  4706. IN PCONFIG_NODE Node
  4707. )
  4708. /*++
  4709. Routine Description:
  4710. Check the schedule for consistency
  4711. Arguments:
  4712. Sites
  4713. Return Value:
  4714. None.
  4715. --*/
  4716. {
  4717. #undef DEBSUB
  4718. #define DEBSUB "FrsDsVerifySchedule:"
  4719. ULONG i;
  4720. ULONG Num;
  4721. ULONG Len;
  4722. ULONG NumType;
  4723. PSCHEDULE Schedule = Node->Schedule;
  4724. if (!Schedule) {
  4725. return;
  4726. }
  4727. //
  4728. // Too many schedules
  4729. //
  4730. Num = Schedule->NumberOfSchedules;
  4731. if (Num > 3) {
  4732. DPRINT2(4, ":DS: %ws has %d schedules\n", Node->Name->Name, Num);
  4733. Node->Consistent = FALSE;
  4734. return;
  4735. }
  4736. //
  4737. // Too few schedules
  4738. //
  4739. if (Num < 1) {
  4740. DPRINT2(4, ":DS: %ws has %d schedules\n", Node->Name->Name, Num);
  4741. Node->Consistent = FALSE;
  4742. return;
  4743. }
  4744. //
  4745. // Not enough memory
  4746. //
  4747. Len = sizeof(SCHEDULE) +
  4748. (sizeof(SCHEDULE_HEADER) * (Num - 1)) +
  4749. (SCHEDULE_DATA_BYTES * Num);
  4750. if (Node->ScheduleLength < Len) {
  4751. DPRINT2(4, ":DS: %ws is short (ds) by %d bytes\n",
  4752. Node->Name->Name, Len - Node->ScheduleLength);
  4753. Node->Consistent = FALSE;
  4754. return;
  4755. }
  4756. if (Node->Schedule->Size < Len) {
  4757. DPRINT2(4, ":DS: %ws is short (size) by %d bytes\n",
  4758. Node->Name->Name, Len - Node->Schedule->Size);
  4759. Node->Consistent = FALSE;
  4760. return;
  4761. }
  4762. Node->Schedule->Size = Len;
  4763. //
  4764. // Invalid type
  4765. //
  4766. for (i = 0; i < Num; ++i) {
  4767. switch (Schedule->Schedules[i].Type) {
  4768. case SCHEDULE_INTERVAL:
  4769. break;
  4770. case SCHEDULE_BANDWIDTH:
  4771. DPRINT1(4, ":DS: WARN Bandwidth schedule is not supported for %ws\n",
  4772. Node->Name->Name);
  4773. break;
  4774. case SCHEDULE_PRIORITY:
  4775. DPRINT1(4, ":DS: WARN Priority schedule is not supported for %ws\n",
  4776. Node->Name->Name);
  4777. break;
  4778. default:
  4779. DPRINT2(4, ":DS: %ws has an invalid schedule type (%d)\n",
  4780. Node->Name->Name, Schedule->Schedules[i].Type);
  4781. Node->Consistent = FALSE;
  4782. return;
  4783. }
  4784. }
  4785. //
  4786. // Only 0 or 1 interval
  4787. //
  4788. for (NumType = i = 0; i < Num; ++i) {
  4789. if (Schedule->Schedules[i].Type == SCHEDULE_INTERVAL)
  4790. ++NumType;
  4791. }
  4792. if (NumType > 1) {
  4793. DPRINT2(4, ":DS: %ws has %d interval schedules\n",
  4794. Node->Name->Name, NumType);
  4795. Node->Consistent = FALSE;
  4796. }
  4797. //
  4798. // Only 0 or 1 bandwidth
  4799. //
  4800. for (NumType = i = 0; i < Num; ++i) {
  4801. if (Schedule->Schedules[i].Type == SCHEDULE_BANDWIDTH)
  4802. ++NumType;
  4803. }
  4804. if (NumType > 1) {
  4805. DPRINT2(4, ":DS: %ws has %d bandwidth schedules\n",
  4806. Node->Name->Name, NumType);
  4807. Node->Consistent = FALSE;
  4808. }
  4809. //
  4810. // Only 0 or 1 priority
  4811. //
  4812. for (NumType = i = 0; i < Num; ++i) {
  4813. if (Schedule->Schedules[i].Type == SCHEDULE_PRIORITY)
  4814. ++NumType;
  4815. }
  4816. if (NumType > 1) {
  4817. DPRINT2(4, ":DS: %ws has %d priority schedules\n",
  4818. Node->Name->Name, NumType);
  4819. Node->Consistent = FALSE;
  4820. }
  4821. //
  4822. // Invalid offset
  4823. //
  4824. for (i = 0; i < Num; ++i) {
  4825. if (Schedule->Schedules[i].Offset >
  4826. Node->ScheduleLength - SCHEDULE_DATA_BYTES) {
  4827. DPRINT2(4, ":DS: %ws has an invalid offset (%d)\n",
  4828. Node->Name->Name, Schedule->Schedules[i].Offset);
  4829. Node->Consistent = FALSE;
  4830. return;
  4831. }
  4832. }
  4833. }
  4834. VOID
  4835. FrsDsCheckSchedules(
  4836. IN PCONFIG_NODE Root
  4837. )
  4838. /*++
  4839. Routine Description:
  4840. Check all of the schedules for consistency
  4841. Arguments:
  4842. Sites
  4843. Return Value:
  4844. None.
  4845. --*/
  4846. {
  4847. #undef DEBSUB
  4848. #define DEBSUB "FrsDsCheckSchedules:"
  4849. PCONFIG_NODE Node;
  4850. for (Node = Root; Node; Node = Node->Peer) {
  4851. FrsDsVerifySchedule(Node);
  4852. FrsDsCheckSchedules(Node->Children);
  4853. }
  4854. }
  4855. VOID
  4856. FrsDsPushInConsistenciesDown(
  4857. IN PCONFIG_NODE Sites
  4858. )
  4859. /*++
  4860. Routine Description:
  4861. Mark the children of inconsistent parents as inconsistent
  4862. Arguments:
  4863. Sites
  4864. Return Value:
  4865. None.
  4866. --*/
  4867. {
  4868. #undef DEBSUB
  4869. #define DEBSUB "FrsDsPushInConsistenciesDown:"
  4870. PCONFIG_NODE Site;
  4871. PCONFIG_NODE Settings;
  4872. PCONFIG_NODE Set;
  4873. PCONFIG_NODE Server;
  4874. PCONFIG_NODE Cxtion;
  4875. //
  4876. // Push a parent's inconsistency to its children
  4877. //
  4878. for (Site = Sites; Site; Site = Site->Peer) {
  4879. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  4880. if (!Site->Consistent)
  4881. Settings->Consistent = FALSE;
  4882. for (Set = Settings->Children; Set; Set = Set->Peer) {
  4883. if (!Settings->Consistent)
  4884. Set->Consistent = FALSE;
  4885. for (Server = Set->Children; Server; Server = Server->Peer) {
  4886. if (!Set->Consistent)
  4887. Server->Consistent = FALSE;
  4888. for (Cxtion = Server->Children; Cxtion; Cxtion = Cxtion->Peer) {
  4889. if (!Server->Consistent)
  4890. Cxtion->Consistent = FALSE;
  4891. }
  4892. }
  4893. }
  4894. }
  4895. }
  4896. }
  4897. #if DBG
  4898. #define CHECK_NODE_LINKAGE(_Nodes_) FrsDsCheckNodeLinkage(_Nodes)
  4899. BOOL
  4900. FrsDsCheckNodeLinkage(
  4901. PCONFIG_NODE Nodes
  4902. )
  4903. /*++
  4904. Routine Description:
  4905. Recursively check a configuration's site and table linkage
  4906. for incore consistency.
  4907. Arguments:
  4908. Nodes - linked list of nodes
  4909. Return Value:
  4910. None.
  4911. --*/
  4912. {
  4913. #undef DEBSUB
  4914. #define DEBSUB "FrsDsCheckNodeLinkage:"
  4915. PCONFIG_NODE Node; // scan nodes list
  4916. PCONFIG_NODE Child; // scan children list
  4917. DWORD NumChildren; // Count children
  4918. for (Node = Nodes; Node; Node = Node->Peer) {
  4919. //
  4920. // Make sure the number of children matches the actual number
  4921. //
  4922. NumChildren = 0;
  4923. for (Child = Node->Children; Child; Child = Child->Peer) {
  4924. ++NumChildren;
  4925. }
  4926. FRS_ASSERT(NumChildren == Node->NumChildren);
  4927. if (!FrsDsCheckNodeLinkage(Node->Children))
  4928. return FALSE;
  4929. }
  4930. return TRUE; // for Assert(DbgCheckLinkage);
  4931. }
  4932. #else DBG
  4933. #define CHECK_NODE_LINKAGE(_Nodes_)
  4934. #endif DBG
  4935. #define UF_IS_A_DC (UF_SERVER_TRUST_ACCOUNT)
  4936. BOOL
  4937. FrsDsIsPartnerADc(
  4938. IN PWCHAR PartnerName
  4939. )
  4940. /*++
  4941. Routine Description:
  4942. Check if the PartnerName's comptuer object indicates that it is a DC.
  4943. Arguments:
  4944. PartnerName - RPC bindable name
  4945. Return Value:
  4946. Win32 Status
  4947. --*/
  4948. {
  4949. #undef DEBSUB
  4950. #define DEBSUB "FrsDsIsPartnerADc:"
  4951. DWORD WStatus;
  4952. DWORD LStatus;
  4953. DWORD UserAccountFlags;
  4954. PLDAP LocalLdap = NULL;
  4955. PLDAPMessage LdapEntry = NULL;
  4956. PLDAPMessage LdapMsg = NULL;
  4957. PWCHAR *Values = NULL;
  4958. PWCHAR DefaultNamingContext = NULL;
  4959. BOOL PartnerIsADc = FALSE;
  4960. PWCHAR UserAccountControl = NULL;
  4961. PWCHAR Attrs[2];
  4962. WCHAR Filter[MAX_PATH + 1];
  4963. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  4964. PWCHAR SamAccountName = NULL;
  4965. ULONG ulOptions;
  4966. //
  4967. // Convert the passed in name to sam account name.
  4968. // passed in name is of the form FRS1\FRSTEST23$
  4969. // The sam account name is everything after the first '\'
  4970. //
  4971. SamAccountName = wcschr(PartnerName,L'\\');
  4972. if (SamAccountName == NULL) {
  4973. DPRINT1(0, "PartnerName name supplied is in invalid format; %ws\n", PartnerName);
  4974. goto CLEANUP;
  4975. }
  4976. SamAccountName++;
  4977. DPRINT2(4, ":DS: Converted %ws to %ws\n", PartnerName, SamAccountName);
  4978. //
  4979. // Bind to the DS on this DC
  4980. //
  4981. //
  4982. // if ldap_open is called with a server name the api will call DsGetDcName
  4983. // passing the server name as the domainname parm...bad, because
  4984. // DsGetDcName will make a load of DNS queries based on the server name,
  4985. // it is designed to construct these queries from a domain name...so all
  4986. // these queries will be bogus, meaning they will waste network bandwidth,
  4987. // time to fail, and worst case cause expensive on demand links to come up
  4988. // as referrals/forwarders are contacted to attempt to resolve the bogus
  4989. // names. By setting LDAP_OPT_AREC_EXCLUSIVE to on using ldap_set_option
  4990. // after the ldap_init but before any other operation using the ldap
  4991. // handle from ldap_init, the delayed connection setup will not call
  4992. // DsGetDcName, just gethostbyname, or if an IP is passed, the ldap client
  4993. // will detect that and use the address directly.
  4994. //
  4995. // LocalLdap = ldap_open(ComputerName, LDAP_PORT);
  4996. LocalLdap = ldap_init(ComputerName, LDAP_PORT);
  4997. if (LocalLdap == NULL) {
  4998. DPRINT1_WS(4, ":DS: WARN - Coult not open DS on %ws;", ComputerName, GetLastError());
  4999. goto CLEANUP;
  5000. }
  5001. ulOptions = PtrToUlong(LDAP_OPT_ON);
  5002. ldap_set_option(LocalLdap, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions);
  5003. LStatus = ldap_bind_s(LocalLdap, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  5004. CLEANUP1_LS(0, ":DS: WARN - Could not bind to the DS on %ws :",
  5005. ComputerName, LStatus, CLEANUP);
  5006. DPRINT1(4, ":DS: Bound to the DS on %ws\n", ComputerName);
  5007. //
  5008. // Find the default naming context (objectCategory=*)
  5009. //
  5010. MK_ATTRS_1(Attrs, ATTR_DEFAULT_NAMING_CONTEXT);
  5011. if (!FrsDsLdapSearch(LocalLdap, CN_ROOT, LDAP_SCOPE_BASE, CATEGORY_ANY,
  5012. Attrs, 0, &LdapMsg)) {
  5013. goto CLEANUP;
  5014. }
  5015. LdapEntry = ldap_first_entry(LocalLdap, LdapMsg);
  5016. if (!LdapEntry) {
  5017. goto CLEANUP;
  5018. }
  5019. Values = (PWCHAR *)FrsDsFindValues(LocalLdap,
  5020. LdapEntry,
  5021. ATTR_DEFAULT_NAMING_CONTEXT,
  5022. FALSE);
  5023. if (!Values) {
  5024. goto CLEANUP;
  5025. }
  5026. DefaultNamingContext = FrsWcsDup(Values[0]);
  5027. LDAP_FREE_VALUES(Values);
  5028. LDAP_FREE_MSG(LdapMsg);
  5029. DPRINT2(4, ":DS: Default naming context for %ws is %ws\n",
  5030. ComputerName, DefaultNamingContext);
  5031. //
  5032. // Find the account object for PartnerName
  5033. //
  5034. swprintf(Filter, L"(sAMAccountName=%s)", SamAccountName);
  5035. MK_ATTRS_1(Attrs, ATTR_USER_ACCOUNT_CONTROL);
  5036. if (!FrsDsLdapSearchInit(LocalLdap, DefaultNamingContext, LDAP_SCOPE_SUBTREE, Filter,
  5037. Attrs, 0, &FrsSearchContext)) {
  5038. goto CLEANUP;
  5039. }
  5040. //
  5041. // Scan the returned account objects for a valid DC
  5042. //
  5043. for (LdapEntry = FrsDsLdapSearchNext(LocalLdap, &FrsSearchContext);
  5044. LdapEntry != NULL;
  5045. LdapEntry = FrsDsLdapSearchNext(LocalLdap, &FrsSearchContext)) {
  5046. //
  5047. // No user account control flags
  5048. //
  5049. UserAccountControl = FrsDsFindValue(LocalLdap, LdapEntry, ATTR_USER_ACCOUNT_CONTROL);
  5050. if (!UserAccountControl) {
  5051. continue;
  5052. }
  5053. UserAccountFlags = wcstoul(UserAccountControl, NULL, 10);
  5054. DPRINT2(4, ":DS: UserAccountControl for %ws is 0x%08x\n",
  5055. SamAccountName, UserAccountFlags);
  5056. //
  5057. // IS A DC!
  5058. //
  5059. if (UserAccountFlags & UF_IS_A_DC) {
  5060. DPRINT1(4, ":DS: Partner %ws is really a DC!\n", SamAccountName);
  5061. PartnerIsADc = TRUE;
  5062. goto CLEANUP;
  5063. }
  5064. FrsFree(UserAccountControl);
  5065. }
  5066. FrsDsLdapSearchClose(&FrsSearchContext);
  5067. DPRINT1(0, ":DS: ERROR - Partner %ws is NOT a DC!\n", SamAccountName);
  5068. CLEANUP:
  5069. LDAP_FREE_VALUES(Values);
  5070. LDAP_FREE_MSG(LdapMsg);
  5071. FrsFree(DefaultNamingContext);
  5072. FrsFree(UserAccountControl);
  5073. if (LocalLdap) {
  5074. ldap_unbind_s(LocalLdap);
  5075. }
  5076. DPRINT2(4, ":DS: Partner %ws is %s a DC\n",
  5077. PartnerName, (PartnerIsADc) ? "assumed to be" : "NOT");
  5078. return PartnerIsADc;
  5079. }
  5080. DWORD
  5081. FrsDsGetRole(
  5082. VOID
  5083. )
  5084. /*++
  5085. Routine Description:
  5086. Get this computer's role in the domain.
  5087. Arguments:
  5088. Return Value:
  5089. Win32 Status
  5090. --*/
  5091. {
  5092. #undef DEBSUB
  5093. #define DEBSUB "FrsDsGetRole:"
  5094. DWORD WStatus;
  5095. DWORD SysvolReady;
  5096. CHAR GuidStr[GUID_CHAR_LEN];
  5097. DSROLE_PRIMARY_DOMAIN_INFO_BASIC *DsRole;
  5098. //
  5099. // We already know our role; carry on
  5100. //
  5101. if (IsAMember) {
  5102. return ERROR_SUCCESS;
  5103. }
  5104. DPRINT(4, ":DS: Finding this computer's role in the domain.\n");
  5105. #if DBG
  5106. //
  5107. // Emulating multiple machines
  5108. //
  5109. if (ServerGuid) {
  5110. DPRINT(4, ":DS: Always a member with hardwired config\n");
  5111. IsAMember = TRUE;
  5112. return ERROR_SUCCESS;
  5113. }
  5114. #endif DBG
  5115. //
  5116. // Is this a domain controller?
  5117. //
  5118. WStatus = DsRoleGetPrimaryDomainInformation(NULL,
  5119. DsRolePrimaryDomainInfoBasic,
  5120. (PBYTE *)&DsRole);
  5121. CLEANUP_WS(4, ":DS: Can't get Ds role info;", WStatus, RETURN);
  5122. DPRINT1(4, ":DS: Ds Role : %ws\n", Roles[DsRole->MachineRole]);
  5123. DPRINT1(4, ":DS: Ds Role Flags : %08x\n", DsRole->Flags);
  5124. if (DsRole->Flags & DSROLE_PRIMARY_DS_RUNNING) {
  5125. DPRINT(4, ":DS: Ds Role Flag : DSROLE_PRIMARY_DS_RUNNING\n");
  5126. }
  5127. if (DsRole->Flags & DSROLE_PRIMARY_DOMAIN_GUID_PRESENT) {
  5128. DPRINT(4, ":DS: Ds Role Flag : DSROLE_PRIMARY_DOMAIN_GUID_PRESENT\n");
  5129. }
  5130. DPRINT1(4, ":DS: Ds Role DomainNameFlat: %ws\n", DsRole->DomainNameFlat);
  5131. DPRINT1(4, ":DS: Ds Role DomainNameDns : %ws\n", DsRole->DomainNameDns);
  5132. // DPRINT1(4, ":DS: Ds Role DomainForestName: %ws\n", DsRole->DomainForestName);
  5133. GuidToStr(&DsRole->DomainGuid, GuidStr);
  5134. DPRINT1(4, ":DS: Ds Role DomainGuid : %s\n", GuidStr);
  5135. //
  5136. // Backup Domain Controller (DC)
  5137. //
  5138. if (DsRole->MachineRole == DsRole_RoleBackupDomainController) {
  5139. DPRINT(4, ":DS: Computer is a backup DC; sysvol support is enabled.\n");
  5140. IsAMember = TRUE;
  5141. IsADc = TRUE;
  5142. //
  5143. // Primary Domain Controller (DC)
  5144. //
  5145. } else if (DsRole->MachineRole == DsRole_RolePrimaryDomainController) {
  5146. DPRINT(4, ":DS: Computer is a DC; sysvol support is enabled.\n");
  5147. IsAMember = TRUE;
  5148. IsADc = TRUE;
  5149. IsAPrimaryDc = TRUE;
  5150. //
  5151. // Member Server
  5152. //
  5153. } else if (DsRole->MachineRole == DsRole_RoleMemberServer) {
  5154. DPRINT(4, ":DS: Computer is just a member server.\n");
  5155. IsAMember = TRUE;
  5156. #ifdef DS_FREE
  5157. } else if ((DsRole->MachineRole == DsRole_RoleStandaloneServer) && (NoDs == TRUE)) {
  5158. DPRINT(4, ":DS: Computer is running in DS free environment.\n");
  5159. IsAMember = TRUE;
  5160. } else if ((DsRole->MachineRole == DsRole_RoleStandaloneWorkstation) && (NoDs == TRUE)) {
  5161. DPRINT(4, ":DS: Computer is running in DS free environment on non-server.\n");
  5162. IsAMember = TRUE;
  5163. } else if ((DsRole->MachineRole == DsRole_RoleMemberWorkstation) && (NoDs == TRUE)) {
  5164. DPRINT(4, ":DS: Computer is running in DS free environment on non-server.\n");
  5165. IsAMember = TRUE;
  5166. #endif DS_FREE
  5167. //
  5168. // Not in a server in a domain; stop the service
  5169. //
  5170. } else {
  5171. DPRINT(1, ":DS: Computer is not a server in a domain.\n");
  5172. }
  5173. DsRoleFreeMemory(DsRole);
  5174. //
  5175. // Has the sysvol been seeded?
  5176. //
  5177. if (IsADc) {
  5178. //
  5179. // Access the netlogon\parameters key to get the sysvol share status
  5180. //
  5181. WStatus = CfgRegReadDWord(FKC_SYSVOL_READY, NULL, 0, &SysvolReady);
  5182. if (WIN_SUCCESS(WStatus)) {
  5183. if (!SysvolReady) {
  5184. EPRINT1((IsAPrimaryDc) ? EVENT_FRS_SYSVOL_NOT_READY_PRIMARY_2 :
  5185. EVENT_FRS_SYSVOL_NOT_READY_2,
  5186. ComputerName);
  5187. }
  5188. } else {
  5189. DPRINT2_WS(0, "ERROR - reading %ws\\%ws :",
  5190. NETLOGON_SECTION, SYSVOL_READY, WStatus);
  5191. }
  5192. }
  5193. WStatus = ERROR_SUCCESS;
  5194. RETURN:
  5195. return WStatus;
  5196. }
  5197. DWORD
  5198. FrsDsCommitDemotion(
  5199. VOID
  5200. )
  5201. /*++
  5202. Routine Description:
  5203. Commit the demotion process by marking the tombstoned
  5204. sysvols as "do not animate".
  5205. Arguments:
  5206. None.
  5207. Return Value:
  5208. Win32 Status
  5209. --*/
  5210. {
  5211. #undef DEBSUB
  5212. #define DEBSUB "FrsDsCommitDemotion:"
  5213. DWORD WStatus;
  5214. DWORD i;
  5215. PREPLICA DbReplica;
  5216. PVOID Key;
  5217. DWORD SaveWStatus;
  5218. DWORD SysvolPathLen;
  5219. PWCHAR SysvolPath = NULL;
  5220. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  5221. //
  5222. // SHUTDOWN THE DS POLLING THREAD
  5223. // Demotion can run in parallel with the Ds polling thread iff
  5224. // the polling thread never tries to merge the info in the Ds
  5225. // with the active replicas. This could result in the sysvol
  5226. // replica being animated. So, we tell the Ds polling thead to
  5227. // shut down, wake it up if it is asleep so it can see the shutdown
  5228. // request, and then synchronize with the merging code in the
  5229. // Ds polling thread. We don't want to wait for the polling
  5230. // thread to simply die because it may be stuck talking to the
  5231. // Ds. Alternatively, we could use async ldap but that would
  5232. // take too long and is overkill at this time.
  5233. //
  5234. // In any case, the service will be restarted after dcpromo/demote
  5235. // by a reboot or a restart-service by the ntfrsapi.
  5236. //
  5237. //
  5238. // PERF: should use async ldap in polling thread.
  5239. //
  5240. DsIsShuttingDown = TRUE;
  5241. SetEvent(DsPollEvent);
  5242. EnterCriticalSection(&MergingReplicasWithDs);
  5243. LeaveCriticalSection(&MergingReplicasWithDs);
  5244. //
  5245. // Is the service shutting down?
  5246. //
  5247. if (FrsIsShuttingDown) {
  5248. WStatus = ERROR_SERVICE_NOT_ACTIVE;
  5249. goto CLEANUP;
  5250. }
  5251. //
  5252. // WAIT FOR THE ACTIVE REPLICATION SUBSYSTEM TO START
  5253. //
  5254. MainInit();
  5255. if (!MainInitHasRun) {
  5256. WStatus = ERROR_SERVICE_NOT_ACTIVE;
  5257. goto CLEANUP;
  5258. }
  5259. //
  5260. // Let dcpromo determine the timeout
  5261. //
  5262. DPRINT(4, ":S: Waiting for replica command server to start.\n");
  5263. WStatus = WaitForSingleObject(ReplicaEvent, 30 * 60 * 1000);
  5264. CHECK_WAIT_ERRORS(3, WStatus, 1, ACTION_RETURN);
  5265. //
  5266. // Unshare the sysvol
  5267. //
  5268. RcsSetSysvolReady(0);
  5269. //
  5270. // Mark the tombstoned replica sets for sysvols as "do not animate".
  5271. //
  5272. SaveWStatus = ERROR_SUCCESS;
  5273. Key = NULL;
  5274. while (DbReplica = RcsFindNextReplica(&Key)) {
  5275. //
  5276. // Not a sysvol
  5277. //
  5278. if (!FRS_RSTYPE_IS_SYSVOL(DbReplica->ReplicaSetType)) {
  5279. continue;
  5280. }
  5281. //
  5282. // Not tombstoned
  5283. //
  5284. if (IS_TIME_ZERO(DbReplica->MembershipExpires)) {
  5285. continue;
  5286. }
  5287. //
  5288. // Mark as "do not animate"
  5289. //
  5290. WStatus = RcsSubmitReplicaSync(DbReplica, NULL, NULL, CMD_DELETE_NOW);
  5291. if (!WIN_SUCCESS(WStatus)) {
  5292. DPRINT1_WS(0, ":S: ERROR - Could not delete %ws now;",
  5293. DbReplica->ReplicaName->Name, WStatus);
  5294. SaveWStatus = WStatus;
  5295. continue;
  5296. }
  5297. DPRINT1(4, ":S: Deleted %ws in DB", DbReplica->ReplicaName->Name);
  5298. //
  5299. // Reset loop enum key because CMD_DELTE_NOW has removed the entry from
  5300. // the ReplicasByGuid table.
  5301. //
  5302. Key = NULL;
  5303. //
  5304. // Delete ALL OF THE SYSVOL DIRECTORY
  5305. //
  5306. // WARNING: makes assumptions about tree built by dcpromo.
  5307. //
  5308. if (DbReplica->Root) {
  5309. SysvolPath = FrsFree(SysvolPath);
  5310. SysvolPath = FrsWcsDup(DbReplica->Root);
  5311. SysvolPathLen = wcslen(SysvolPath);
  5312. if (SysvolPathLen) {
  5313. for (i = SysvolPathLen - 1; i; --i) {
  5314. if (*(SysvolPath + i) == L'\\') {
  5315. *(SysvolPath + i) = L'\0';
  5316. DPRINT1(4, ":S: Deleting sysvol path %ws\n", SysvolPath);
  5317. WStatus = FrsDeletePath(SysvolPath,
  5318. ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE);
  5319. if (!WIN_SUCCESS(WStatus)) {
  5320. DPRINT1_WS(3, ":S: Warn - FrsDeletePath(%ws); (IGNORED)",
  5321. SysvolPath, WStatus);
  5322. WStatus = ERROR_SUCCESS;
  5323. }
  5324. break;
  5325. }
  5326. }
  5327. }
  5328. }
  5329. //
  5330. // The original code deleted the root and staging directories, not
  5331. // the entire sysvol tree. Allow the original code to execute in
  5332. // case the new code above runs into problems.
  5333. //
  5334. //
  5335. // Why wouldn't a replica set have a root path? But, BSTS.
  5336. //
  5337. if (!DbReplica->Root) {
  5338. continue;
  5339. }
  5340. //
  5341. // DELETE THE CONTENTS OF THE ROOT DIRECTORY
  5342. // Always open the replica root by masking off the FILE_OPEN_REPARSE_POINT flag
  5343. // because we want to open the destination dir not the junction if the root
  5344. // happens to be a mount point.
  5345. //
  5346. WStatus = FrsOpenSourceFileW(&FileHandle,
  5347. DbReplica->Root,
  5348. // WRITE_ACCESS | READ_ACCESS,
  5349. DELETE | READ_ATTRIB_ACCESS | WRITE_ATTRIB_ACCESS | FILE_LIST_DIRECTORY,
  5350. OPEN_OPTIONS & ~FILE_OPEN_REPARSE_POINT);
  5351. if (!WIN_SUCCESS(WStatus)) {
  5352. DPRINT1_WS(0, ":S: ERROR - Cannot open root of replica tree %ws;",
  5353. DbReplica->Root, WStatus);
  5354. continue;
  5355. }
  5356. //
  5357. // Remove object id
  5358. //
  5359. WStatus = FrsDeleteFileObjectId(FileHandle, DbReplica->Root);
  5360. DPRINT1_WS(0, ":S: ERROR - Cannot remove object id from root "
  5361. "of replica tree %ws; Continue with delete",
  5362. DbReplica->Root, WStatus);
  5363. //
  5364. // Delete files/subdirs
  5365. //
  5366. FrsEnumerateDirectory(FileHandle,
  5367. DbReplica->Root,
  5368. 0,
  5369. ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE,
  5370. NULL,
  5371. FrsEnumerateDirectoryDeleteWorker);
  5372. DPRINT1(4, ":S: Deleted files/subdirs for %ws\n", DbReplica->Root);
  5373. FRS_CLOSE(FileHandle);
  5374. //
  5375. // Why wouldn't a replica set have a stage path? But, BSTS.
  5376. //
  5377. if (!DbReplica->Stage) {
  5378. continue;
  5379. }
  5380. //
  5381. // DELETE THE CONTENTS OF THE STAGE DIRECTORY
  5382. //
  5383. WStatus = FrsOpenSourceFileW(&FileHandle,
  5384. DbReplica->Stage,
  5385. // WRITE_ACCESS | READ_ACCESS,
  5386. DELETE | READ_ATTRIB_ACCESS | WRITE_ATTRIB_ACCESS | FILE_LIST_DIRECTORY,
  5387. OPEN_OPTIONS);
  5388. if (!WIN_SUCCESS(WStatus)) {
  5389. DPRINT1_WS(0, ":S: ERROR - Cannot open stage of replica tree %ws;",
  5390. DbReplica->Root, WStatus);
  5391. continue;
  5392. }
  5393. //
  5394. // Delete files/subdirs
  5395. //
  5396. FrsEnumerateDirectory(FileHandle,
  5397. DbReplica->Stage,
  5398. 0,
  5399. ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE,
  5400. NULL,
  5401. FrsEnumerateDirectoryDeleteWorker);
  5402. DPRINT1(4, ":S: Deleted files/subdirs for %ws\n", DbReplica->Stage);
  5403. FRS_CLOSE(FileHandle);
  5404. }
  5405. WStatus = SaveWStatus;
  5406. if (!WIN_SUCCESS(WStatus)) {
  5407. goto CLEANUP;
  5408. }
  5409. //
  5410. // SUCCESS
  5411. //
  5412. WStatus = ERROR_SUCCESS;
  5413. DPRINT(4, ":S: Successfully marked tombstoned sysvols as do not animate.\n");
  5414. //
  5415. // CLEANUP
  5416. //
  5417. CLEANUP:
  5418. FRS_CLOSE(FileHandle);
  5419. SysvolPath = FrsFree(SysvolPath);
  5420. return WStatus;
  5421. }
  5422. DWORD
  5423. FrsDsStartDemotion(
  5424. IN PWCHAR ReplicaSetName
  5425. )
  5426. /*++
  5427. Routine Description:
  5428. Start the demotion process by tombstoning the sysvol.
  5429. Arguments:
  5430. ReplicaSetName - Replica set name
  5431. Return Value:
  5432. Win32 Status
  5433. --*/
  5434. {
  5435. #undef DEBSUB
  5436. #define DEBSUB "FrsDsStartDemotion:"
  5437. DWORD WStatus;
  5438. DWORD FStatus;
  5439. DWORD DbReplicaSetType;
  5440. PREPLICA DbReplica;
  5441. //
  5442. // SHUTDOWN THE DS POLLING THREAD
  5443. // Demotion can run in parallel with the Ds polling thread iff
  5444. // the polling thread never tries to merge the info in the Ds
  5445. // with the active replicas. This could result in the sysvol
  5446. // replica being animated. So, we tell the Ds polling thead to
  5447. // shut down, wake it up if it is asleep so it can see the shutdown
  5448. // request, and then synchronize with the merging code in the
  5449. // Ds polling thread. We don't want to wait for the polling
  5450. // thread to simply die because it may be stuck talking to the
  5451. // Ds. Alternatively, we could use async ldap but that would
  5452. // take too long and is overkill at this time.
  5453. //
  5454. // In any case, the service will be restarted after dcpromo/demote
  5455. // by a reboot or a restart-service by the ntfrsapi.
  5456. //
  5457. //
  5458. // PERF: should use async ldap in polling thread.
  5459. //
  5460. DsIsShuttingDown = TRUE;
  5461. SetEvent(DsPollEvent);
  5462. EnterCriticalSection(&MergingReplicasWithDs);
  5463. LeaveCriticalSection(&MergingReplicasWithDs);
  5464. //
  5465. // Is the service shutting down?
  5466. //
  5467. if (FrsIsShuttingDown) {
  5468. WStatus = ERROR_SERVICE_NOT_ACTIVE;
  5469. goto cleanup;
  5470. }
  5471. //
  5472. // WAIT FOR THE ACTIVE REPLICATION SUBSYSTEM TO START
  5473. //
  5474. MainInit();
  5475. if (!MainInitHasRun) {
  5476. WStatus = ERROR_SERVICE_NOT_ACTIVE;
  5477. goto cleanup;
  5478. }
  5479. //
  5480. // Let dcpromo determine the timeout
  5481. //
  5482. DPRINT(4, ":S: Waiting for replica command server to start.\n");
  5483. WStatus = WaitForSingleObject(ReplicaEvent, 30 * 60 * 1000);
  5484. CHECK_WAIT_ERRORS(3, WStatus, 1, ACTION_RETURN);
  5485. //
  5486. // TOMBSTONE THE REPLICA SET IN THE ACTIVE REPLICATION SUBSYSTEM
  5487. //
  5488. //
  5489. // Find the sysvol replica and tombstone it.
  5490. //
  5491. DbReplica = RcsFindSysVolByName(ReplicaSetName);
  5492. //
  5493. // Can't find by name, not the enterprise sysvol, and not the
  5494. // special call during promotion. See if the name of the domain
  5495. // sysvol was mapped into CN_DOMAIN_SYSVOL. (B3 naming)
  5496. //
  5497. if (!DbReplica &&
  5498. WSTR_NE(ReplicaSetName, L"enterprise") &&
  5499. WSTR_NE(ReplicaSetName, L"")) {
  5500. //
  5501. // domain name may have been mapped into CN_DOMAIN_SYSVOL (new B3 naming)
  5502. //
  5503. DbReplica = RcsFindSysVolByName(CN_DOMAIN_SYSVOL);
  5504. }
  5505. if (DbReplica) {
  5506. //
  5507. // Tombstone the replica set. The set won't actually be deleted
  5508. // until the tombstone expires. If dcdemote fails the replica set
  5509. // will be reanimated when the service restarts.
  5510. //
  5511. // If dcdemote succeeds, the tombstone expiration will be set to
  5512. // yesterday so the replica set will never be animated. See
  5513. // FrsDsCommitDemotion.
  5514. //
  5515. WStatus = RcsSubmitReplicaSync(DbReplica, NULL, NULL, CMD_DELETE);
  5516. CLEANUP2_WS(0, ":S: ERROR - can't delete %ws on %ws;",
  5517. DbReplica->ReplicaName->Name, ComputerName, WStatus, cleanup);
  5518. DPRINT2(0, ":S: Deleted %ws on %ws\n", ReplicaSetName, ComputerName);
  5519. } else if (!wcscmp(ReplicaSetName, L"")) {
  5520. //
  5521. // Special case called during promotion. Delete existing sysvols
  5522. // that may exist from a previous full install or stale database.
  5523. //
  5524. // Make sure the sysvol doesn't already exist. If it does but is
  5525. // tombstoned, set the tombstone to "do not animate". Otherwise,
  5526. // error off.
  5527. //
  5528. DbReplicaSetType = FRS_RSTYPE_ENTERPRISE_SYSVOL;
  5529. again:
  5530. DbReplica = RcsFindSysVolByType(DbReplicaSetType);
  5531. if (!DbReplica) {
  5532. if (DbReplicaSetType == FRS_RSTYPE_ENTERPRISE_SYSVOL) {
  5533. DbReplicaSetType = FRS_RSTYPE_DOMAIN_SYSVOL;
  5534. goto again;
  5535. }
  5536. }
  5537. if (DbReplica) {
  5538. DPRINT2(4, ":S: WARN - Sysvol %ws exists for %ws; deleting!\n",
  5539. DbReplica->ReplicaName->Name, ComputerName);
  5540. //
  5541. // Find our role. If we aren't a DC or the sysvol has been
  5542. // tombstoned, delete it now.
  5543. //
  5544. FrsDsGetRole();
  5545. if (!IS_TIME_ZERO(DbReplica->MembershipExpires) || !IsADc) {
  5546. //
  5547. // Once the MembershipExpires has been set to a time less
  5548. // than Now the replica set will never appear again. The
  5549. // replica sticks around for now since the RPC server
  5550. // may be putting command packets on this replica's queue.
  5551. // The packets will be ignored. The replica will be deleted
  5552. // from the database the next time the service starts. Even
  5553. // if the deletion fails, the rest of the service will
  5554. // not see the replica because the replica struct is not
  5555. // put in the table of active replicas. The deletion is
  5556. // retried at startup.
  5557. //
  5558. WStatus = RcsSubmitReplicaSync(DbReplica, NULL, NULL, CMD_DELETE_NOW);
  5559. CLEANUP1_WS(0, ":S: ERROR - can't delete %ws;",
  5560. DbReplica->ReplicaName->Name, WStatus, cleanup);
  5561. goto again;
  5562. } else {
  5563. DPRINT2(0, ":S: ERROR - Cannot delete %ws for %ws!\n",
  5564. DbReplica->ReplicaName->Name, ComputerName);
  5565. WStatus = ERROR_DUP_NAME;
  5566. goto cleanup;
  5567. }
  5568. }
  5569. } else {
  5570. DPRINT1(0, ":S: Sysvol %ws not found; declaring victory\n", ReplicaSetName);
  5571. }
  5572. //
  5573. // SUCCESS
  5574. //
  5575. WStatus = ERROR_SUCCESS;
  5576. //
  5577. // CLEANUP
  5578. //
  5579. cleanup:
  5580. return WStatus;
  5581. }
  5582. VOID
  5583. FrsDsFreeTree(
  5584. PCONFIG_NODE Root
  5585. )
  5586. /*++
  5587. Routine Description:
  5588. Free every node in a tree
  5589. Arguments:
  5590. Root
  5591. Return Value:
  5592. None.
  5593. --*/
  5594. {
  5595. #undef DEBSUB
  5596. #define DEBSUB "FrsDsFreeTree:"
  5597. PCONFIG_NODE Node;
  5598. while (Root != NULL) {
  5599. Node = Root;
  5600. Root = Root->Peer;
  5601. FrsDsFreeTree(Node->Children);
  5602. FrsFreeType(Node);
  5603. }
  5604. }
  5605. VOID
  5606. FrsDsSwapPtrs(
  5607. PVOID *P1,
  5608. PVOID *P2
  5609. )
  5610. /*++
  5611. Routine Description:
  5612. Swap two pointers.
  5613. Arguments:
  5614. P1 - address of a pointer
  5615. P2 - address of a pointer
  5616. Return Value:
  5617. None.
  5618. --*/
  5619. {
  5620. #undef DEBSUB
  5621. #define DEBSUB "FrsDsSwapPtrs:"
  5622. PVOID Tmp;
  5623. Tmp = *P2;
  5624. *P2 = *P1;
  5625. *P1 = Tmp;
  5626. }
  5627. #if DBG
  5628. //
  5629. // Hardwired configuration for testing w/o the DS
  5630. //
  5631. #define HW_MACHINES 8
  5632. #define THIS_COMPUTER L"[This Computer]"
  5633. typedef struct _HardWired{
  5634. PWCHAR Machine;
  5635. PWCHAR Server;
  5636. PWCHAR Replica;
  5637. BOOL IsPrimary;
  5638. PWCHAR FileFilterList;
  5639. PWCHAR DirFilterList;
  5640. PWCHAR InNames[HW_MACHINES];
  5641. PWCHAR InMachines[HW_MACHINES];
  5642. PWCHAR InServers[HW_MACHINES];
  5643. PWCHAR OutNames[HW_MACHINES];
  5644. PWCHAR OutMachines[HW_MACHINES];
  5645. PWCHAR OutServers[HW_MACHINES];
  5646. PWCHAR Stage;
  5647. PWCHAR Root;
  5648. PWCHAR JetPath;
  5649. } HARDWIRED, *PHARDWIRED;
  5650. //
  5651. // This hard wired configuration is loaded if a path
  5652. // to a ini file is provided at command line.
  5653. //
  5654. PHARDWIRED LoadedWired;
  5655. HARDWIRED DavidWired[] = {
  5656. /*
  5657. t:
  5658. cd \
  5659. md \staging
  5660. md \Replica-A\SERVO1
  5661. md \jet
  5662. md \jet\serv01
  5663. md \jet\serv01\sys
  5664. md \jet\serv01\temp
  5665. md \jet\serv01\log
  5666. u:
  5667. cd \
  5668. md \staging
  5669. md \Replica-A\SERVO2
  5670. md \jet
  5671. md \jet\serv02
  5672. md \jet\serv02\sys
  5673. md \jet\serv02\temp
  5674. md \jet\serv02\log
  5675. s:
  5676. cd \
  5677. md \staging
  5678. md \Replica-A\SERVO3
  5679. md \jet
  5680. md \jet\serv03
  5681. md \jet\serv03\sys
  5682. md \jet\serv03\temp
  5683. md \jet\serv03\log
  5684. */
  5685. #define RSA L"Replica-A"
  5686. #define TEST_MACHINE_NAME THIS_COMPUTER
  5687. #define SERVER_1 L"SERV01"
  5688. #define MACHINE_1 TEST_MACHINE_NAME
  5689. #define SERVER_2 L"SERV02"
  5690. #define MACHINE_2 TEST_MACHINE_NAME
  5691. #define SERVER_3 L"SERV03"
  5692. #define MACHINE_3 TEST_MACHINE_NAME
  5693. #define SERVER_4 L"SERV04"
  5694. #define MACHINE_4 TEST_MACHINE_NAME
  5695. #define SERVER_5 L"SERV05"
  5696. #define MACHINE_5 TEST_MACHINE_NAME
  5697. #define SERVER_6 L"SERV06"
  5698. #define MACHINE_6 TEST_MACHINE_NAME
  5699. #define SERVER_7 L"SERV07"
  5700. #define MACHINE_7 TEST_MACHINE_NAME
  5701. #define SERVER_8 L"SERV08"
  5702. #define MACHINE_8 TEST_MACHINE_NAME
  5703. /*
  5704. // These are the old vol assignments
  5705. #define SERVER_1_VOL L"t:"
  5706. #define SERVER_2_VOL L"u:"
  5707. #define SERVER_3_VOL L"s:"
  5708. #define SERVER_4_VOL L"v:"
  5709. #define SERVER_5_VOL L"w:"
  5710. #define SERVER_6_VOL L"x:"
  5711. #define SERVER_7_VOL L"y:"
  5712. #define SERVER_8_VOL L"z:"
  5713. */
  5714. // /*
  5715. // These are the new vol assignments
  5716. #define SERVER_1_VOL L"d:"
  5717. #define SERVER_2_VOL L"e:"
  5718. #define SERVER_3_VOL L"f:"
  5719. #define SERVER_4_VOL L"g:"
  5720. #define SERVER_5_VOL L"h:"
  5721. #define SERVER_6_VOL L"i:"
  5722. #define SERVER_7_VOL L"j:"
  5723. #define SERVER_8_VOL L"k:"
  5724. // */
  5725. /*
  5726. //
  5727. // NOTE: The following was generated from an excel spreadsheet
  5728. // \nt\private\net\svcimgs\ntrepl\topology.xls
  5729. // Hand generation is a bit tedious and error prone so use the spreadsheet.
  5730. //
  5731. //
  5732. // David's 8-way fully connected
  5733. //
  5734. TEST_MACHINE_NAME, // machine
  5735. SERVER_1, // server name
  5736. RSA, // replica
  5737. TRUE, // IsPrimary
  5738. NULL, NULL, // File/Dir Filter
  5739. {L"CXT2_1", L"CXT3_1", L"CXT4_1", L"CXT5_1", L"CXT6_1", L"CXT7_1", L"CXT8_1", NULL}, // inbound cxtions
  5740. {MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // inbound machines
  5741. {SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // inbound servers
  5742. {L"CXT1_2", L"CXT1_3", L"CXT1_4", L"CXT1_5", L"CXT1_6", L"CXT1_7", L"CXT1_8", NULL}, // outbound cxtions
  5743. {MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // outbound machines
  5744. {SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // outbound servers
  5745. SERVER_1_VOL L"\\staging", // stage
  5746. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  5747. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  5748. TEST_MACHINE_NAME, // machine
  5749. SERVER_2, // server name
  5750. RSA, // replica
  5751. FALSE, // IsPrimary
  5752. NULL, NULL, // File/Dir Filter
  5753. {L"CXT1_2", L"CXT3_2", L"CXT4_2", L"CXT5_2", L"CXT6_2", L"CXT7_2", L"CXT8_2", NULL}, // inbound cxtions
  5754. {MACHINE_1, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // inbound machines
  5755. {SERVER_1, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // inbound servers
  5756. {L"CXT2_1", L"CXT2_3", L"CXT2_4", L"CXT2_5", L"CXT2_6", L"CXT2_7", L"CXT2_8", NULL}, // outbound cxtions
  5757. {MACHINE_1, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // outbound machines
  5758. {SERVER_1, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // outbound servers
  5759. SERVER_2_VOL L"\\staging", // stage
  5760. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  5761. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  5762. TEST_MACHINE_NAME, // machine
  5763. SERVER_3, // server name
  5764. RSA, // replica
  5765. FALSE, // IsPrimary
  5766. NULL, NULL, // File/Dir Filter
  5767. {L"CXT1_3", L"CXT2_3", L"CXT4_3", L"CXT5_3", L"CXT6_3", L"CXT7_3", L"CXT8_3", NULL}, // inbound cxtions
  5768. {MACHINE_1, MACHINE_2, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // inbound machines
  5769. {SERVER_1, SERVER_2, SERVER_4, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // inbound servers
  5770. {L"CXT3_1", L"CXT3_2", L"CXT3_4", L"CXT3_5", L"CXT3_6", L"CXT3_7", L"CXT3_8", NULL}, // outbound cxtions
  5771. {MACHINE_1, MACHINE_2, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // outbound machines
  5772. {SERVER_1, SERVER_2, SERVER_4, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // outbound servers
  5773. SERVER_3_VOL L"\\staging", // stage
  5774. SERVER_3_VOL L"\\" RSA L"\\" SERVER_3, // root
  5775. SERVER_3_VOL L"\\jet\\" SERVER_3, // Jet Path
  5776. TEST_MACHINE_NAME, // machine
  5777. SERVER_4, // server name
  5778. RSA, // replica
  5779. FALSE, // IsPrimary
  5780. NULL, NULL, // File/Dir Filter
  5781. {L"CXT1_4", L"CXT2_4", L"CXT3_4", L"CXT5_4", L"CXT6_4", L"CXT7_4", L"CXT8_4", NULL}, // inbound cxtions
  5782. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // inbound machines
  5783. {SERVER_1, SERVER_2, SERVER_3, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // inbound servers
  5784. {L"CXT4_1", L"CXT4_2", L"CXT4_3", L"CXT4_5", L"CXT4_6", L"CXT4_7", L"CXT4_8", NULL}, // outbound cxtions
  5785. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_5, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // outbound machines
  5786. {SERVER_1, SERVER_2, SERVER_3, SERVER_5, SERVER_6, SERVER_7, SERVER_8, NULL}, // outbound servers
  5787. SERVER_4_VOL L"\\staging", // stage
  5788. SERVER_4_VOL L"\\" RSA L"\\" SERVER_4, // root
  5789. SERVER_4_VOL L"\\jet\\" SERVER_4, // Jet Path
  5790. TEST_MACHINE_NAME, // machine
  5791. SERVER_5, // server name
  5792. RSA, // replica
  5793. FALSE, // IsPrimary
  5794. NULL, NULL, // File/Dir Filter
  5795. {L"CXT1_5", L"CXT2_5", L"CXT3_5", L"CXT4_5", L"CXT6_5", L"CXT7_5", L"CXT8_5", NULL}, // inbound cxtions
  5796. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // inbound machines
  5797. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_6, SERVER_7, SERVER_8, NULL}, // inbound servers
  5798. {L"CXT5_1", L"CXT5_2", L"CXT5_3", L"CXT5_4", L"CXT5_6", L"CXT5_7", L"CXT5_8", NULL}, // outbound cxtions
  5799. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_6, MACHINE_7, MACHINE_8, NULL}, // outbound machines
  5800. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_6, SERVER_7, SERVER_8, NULL}, // outbound servers
  5801. SERVER_5_VOL L"\\staging", // stage
  5802. SERVER_5_VOL L"\\" RSA L"\\" SERVER_5, // root
  5803. SERVER_5_VOL L"\\jet\\" SERVER_5, // Jet Path
  5804. TEST_MACHINE_NAME, // machine
  5805. SERVER_6, // server name
  5806. RSA, // replica
  5807. FALSE, // IsPrimary
  5808. NULL, NULL, // File/Dir Filter
  5809. {L"CXT1_6", L"CXT2_6", L"CXT3_6", L"CXT4_6", L"CXT5_6", L"CXT7_6", L"CXT8_6", NULL}, // inbound cxtions
  5810. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_7, MACHINE_8, NULL}, // inbound machines
  5811. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_7, SERVER_8, NULL}, // inbound servers
  5812. {L"CXT6_1", L"CXT6_2", L"CXT6_3", L"CXT6_4", L"CXT6_5", L"CXT6_7", L"CXT6_8", NULL}, // outbound cxtions
  5813. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_7, MACHINE_8, NULL}, // outbound machines
  5814. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_7, SERVER_8, NULL}, // outbound servers
  5815. SERVER_6_VOL L"\\staging", // stage
  5816. SERVER_6_VOL L"\\" RSA L"\\" SERVER_6, // root
  5817. SERVER_6_VOL L"\\jet\\" SERVER_6, // Jet Path
  5818. TEST_MACHINE_NAME, // machine
  5819. SERVER_7, // server name
  5820. RSA, // replica
  5821. FALSE, // IsPrimary
  5822. NULL, NULL, // File/Dir Filter
  5823. {L"CXT1_7", L"CXT2_7", L"CXT3_7", L"CXT4_7", L"CXT5_7", L"CXT6_7", L"CXT8_7", NULL}, // inbound cxtions
  5824. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_8, NULL}, // inbound machines
  5825. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_8, NULL}, // inbound servers
  5826. {L"CXT7_1", L"CXT7_2", L"CXT7_3", L"CXT7_4", L"CXT7_5", L"CXT7_6", L"CXT7_8", NULL}, // outbound cxtions
  5827. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_8, NULL}, // outbound machines
  5828. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_8, NULL}, // outbound servers
  5829. SERVER_7_VOL L"\\staging", // stage
  5830. SERVER_7_VOL L"\\" RSA L"\\" SERVER_7, // root
  5831. SERVER_7_VOL L"\\jet\\" SERVER_7, // Jet Path
  5832. TEST_MACHINE_NAME, // machine
  5833. SERVER_8, // server name
  5834. RSA, // replica
  5835. FALSE, // IsPrimary
  5836. NULL, NULL, // File/Dir Filter
  5837. {L"CXT1_8", L"CXT2_8", L"CXT3_8", L"CXT4_8", L"CXT5_8", L"CXT6_8", L"CXT7_8", NULL}, // inbound cxtions
  5838. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, NULL}, // inbound machines
  5839. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_7, NULL}, // inbound servers
  5840. {L"CXT8_1", L"CXT8_2", L"CXT8_3", L"CXT8_4", L"CXT8_5", L"CXT8_6", L"CXT8_7", NULL}, // outbound cxtions
  5841. {MACHINE_1, MACHINE_2, MACHINE_3, MACHINE_4, MACHINE_5, MACHINE_6, MACHINE_7, NULL}, // outbound machines
  5842. {SERVER_1, SERVER_2, SERVER_3, SERVER_4, SERVER_5, SERVER_6, SERVER_7, NULL}, // outbound servers
  5843. SERVER_8_VOL L"\\staging", // stage
  5844. SERVER_8_VOL L"\\" RSA L"\\" SERVER_8, // root
  5845. SERVER_8_VOL L"\\jet\\" SERVER_8, // Jet Path
  5846. */
  5847. ///*
  5848. //
  5849. // 8 way ring
  5850. //
  5851. TEST_MACHINE_NAME, // machine
  5852. SERVER_1, // server name
  5853. RSA, // replica
  5854. TRUE, // IsPrimary
  5855. NULL, NULL, // File/Dir Filter
  5856. {L"CXT2_1", L"CXT8_1", NULL }, // inbound cxtions
  5857. {MACHINE_2, MACHINE_8, NULL }, // inbound machines
  5858. {SERVER_2, SERVER_8, NULL }, // inbound servers
  5859. {L"CXT1_2", L"CXT1_8", NULL }, // outbound cxtions
  5860. {MACHINE_2, MACHINE_8, NULL }, // outbound machines
  5861. {SERVER_2, SERVER_8, NULL }, // outbound servers
  5862. SERVER_1_VOL L"\\staging", // stage
  5863. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  5864. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  5865. TEST_MACHINE_NAME, // machine
  5866. SERVER_2, // server name
  5867. RSA, // replica
  5868. FALSE, // IsPrimary
  5869. NULL, NULL, // File/Dir Filter
  5870. {L"CXT3_2", L"CXT1_2", NULL }, // inbound cxtions
  5871. {MACHINE_3, MACHINE_1, NULL }, // inbound machines
  5872. {SERVER_3, SERVER_1, NULL }, // inbound servers
  5873. {L"CXT2_3", L"CXT2_1", NULL }, // outbound cxtions
  5874. {MACHINE_3, MACHINE_1, NULL }, // outbound machines
  5875. {SERVER_3, SERVER_1, NULL }, // outbound servers
  5876. SERVER_2_VOL L"\\staging", // stage
  5877. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  5878. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  5879. TEST_MACHINE_NAME, // machine
  5880. SERVER_3, // server name
  5881. RSA, // replica
  5882. FALSE, // IsPrimary
  5883. NULL, NULL, // File/Dir Filter
  5884. {L"CXT4_3", L"CXT2_3", NULL }, // inbound cxtions
  5885. {MACHINE_4, MACHINE_2, NULL }, // inbound machines
  5886. {SERVER_4, SERVER_2, NULL }, // inbound servers
  5887. {L"CXT3_4", L"CXT3_2", NULL }, // outbound cxtions
  5888. {MACHINE_4, MACHINE_2, NULL }, // outbound machines
  5889. {SERVER_4, SERVER_2, NULL }, // outbound servers
  5890. SERVER_3_VOL L"\\staging", // stage
  5891. SERVER_3_VOL L"\\" RSA L"\\" SERVER_3, // root
  5892. SERVER_3_VOL L"\\jet\\" SERVER_3, // Jet Path
  5893. TEST_MACHINE_NAME, // machine
  5894. SERVER_4, // server name
  5895. RSA, // replica
  5896. FALSE, // IsPrimary
  5897. NULL, NULL, // File/Dir Filter
  5898. {L"CXT5_4", L"CXT3_4", NULL }, // inbound cxtions
  5899. {MACHINE_5, MACHINE_3, NULL }, // inbound machines
  5900. {SERVER_5, SERVER_3, NULL }, // inbound servers
  5901. {L"CXT4_5", L"CXT4_3", NULL }, // outbound cxtions
  5902. {MACHINE_5, MACHINE_3, NULL }, // outbound machines
  5903. {SERVER_5, SERVER_3, NULL }, // outbound servers
  5904. SERVER_4_VOL L"\\staging", // stage
  5905. SERVER_4_VOL L"\\" RSA L"\\" SERVER_4, // root
  5906. SERVER_4_VOL L"\\jet\\" SERVER_4, // Jet Path
  5907. TEST_MACHINE_NAME, // machine
  5908. SERVER_5, // server name
  5909. RSA, // replica
  5910. FALSE, // IsPrimary
  5911. NULL, NULL, // File/Dir Filter
  5912. {L"CXT6_5", L"CXT4_5", NULL }, // inbound cxtions
  5913. {MACHINE_6, MACHINE_4, NULL }, // inbound machines
  5914. {SERVER_6, SERVER_4, NULL }, // inbound servers
  5915. {L"CXT5_6", L"CXT5_4", NULL }, // outbound cxtions
  5916. {MACHINE_6, MACHINE_4, NULL }, // outbound machines
  5917. {SERVER_6, SERVER_4, NULL }, // outbound servers
  5918. SERVER_5_VOL L"\\staging", // stage
  5919. SERVER_5_VOL L"\\" RSA L"\\" SERVER_5, // root
  5920. SERVER_5_VOL L"\\jet\\" SERVER_5, // Jet Path
  5921. TEST_MACHINE_NAME, // machine
  5922. SERVER_6, // server name
  5923. RSA, // replica
  5924. FALSE, // IsPrimary
  5925. NULL, NULL, // File/Dir Filter
  5926. {L"CXT7_6", L"CXT5_6", NULL }, // inbound cxtions
  5927. {MACHINE_7, MACHINE_5, NULL }, // inbound machines
  5928. {SERVER_7, SERVER_5, NULL }, // inbound servers
  5929. {L"CXT6_7", L"CXT6_5", NULL }, // outbound cxtions
  5930. {MACHINE_7, MACHINE_5, NULL }, // outbound machines
  5931. {SERVER_7, SERVER_5, NULL }, // outbound servers
  5932. SERVER_6_VOL L"\\staging", // stage
  5933. SERVER_6_VOL L"\\" RSA L"\\" SERVER_6, // root
  5934. SERVER_6_VOL L"\\jet\\" SERVER_6, // Jet Path
  5935. TEST_MACHINE_NAME, // machine
  5936. SERVER_7, // server name
  5937. RSA, // replica
  5938. FALSE, // IsPrimary
  5939. NULL, NULL, // File/Dir Filter
  5940. {L"CXT8_7", L"CXT6_7", NULL }, // inbound cxtions
  5941. {MACHINE_8, MACHINE_6, NULL }, // inbound machines
  5942. {SERVER_8, SERVER_6, NULL }, // inbound servers
  5943. {L"CXT7_8", L"CXT7_6", NULL }, // outbound cxtions
  5944. {MACHINE_8, MACHINE_6, NULL }, // outbound machines
  5945. {SERVER_8, SERVER_6, NULL }, // outbound servers
  5946. SERVER_7_VOL L"\\staging", // stage
  5947. SERVER_7_VOL L"\\" RSA L"\\" SERVER_7, // root
  5948. SERVER_7_VOL L"\\jet\\" SERVER_7, // Jet Path
  5949. TEST_MACHINE_NAME, // machine
  5950. SERVER_8, // server name
  5951. RSA, // replica
  5952. FALSE, // IsPrimary
  5953. NULL, NULL, // File/Dir Filter
  5954. {L"CXT1_8", L"CXT7_8", NULL }, // inbound cxtions
  5955. {MACHINE_1, MACHINE_7, NULL }, // inbound machines
  5956. {SERVER_1, SERVER_7, NULL }, // inbound servers
  5957. {L"CXT8_1", L"CXT8_7", NULL }, // outbound cxtions
  5958. {MACHINE_1, MACHINE_7, NULL }, // outbound machines
  5959. {SERVER_1, SERVER_7, NULL }, // outbound servers
  5960. SERVER_8_VOL L"\\staging", // stage
  5961. SERVER_8_VOL L"\\" RSA L"\\" SERVER_8, // root
  5962. SERVER_8_VOL L"\\jet\\" SERVER_8, // Jet Path
  5963. //*/
  5964. #define CXT_2_FM_1 L"CXT1_2"
  5965. #define CXT_3_FM_1 L"CXT1_3"
  5966. #define CXT_4_FM_1 L"CXT1_4"
  5967. #define CXT_1_FM_2 L"CXT2_1"
  5968. #define CXT_3_FM_2 L"CXT2_3"
  5969. #define CXT_4_FM_2 L"CXT2_4"
  5970. #define CXT_1_FM_3 L"CXT3_1"
  5971. #define CXT_2_FM_3 L"CXT3_2"
  5972. #define CXT_4_FM_3 L"CXT3_4"
  5973. #define CXT_1_FM_4 L"CXT4_1"
  5974. #define CXT_2_FM_4 L"CXT4_2"
  5975. #define CXT_3_FM_4 L"CXT4_3"
  5976. #define CXT_1_TO_2 L"CXT1_2"
  5977. #define CXT_1_TO_3 L"CXT1_3"
  5978. #define CXT_1_TO_4 L"CXT1_4"
  5979. #define CXT_2_TO_1 L"CXT2_1"
  5980. #define CXT_2_TO_3 L"CXT2_3"
  5981. #define CXT_2_TO_4 L"CXT2_4"
  5982. #define CXT_3_TO_1 L"CXT3_1"
  5983. #define CXT_3_TO_2 L"CXT3_2"
  5984. #define CXT_3_TO_4 L"CXT3_4"
  5985. #define CXT_4_TO_1 L"CXT4_1"
  5986. #define CXT_4_TO_2 L"CXT4_2"
  5987. #define CXT_4_TO_3 L"CXT4_3"
  5988. /*
  5989. //
  5990. // David's 4-way
  5991. //
  5992. TEST_MACHINE_NAME, // machine
  5993. SERVER_1, // server name
  5994. RSA, // replica
  5995. TRUE, // IsPrimary
  5996. NULL, NULL, // File/Dir Filter
  5997. { CXT_1_FM_2, CXT_1_FM_3, CXT_1_FM_4, NULL }, // inbound cxtions
  5998. { MACHINE_2, MACHINE_3, MACHINE_4, NULL }, // inbound machines
  5999. { SERVER_2, SERVER_3, SERVER_4, NULL }, // inbound servers
  6000. { CXT_1_TO_2, CXT_1_TO_3, CXT_1_TO_4, NULL }, // outbound cxtions
  6001. { MACHINE_2, MACHINE_3, MACHINE_4, NULL }, // outbound machines
  6002. { SERVER_2, SERVER_3, SERVER_4, NULL }, // outbound servers
  6003. SERVER_1_VOL L"\\staging", // stage
  6004. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  6005. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  6006. TEST_MACHINE_NAME, // machine
  6007. SERVER_2, // server name
  6008. RSA, // replica
  6009. FALSE, // IsPrimary
  6010. NULL, NULL, // File/Dir Filter
  6011. { CXT_2_FM_1, CXT_2_FM_3, CXT_2_FM_4, NULL }, // inbound cxtions
  6012. { MACHINE_1, MACHINE_3, MACHINE_4, NULL }, // inbound machines
  6013. { SERVER_1, SERVER_3, SERVER_4, NULL }, // inbound servers
  6014. { CXT_2_TO_1, CXT_2_TO_3, CXT_2_TO_4, NULL }, // outbound cxtions
  6015. { MACHINE_1, MACHINE_3, MACHINE_4, NULL }, // outbound machines
  6016. { SERVER_1, SERVER_3, SERVER_4, NULL }, // outbound servers
  6017. SERVER_2_VOL L"\\staging", // stage
  6018. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  6019. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  6020. TEST_MACHINE_NAME, // machine
  6021. SERVER_3, // server name
  6022. RSA, // replica
  6023. FALSE, // IsPrimary
  6024. NULL, NULL, // File/Dir Filter
  6025. { CXT_3_FM_1, CXT_3_FM_2, CXT_3_FM_4, NULL }, // inbound cxtions
  6026. { MACHINE_1, MACHINE_2, MACHINE_4, NULL }, // inbound machines
  6027. { SERVER_1, SERVER_2, SERVER_4, NULL }, // inbound servers
  6028. { CXT_3_TO_1, CXT_3_TO_2, CXT_3_TO_4, NULL }, // outbound cxtions
  6029. { MACHINE_1, MACHINE_2, MACHINE_4, NULL }, // outbound machines
  6030. { SERVER_1, SERVER_2, SERVER_4, NULL }, // outbound servers
  6031. SERVER_3_VOL L"\\staging", // stage
  6032. SERVER_3_VOL L"\\" RSA L"\\" SERVER_3, // root
  6033. SERVER_3_VOL L"\\jet\\" SERVER_3, // Jet Path
  6034. TEST_MACHINE_NAME, // machine
  6035. SERVER_4, // server name
  6036. RSA, // replica
  6037. FALSE, // IsPrimary
  6038. NULL, NULL, // File/Dir Filter
  6039. { CXT_4_FM_1, CXT_4_FM_2, CXT_4_FM_3, NULL }, // inbound cxtions
  6040. { MACHINE_1, MACHINE_2, MACHINE_3, NULL }, // inbound machines
  6041. { SERVER_1, SERVER_2, SERVER_3, NULL }, // inbound servers
  6042. { CXT_4_TO_1, CXT_4_TO_2, CXT_4_TO_3, NULL }, // outbound cxtions
  6043. { MACHINE_1, MACHINE_2, MACHINE_3, NULL }, // outbound machines
  6044. { SERVER_1, SERVER_2, SERVER_3, NULL }, // outbound servers
  6045. SERVER_4_VOL L"\\staging", // stage
  6046. SERVER_4_VOL L"\\" RSA L"\\" SERVER_4, // root
  6047. SERVER_4_VOL L"\\jet\\" SERVER_4, // Jet Path
  6048. */
  6049. /*
  6050. //
  6051. // David's 3-way
  6052. //
  6053. TEST_MACHINE_NAME, // machine
  6054. SERVER_1, // server name
  6055. RSA, // replica
  6056. TRUE, // IsPrimary
  6057. NULL, NULL, // File/Dir Filter
  6058. { CXT_1_FM_2, CXT_1_FM_3, NULL }, // inbound cxtions
  6059. { MACHINE_2, MACHINE_3, NULL }, // inbound machines
  6060. { SERVER_2, SERVER_3, NULL }, // inbound servers
  6061. { CXT_1_TO_2, CXT_1_TO_3, NULL }, // outbound cxtions
  6062. { MACHINE_2, MACHINE_3, NULL }, // outbound machines
  6063. { SERVER_2, SERVER_3, NULL }, // outbound servers
  6064. SERVER_1_VOL L"\\staging", // stage
  6065. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  6066. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  6067. TEST_MACHINE_NAME, // machine
  6068. SERVER_2, // server name
  6069. RSA, // replica
  6070. FALSE, // IsPrimary
  6071. NULL, NULL, // File/Dir Filter
  6072. { CXT_2_FM_1, CXT_2_FM_3, NULL }, // inbound cxtions
  6073. { MACHINE_1, MACHINE_3, NULL }, // inbound machines
  6074. { SERVER_1, SERVER_3, NULL }, // inbound servers
  6075. { CXT_2_TO_1, CXT_2_TO_3, NULL }, // outbound cxtions
  6076. { MACHINE_1, MACHINE_3, NULL }, // outbound machines
  6077. { SERVER_1, SERVER_3, NULL }, // outbound servers
  6078. SERVER_2_VOL L"\\staging", // stage
  6079. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  6080. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  6081. TEST_MACHINE_NAME, // machine
  6082. SERVER_3, // server name
  6083. RSA, // replica
  6084. FALSE, // IsPrimary
  6085. NULL, NULL, // File/Dir Filter
  6086. { CXT_3_FM_1, CXT_3_FM_2, NULL }, // inbound cxtions
  6087. { MACHINE_1, MACHINE_2, NULL }, // inbound machines
  6088. { SERVER_1, SERVER_2, NULL }, // inbound servers
  6089. { CXT_3_TO_1, CXT_3_TO_2, NULL }, // outbound cxtions
  6090. { MACHINE_1, MACHINE_2, NULL }, // outbound machines
  6091. { SERVER_1, SERVER_2, NULL }, // outbound servers
  6092. SERVER_3_VOL L"\\staging", // stage
  6093. SERVER_3_VOL L"\\" RSA L"\\" SERVER_3, // root
  6094. SERVER_3_VOL L"\\jet\\" SERVER_3, // Jet Path
  6095. */
  6096. /*
  6097. //
  6098. // David's 1-way
  6099. //
  6100. TEST_MACHINE_NAME, // machine
  6101. SERVER_1, // server name
  6102. RSA, // replica
  6103. TRUE, // IsPrimary
  6104. NULL, NULL, // File/Dir Filter
  6105. { NULL, NULL }, // inbound cxtions
  6106. { NULL, NULL }, // inbound machines
  6107. { NULL, NULL }, // inbound servers
  6108. { CXT_1_TO_2, NULL }, // outbound cxtions
  6109. { MACHINE_2, NULL }, // outbound machines
  6110. { SERVER_2, NULL }, // outbound servers
  6111. SERVER_1_VOL L"\\staging", // stage
  6112. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  6113. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  6114. TEST_MACHINE_NAME, // machine
  6115. SERVER_2, // server name
  6116. RSA, // replica
  6117. FALSE, // IsPrimary
  6118. NULL, NULL, // File/Dir Filter
  6119. { CXT_2_FM_1, NULL }, // inbound cxtions
  6120. { MACHINE_1, NULL }, // inbound machines
  6121. { SERVER_1, NULL }, // inbound servers
  6122. { NULL, NULL }, // outbound cxtions
  6123. { NULL, NULL }, // outbound machines
  6124. { NULL, NULL }, // outbound servers
  6125. SERVER_2_VOL L"\\staging", // stage
  6126. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  6127. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  6128. */
  6129. /*
  6130. //
  6131. // David's 2-way
  6132. //
  6133. TEST_MACHINE_NAME, // machine
  6134. SERVER_1, // server name
  6135. RSA, // replica
  6136. TRUE, // IsPrimary
  6137. NULL, NULL, // File/Dir Filter
  6138. { CXT_1_FM_2, NULL }, // inbound cxtions
  6139. { MACHINE_2, NULL }, // inbound machines
  6140. { SERVER_2, NULL }, // inbound servers
  6141. { CXT_1_TO_2, NULL }, // outbound cxtions
  6142. { MACHINE_2, NULL }, // outbound machines
  6143. { SERVER_2, NULL }, // outbound servers
  6144. SERVER_1_VOL L"\\staging", // stage
  6145. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  6146. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  6147. TEST_MACHINE_NAME, // machine
  6148. SERVER_2, // server name
  6149. RSA, // replica
  6150. FALSE, // IsPrimary
  6151. NULL, NULL, // File/Dir Filter
  6152. { CXT_2_FM_1, NULL }, // inbound cxtions
  6153. { MACHINE_1, NULL }, // inbound machines
  6154. { SERVER_1, NULL }, // inbound servers
  6155. { CXT_2_TO_1, NULL }, // outbound cxtions
  6156. { MACHINE_1, NULL }, // outbound machines
  6157. { SERVER_1, NULL }, // outbound servers
  6158. SERVER_2_VOL L"\\staging", // stage
  6159. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  6160. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  6161. */
  6162. //
  6163. // End of Config
  6164. //
  6165. NULL, NULL
  6166. };
  6167. HARDWIRED DavidWired2[] = {
  6168. ///*
  6169. //
  6170. // 8 way ring Without Server2
  6171. //
  6172. TEST_MACHINE_NAME, // machine
  6173. SERVER_1, // server name
  6174. RSA, // replica
  6175. TRUE, // IsPrimary
  6176. NULL, NULL, // File/Dir Filter
  6177. {L"CXT2_1", L"CXT8_1", NULL }, // inbound cxtions
  6178. {MACHINE_2, MACHINE_8, NULL }, // inbound machines
  6179. {SERVER_2, SERVER_8, NULL }, // inbound servers
  6180. {L"CXT1_2", L"CXT1_8", NULL }, // outbound cxtions
  6181. {MACHINE_2, MACHINE_8, NULL }, // outbound machines
  6182. {SERVER_2, SERVER_8, NULL }, // outbound servers
  6183. SERVER_1_VOL L"\\staging", // stage
  6184. SERVER_1_VOL L"\\" RSA L"\\" SERVER_1, // root
  6185. SERVER_1_VOL L"\\jet\\" SERVER_1, // Jet Path
  6186. #if 0
  6187. TEST_MACHINE_NAME, // machine
  6188. SERVER_2, // server name
  6189. RSA, // replica
  6190. FALSE, // IsPrimary
  6191. NULL, NULL, // File/Dir Filter
  6192. {L"CXT3_2", L"CXT1_2", NULL }, // inbound cxtions
  6193. {MACHINE_3, MACHINE_1, NULL }, // inbound machines
  6194. {SERVER_3, SERVER_1, NULL }, // inbound servers
  6195. {L"CXT2_3", L"CXT2_1", NULL }, // outbound cxtions
  6196. {MACHINE_3, MACHINE_1, NULL }, // outbound machines
  6197. {SERVER_3, SERVER_1, NULL }, // outbound servers
  6198. SERVER_2_VOL L"\\staging", // stage
  6199. SERVER_2_VOL L"\\" RSA L"\\" SERVER_2, // root
  6200. SERVER_2_VOL L"\\jet\\" SERVER_2, // Jet Path
  6201. #endif
  6202. TEST_MACHINE_NAME, // machine
  6203. SERVER_3, // server name
  6204. RSA, // replica
  6205. FALSE, // IsPrimary
  6206. NULL, NULL, // File/Dir Filter
  6207. {L"CXT4_3", L"CXT2_3", NULL }, // inbound cxtions
  6208. {MACHINE_4, MACHINE_2, NULL }, // inbound machines
  6209. {SERVER_4, SERVER_2, NULL }, // inbound servers
  6210. {L"CXT3_4", L"CXT3_2", NULL }, // outbound cxtions
  6211. {MACHINE_4, MACHINE_2, NULL }, // outbound machines
  6212. {SERVER_4, SERVER_2, NULL }, // outbound servers
  6213. SERVER_3_VOL L"\\staging", // stage
  6214. SERVER_3_VOL L"\\" RSA L"\\" SERVER_3, // root
  6215. SERVER_3_VOL L"\\jet\\" SERVER_3, // Jet Path
  6216. TEST_MACHINE_NAME, // machine
  6217. SERVER_4, // server name
  6218. RSA, // replica
  6219. FALSE, // IsPrimary
  6220. NULL, NULL, // File/Dir Filter
  6221. {L"CXT5_4", L"CXT3_4", NULL }, // inbound cxtions
  6222. {MACHINE_5, MACHINE_3, NULL }, // inbound machines
  6223. {SERVER_5, SERVER_3, NULL }, // inbound servers
  6224. {L"CXT4_5", L"CXT4_3", NULL }, // outbound cxtions
  6225. {MACHINE_5, MACHINE_3, NULL }, // outbound machines
  6226. {SERVER_5, SERVER_3, NULL }, // outbound servers
  6227. SERVER_4_VOL L"\\staging", // stage
  6228. SERVER_4_VOL L"\\" RSA L"\\" SERVER_4, // root
  6229. SERVER_4_VOL L"\\jet\\" SERVER_4, // Jet Path
  6230. TEST_MACHINE_NAME, // machine
  6231. SERVER_5, // server name
  6232. RSA, // replica
  6233. FALSE, // IsPrimary
  6234. NULL, NULL, // File/Dir Filter
  6235. {L"CXT6_5", L"CXT4_5", NULL }, // inbound cxtions
  6236. {MACHINE_6, MACHINE_4, NULL }, // inbound machines
  6237. {SERVER_6, SERVER_4, NULL }, // inbound servers
  6238. {L"CXT5_6", L"CXT5_4", NULL }, // outbound cxtions
  6239. {MACHINE_6, MACHINE_4, NULL }, // outbound machines
  6240. {SERVER_6, SERVER_4, NULL }, // outbound servers
  6241. SERVER_5_VOL L"\\staging", // stage
  6242. SERVER_5_VOL L"\\" RSA L"\\" SERVER_5, // root
  6243. SERVER_5_VOL L"\\jet\\" SERVER_5, // Jet Path
  6244. TEST_MACHINE_NAME, // machine
  6245. SERVER_6, // server name
  6246. RSA, // replica
  6247. FALSE, // IsPrimary
  6248. NULL, NULL, // File/Dir Filter
  6249. {L"CXT7_6", L"CXT5_6", NULL }, // inbound cxtions
  6250. {MACHINE_7, MACHINE_5, NULL }, // inbound machines
  6251. {SERVER_7, SERVER_5, NULL }, // inbound servers
  6252. {L"CXT6_7", L"CXT6_5", NULL }, // outbound cxtions
  6253. {MACHINE_7, MACHINE_5, NULL }, // outbound machines
  6254. {SERVER_7, SERVER_5, NULL }, // outbound servers
  6255. SERVER_6_VOL L"\\staging", // stage
  6256. SERVER_6_VOL L"\\" RSA L"\\" SERVER_6, // root
  6257. SERVER_6_VOL L"\\jet\\" SERVER_6, // Jet Path
  6258. TEST_MACHINE_NAME, // machine
  6259. SERVER_7, // server name
  6260. RSA, // replica
  6261. FALSE, // IsPrimary
  6262. NULL, NULL, // File/Dir Filter
  6263. {L"CXT8_7", L"CXT6_7", NULL }, // inbound cxtions
  6264. {MACHINE_8, MACHINE_6, NULL }, // inbound machines
  6265. {SERVER_8, SERVER_6, NULL }, // inbound servers
  6266. {L"CXT7_8", L"CXT7_6", NULL }, // outbound cxtions
  6267. {MACHINE_8, MACHINE_6, NULL }, // outbound machines
  6268. {SERVER_8, SERVER_6, NULL }, // outbound servers
  6269. SERVER_7_VOL L"\\staging", // stage
  6270. SERVER_7_VOL L"\\" RSA L"\\" SERVER_7, // root
  6271. SERVER_7_VOL L"\\jet\\" SERVER_7, // Jet Path
  6272. TEST_MACHINE_NAME, // machine
  6273. SERVER_8, // server name
  6274. RSA, // replica
  6275. FALSE, // IsPrimary
  6276. NULL, NULL, // File/Dir Filter
  6277. {L"CXT1_8", L"CXT7_8", NULL }, // inbound cxtions
  6278. {MACHINE_1, MACHINE_7, NULL }, // inbound machines
  6279. {SERVER_1, SERVER_7, NULL }, // inbound servers
  6280. {L"CXT8_1", L"CXT8_7", NULL }, // outbound cxtions
  6281. {MACHINE_1, MACHINE_7, NULL }, // outbound machines
  6282. {SERVER_1, SERVER_7, NULL }, // outbound servers
  6283. SERVER_8_VOL L"\\staging", // stage
  6284. SERVER_8_VOL L"\\" RSA L"\\" SERVER_8, // root
  6285. SERVER_8_VOL L"\\jet\\" SERVER_8, // Jet Path
  6286. //
  6287. // End of Config
  6288. //
  6289. NULL, NULL
  6290. };
  6291. #endif DBG
  6292. #if DBG
  6293. GUID *
  6294. FrsDsBuildGuidFromName(
  6295. IN PWCHAR OrigName
  6296. )
  6297. /*++
  6298. Routine Description:
  6299. Build a guid from a character string
  6300. Arguments:
  6301. Name
  6302. Return Value:
  6303. Guid
  6304. --*/
  6305. {
  6306. #undef DEBSUB
  6307. #define DEBSUB "FrsDsBuildGuidFromName:"
  6308. PULONG Guid;
  6309. ULONG Len;
  6310. ULONG *Sum;
  6311. ULONG *SumOfSum;
  6312. PWCHAR Name = OrigName;
  6313. Guid = FrsAlloc(sizeof(GUID));
  6314. //
  6315. // First four bytes are the sum of the chars in Name; the
  6316. // second four bytes are the sum of sums. The last 8 bytes
  6317. // are 0.
  6318. //
  6319. Len = wcslen(Name);
  6320. Sum = Guid;
  6321. SumOfSum = Guid + 1;
  6322. while (Len--) {
  6323. *Sum += *Name++ + 1;
  6324. *SumOfSum += *Sum;
  6325. }
  6326. return (GUID *)Guid;
  6327. }
  6328. #endif DBG
  6329. #if DBG
  6330. VOID
  6331. FrsDsInitializeHardWiredStructs(
  6332. IN PHARDWIRED Wired
  6333. )
  6334. /*++
  6335. Routine Description:
  6336. Initialize the hardwired config stuff. Must happen before any
  6337. of the command servers start.
  6338. Arguments:
  6339. Wired - struct to initialize
  6340. Return Value:
  6341. None
  6342. --*/
  6343. {
  6344. #undef DEBSUB
  6345. #define DEBSUB "FrsDsInitializeHardWiredStructs:"
  6346. ULONG i;
  6347. ULONG j;
  6348. //
  6349. // NULL entries for "machine name" fields are assigned
  6350. // this machine's name
  6351. //
  6352. for (i = 0; Wired[i].Replica; ++i) {
  6353. if (ServerName && WSTR_EQ(ServerName, Wired[i].Server)) {
  6354. //
  6355. // Adjust the default jet parameters. Also, reset the
  6356. // ServerName to match the server name in the hard
  6357. // wired config so that the phoney guids match.
  6358. //
  6359. FrsFree(ServerName);
  6360. FrsFree(JetPath);
  6361. ServerName = FrsWcsDup(Wired[i].Server);
  6362. JetPath = FrsWcsDup(Wired[i].JetPath);
  6363. }
  6364. //
  6365. // Assign this machine's name if the machine entry is NULL
  6366. //
  6367. if (!Wired[i].Machine ||
  6368. WSTR_EQ(Wired[i].Machine, THIS_COMPUTER)) {
  6369. Wired[i].Machine = ComputerName;
  6370. }
  6371. for (j = 0; Wired[i].InNames[j]; ++j) {
  6372. //
  6373. // Assign this machine's name if the machine entry is NULL
  6374. //
  6375. if (WSTR_NE(Wired[i].InMachines[j], THIS_COMPUTER)) {
  6376. continue;
  6377. }
  6378. Wired[i].InMachines[j] = ComputerName;
  6379. }
  6380. for (j = 0; Wired[i].OutNames[j]; ++j) {
  6381. //
  6382. // Assign this machine's name if the machine entry is NULL
  6383. //
  6384. if (WSTR_NE(Wired[i].OutMachines[j], THIS_COMPUTER)) {
  6385. continue;
  6386. }
  6387. Wired[i].OutMachines[j] = ComputerName;
  6388. }
  6389. }
  6390. }
  6391. BOOL
  6392. FrsDsLoadHardWiredFromFile(
  6393. PHARDWIRED *pMemberList,
  6394. PWCHAR pIniFileName
  6395. )
  6396. /*++
  6397. Routine Description:
  6398. Fills the hardwired structure array from data in a file. The file format
  6399. has the form of:
  6400. [MEMBER0]
  6401. MACHINE=[This Computer]
  6402. SERVER=SERV01
  6403. REPLICA=Replica-A
  6404. ISPRIMARY=TRUE
  6405. FILEFILTERLIST=*.tmp;*.bak
  6406. DIRFILTERLIST=obj
  6407. INNAME=CXT2_1, CXT3_1
  6408. INMACHINE=[This Computer], [This Computer]
  6409. INSERVER=SERV02, SERV03
  6410. OUTNAME=CXT1_2, CXT1_3
  6411. OUTMACHINE=[This Computer], [This Computer]
  6412. OUTSERVER=SERV02, SERV03
  6413. STAGE=d:\staging
  6414. ROOT=d:\Replica-A\SERV01
  6415. JETPATH=d:\jet
  6416. [MEMBER1]
  6417. MACHINE=[This Computer]
  6418. SERVER=SERV02
  6419. REPLICA=Replica-A
  6420. ISPRIMARY=FALSE
  6421. FILEFILTERLIST=*.tmp;*.bak
  6422. DIRFILTERLIST=obj
  6423. INNAME=CXT1_2, CXT3_2
  6424. INMACHINE=[This Computer], [This Computer]
  6425. INSERVER=SERV01, SERV03
  6426. OUTNAME=CXT2_1, CXT2_3
  6427. OUTMACHINE=[This Computer], [This Computer]
  6428. OUTSERVER=SERV01, SERV03
  6429. STAGE=e:\staging
  6430. ROOT=e:\Replica-A\SERV02
  6431. JETPATH=e:\jet
  6432. [MEMBER2]
  6433. MACHINE=[This Computer]
  6434. SERVER=SERV03
  6435. REPLICA=Replica-A
  6436. ISPRIMARY=FALSE
  6437. FILEFILTERLIST=*.tmp;*.bak
  6438. DIRFILTERLIST=obj
  6439. INNAME=CXT1_3, CXT2_3
  6440. INMACHINE=[This Computer], [This Computer]
  6441. INSERVER=SERV01, SERV02
  6442. OUTNAME=CXT3_1, CXT3_2
  6443. OUTMACHINE=[This Computer], [This Computer]
  6444. OUTSERVER=SERV01, SERV02
  6445. STAGE=f:\staging
  6446. ROOT=f:\Replica-A\SERV03
  6447. JETPATH=f:\jet
  6448. The string "[This Computer]" has a special meaning in that it refers to the
  6449. computer on which the server is running. You can substitute a specific
  6450. computer name.
  6451. The entries for INNAME, INMACHINE and INSERVER are lists in which the
  6452. corresponding entries in each list form a related triple that speicfy
  6453. the given inbound connection.
  6454. Ditto for OUTNAME, OUTMACHINE, and OUTSERVER.
  6455. The configuration above is for a fully connected mesh with three members.
  6456. It works only when three copies of NTFRS are run on the same machine since
  6457. all the IN and OUTMACHINE entries specify "[This Computer]". The SERVER names
  6458. distinguish each of the three copies of NTFRS for the purpose of providing RPC
  6459. endpoints.
  6460. If the members were actually run on separate physical machines then the
  6461. INMACHINES and the OUTMACHINES would need to specify the particular machine
  6462. names.
  6463. Arguments:
  6464. MemberList - Pointer to the pointer to the array of HardWired structures..
  6465. IniFileName - Name of the ini file to load from.
  6466. Return Value:
  6467. TRUE if data read ok.
  6468. --*/
  6469. {
  6470. #undef DEBSUB
  6471. #define DEBSUB "FrsDsLoadHardWiredFromFile:"
  6472. ULONG TotalMembers;
  6473. ULONG WStatus, Flag;
  6474. ULONG Len, RecordLen;
  6475. PWCHAR szIndex;
  6476. UINT i, k;
  6477. PHARDWIRED HwMember;
  6478. UNICODE_STRING UStr, ListArg;
  6479. PWCHAR pequal;
  6480. PWCHAR *ListArray;
  6481. WCHAR SectionNumber[16];
  6482. WCHAR SectionName[32];
  6483. WCHAR SectionBuffer[5000];
  6484. //
  6485. //Check if the ini file exists.
  6486. //
  6487. if (GetFileAttributes(pIniFileName) == 0xffffffff) {
  6488. DPRINT1(0, ":DS: Could not find ini file... %ws\n", IniFileName);
  6489. return FALSE;
  6490. }
  6491. //
  6492. // Find the number of members in the replica set.
  6493. //
  6494. TotalMembers = 0;
  6495. while(TRUE) {
  6496. wcscpy(SectionName, L"MEMBER");
  6497. wcscpy(SectionNumber, _itow(TotalMembers, SectionNumber, 10));
  6498. wcscat(SectionName, SectionNumber);
  6499. //
  6500. //Read this section from the ini file.
  6501. //
  6502. Flag = GetPrivateProfileSection(SectionName,
  6503. SectionBuffer,
  6504. sizeof(SectionBuffer)/sizeof(WCHAR),
  6505. pIniFileName);
  6506. if (Flag == 0) {
  6507. WStatus = GetLastError();
  6508. break;
  6509. }
  6510. TotalMembers++;
  6511. }
  6512. if (TotalMembers == 0) {
  6513. DPRINT_WS(0, ":DS: No members found in inifile.", WStatus);
  6514. return FALSE;
  6515. }
  6516. //
  6517. // Allocate memory. Then loop thru each member def in the ini file.
  6518. //
  6519. *pMemberList = (PHARDWIRED) FrsAlloc((TotalMembers + 1) * sizeof(HARDWIRED));
  6520. for ( i = 0 ; i < TotalMembers; ++i) {
  6521. wcscpy(SectionName, L"MEMBER");
  6522. wcscpy(SectionNumber, _itow(i, SectionNumber, 10));
  6523. wcscat(SectionName, SectionNumber);
  6524. WStatus = GetPrivateProfileSection(SectionName,
  6525. SectionBuffer,
  6526. sizeof(SectionBuffer)/sizeof(WCHAR),
  6527. pIniFileName);
  6528. HwMember = &(*pMemberList)[i];
  6529. for (szIndex = SectionBuffer; *szIndex != L'\0'; szIndex += RecordLen+1) {
  6530. RecordLen = wcslen(szIndex);
  6531. DPRINT3(5, ":DS: member %d: %ws [%d]\n", i, szIndex, RecordLen);
  6532. //
  6533. // Look for an arg of the form foo=bar.
  6534. //
  6535. pequal = wcschr(szIndex, L'=');
  6536. if (pequal == NULL) {
  6537. DPRINT1(0, ":DS: ERROR - Malformed parameter: %ws\n", szIndex);
  6538. continue;
  6539. }
  6540. //
  6541. // Null terminate and uppercase lefthand side.
  6542. //
  6543. *pequal = UNICODE_NULL;
  6544. _wcsupr(szIndex);
  6545. ++pequal;
  6546. Len = wcslen(pequal);
  6547. if (Len == 0) {
  6548. DPRINT1(0, ":DS: ERROR - Malformed parameter: %ws\n", szIndex);
  6549. continue;
  6550. }
  6551. Len = (Len + 1) * sizeof(WCHAR);
  6552. FrsSetUnicodeStringFromRawString(&UStr,
  6553. Len,
  6554. FrsWcsDup(pequal),
  6555. Len - sizeof(WCHAR));
  6556. if(!wcsncmp(szIndex, L"MACHINE",7)){
  6557. HwMember->Machine = UStr.Buffer;
  6558. continue;
  6559. }
  6560. if(!wcsncmp(szIndex, L"SERVER",6)){
  6561. HwMember->Server = UStr.Buffer;
  6562. continue;
  6563. }
  6564. if(!wcsncmp(szIndex, L"REPLICA",7)){
  6565. HwMember->Replica = UStr.Buffer;
  6566. continue;
  6567. }
  6568. if(!wcsncmp(szIndex, L"ISPRIMARY",9)){
  6569. if (!wcscmp(UStr.Buffer, L"TRUE")) {
  6570. HwMember->IsPrimary = TRUE;
  6571. }
  6572. continue;
  6573. }
  6574. if(!wcsncmp(szIndex, L"FILEFILTERLIST",14)){
  6575. HwMember->FileFilterList = UStr.Buffer;
  6576. continue;
  6577. }
  6578. if(!wcsncmp(szIndex, L"DIRFILTERLIST",13)){
  6579. HwMember->DirFilterList = UStr.Buffer;
  6580. continue;
  6581. }
  6582. if(!wcsncmp(szIndex, L"STAGE",5)){
  6583. HwMember->Stage = UStr.Buffer;
  6584. continue;
  6585. }
  6586. if(!wcsncmp(szIndex, L"ROOT",4)){
  6587. HwMember->Root = UStr.Buffer;
  6588. continue;
  6589. }
  6590. if(!wcsncmp(szIndex, L"JETPATH",7)) {
  6591. HwMember->JetPath = UStr.Buffer;
  6592. continue;
  6593. }
  6594. if (!wcsncmp(szIndex, L"INNAME", 6)) {
  6595. ListArray = HwMember->InNames;
  6596. goto PARSE_COMMA_LIST;
  6597. }
  6598. if (!wcsncmp(szIndex, L"INMACHINE", 9)) {
  6599. ListArray = HwMember->InMachines;
  6600. goto PARSE_COMMA_LIST;
  6601. }
  6602. if (!wcsncmp(szIndex, L"INSERVER", 8)) {
  6603. ListArray = HwMember->InServers;
  6604. goto PARSE_COMMA_LIST;
  6605. }
  6606. if (!wcsncmp(szIndex, L"OUTNAME", 7)) {
  6607. ListArray = HwMember->OutNames;
  6608. goto PARSE_COMMA_LIST;
  6609. }
  6610. if (!wcsncmp(szIndex, L"OUTMACHINE", 10)) {
  6611. ListArray = HwMember->OutMachines;
  6612. goto PARSE_COMMA_LIST;
  6613. }
  6614. if (!wcsncmp(szIndex, L"OUTSERVER", 9)) {
  6615. ListArray = HwMember->OutServers;
  6616. goto PARSE_COMMA_LIST;
  6617. }
  6618. PARSE_COMMA_LIST:
  6619. //
  6620. // Parse the right hand side of args like
  6621. // INSERVER=machine1, machine2, machine3
  6622. // Code above determined what the left hand side was.
  6623. //
  6624. k = 0;
  6625. while (FrsDissectCommaList(UStr, &ListArg, &UStr) &&
  6626. (k < HW_MACHINES)) {
  6627. ListArray[k] = NULL;
  6628. if (ListArg.Length > 0) {
  6629. DPRINT2(5, ":DS: ListArg string: %ws {%d)\n",
  6630. (ListArg.Buffer != NULL) ? ListArg.Buffer : L"<NULL>",
  6631. ListArg.Length);
  6632. ListArray[k] = ListArg.Buffer;
  6633. // Replace the comma (or white space with a null)
  6634. ListArg.Buffer[ListArg.Length/sizeof(WCHAR)] = UNICODE_NULL;
  6635. }
  6636. k += 1;
  6637. }
  6638. }
  6639. }
  6640. return TRUE;
  6641. }
  6642. VOID
  6643. FrsDsInitializeHardWired(
  6644. VOID
  6645. )
  6646. /*++
  6647. Routine Description:
  6648. Initialize the hardwired config stuff. Must happen before any
  6649. of the command servers start.
  6650. Arguments:
  6651. Jet - change the default jet directory from the registry
  6652. Return Value:
  6653. New jet directory
  6654. --*/
  6655. {
  6656. #undef DEBSUB
  6657. #define DEBSUB "FrsDsInitializeHardWired:"
  6658. //
  6659. // Using Ds, not the hard wired config
  6660. //
  6661. if (!NoDs) {
  6662. return;
  6663. }
  6664. //
  6665. // NULL entries for "machine name" fields are assigned
  6666. // this machine's name
  6667. //
  6668. if (IniFileName){
  6669. DPRINT1(0, ":DS: Reading hardwired config from ini file... %ws\n", IniFileName);
  6670. if (FrsDsLoadHardWiredFromFile(&LoadedWired, IniFileName)) {
  6671. DPRINT1(0, ":DS: Using hardwired config from ini file... %ws\n", IniFileName);
  6672. FrsDsInitializeHardWiredStructs(LoadedWired);
  6673. } else {
  6674. FrsFree(IniFileName);
  6675. IniFileName = NULL;
  6676. DPRINT(0, ":DS: Could not load topology from ini file\n");
  6677. DPRINT(0, ":DS: Using David's hardwired...\n");
  6678. FrsDsInitializeHardWiredStructs(DavidWired2);
  6679. FrsDsInitializeHardWiredStructs(DavidWired);
  6680. }
  6681. } else {
  6682. DPRINT(0, ":DS: Using David's hardwired...\n");
  6683. FrsDsInitializeHardWiredStructs(DavidWired2);
  6684. FrsDsInitializeHardWiredStructs(DavidWired);
  6685. }
  6686. //
  6687. // The ServerGuid is used as part of the rpc endpoint
  6688. //
  6689. if (ServerName) {
  6690. ServerGuid = FrsDsBuildGuidFromName(ServerName);
  6691. }
  6692. }
  6693. #endif DBG
  6694. #if DBG
  6695. VOID
  6696. FrsDsUseHardWired(
  6697. IN PHARDWIRED Wired
  6698. )
  6699. /*++
  6700. Routine Description:
  6701. Use the hardwired config instead of the DS config.
  6702. Arguments:
  6703. Wired - hand crafted config
  6704. Return Value:
  6705. None.
  6706. --*/
  6707. {
  6708. #undef DEBSUB
  6709. #define DEBSUB "FrsDsUseHardWired:"
  6710. ULONG i, j;
  6711. ULONG WStatus;
  6712. PREPLICA Replica;
  6713. PCXTION Cxtion;
  6714. PSCHEDULE Schedule;
  6715. ULONG ScheduleLength;
  6716. PBYTE ScheduleData;
  6717. PHARDWIRED W;
  6718. DWORD FileAttributes = 0xFFFFFFFF;
  6719. DPRINT(1, ":DS: ------------ USING HARD WIRED CONFIG\n");
  6720. for (i = 0; Wired && Wired[i].Replica; ++i) {
  6721. if (i) {
  6722. DPRINT(1, ":DS: \n");
  6723. }
  6724. W = &Wired[i];
  6725. DPRINT1(1, ":DS: \tServer: %ws\n", W->Server);
  6726. DPRINT1(1, ":DS: \t Machine: %ws\n", W->Machine);
  6727. DPRINT1(1, ":DS: \t\tReplica : %ws\n", W->Replica);
  6728. DPRINT(1, ":DS: \n");
  6729. for (j=0; (j<HW_MACHINES) && W->InNames[j]; j++ ) {
  6730. DPRINT4(1, ":DS: \t\tInNames,machine,server [%d] : %ws, %ws, %ws\n", j,
  6731. (W->InNames[j]) ? W->InNames[j] : L"",
  6732. (W->InMachines[j]) ? W->InMachines[j] : L"",
  6733. (W->InServers[j]) ? W->InServers[j] : L"");
  6734. }
  6735. DPRINT(1, ":DS: \n");
  6736. for (j=0; (j<HW_MACHINES) && W->OutNames[j]; j++ ) {
  6737. DPRINT4(1, ":DS: \t\tOutNames,machine,server [%d] : %ws, %ws, %ws\n", j,
  6738. (W->OutNames[j]) ? W->OutNames[j] : L"",
  6739. (W->OutMachines[j]) ? W->OutMachines[j] : L"",
  6740. (W->OutServers[j]) ? W->OutServers[j] : L"");
  6741. }
  6742. DPRINT(1, ":DS: \n");
  6743. DPRINT1(1, ":DS: \t\tStage : %ws\n", W->Stage);
  6744. DPRINT1(1, ":DS: \t\tRoot : %ws\n", W->Root);
  6745. DPRINT1(1, ":DS: \t\tJetPath : %ws\n", W->JetPath);
  6746. }
  6747. //
  6748. // Coordinate with replica command server
  6749. //
  6750. RcsBeginMergeWithDs();
  6751. //
  6752. // Construct a replica for each hardwired configuration
  6753. //
  6754. for (i = 0; Wired && Wired[i].Replica; ++i) {
  6755. W = &Wired[i];
  6756. //
  6757. // This server does not match this machine's name; continue
  6758. //
  6759. if (ServerName) {
  6760. if (WSTR_NE(ServerName, W->Server)) {
  6761. continue;
  6762. }
  6763. } else if (WSTR_NE(ComputerName, W->Machine)) {
  6764. continue;
  6765. }
  6766. Replica = FrsAllocType(REPLICA_TYPE);
  6767. Replica->Consistent = TRUE;
  6768. //
  6769. // MATCH
  6770. //
  6771. //
  6772. // Construct a phoney schedule; always "on"
  6773. //
  6774. ScheduleLength = sizeof(SCHEDULE) +
  6775. (2 * sizeof(SCHEDULE_HEADER)) +
  6776. (3 * SCHEDULE_DATA_BYTES);
  6777. Schedule = FrsAlloc(ScheduleLength);
  6778. Schedule->NumberOfSchedules = 3;
  6779. Schedule->Schedules[0].Type = SCHEDULE_BANDWIDTH;
  6780. Schedule->Schedules[0].Offset = sizeof(SCHEDULE) +
  6781. (2 * sizeof(SCHEDULE_HEADER)) +
  6782. (0 * SCHEDULE_DATA_BYTES);
  6783. Schedule->Schedules[1].Type = SCHEDULE_PRIORITY;
  6784. Schedule->Schedules[1].Offset = sizeof(SCHEDULE) +
  6785. (2 * sizeof(SCHEDULE_HEADER)) +
  6786. (1 * SCHEDULE_DATA_BYTES);
  6787. Schedule->Schedules[2].Type = SCHEDULE_INTERVAL;
  6788. Schedule->Schedules[2].Offset = sizeof(SCHEDULE) +
  6789. (2 * sizeof(SCHEDULE_HEADER)) +
  6790. (2 * SCHEDULE_DATA_BYTES);
  6791. ScheduleData = ((PBYTE)Schedule);
  6792. FRS_ASSERT((ScheduleData +
  6793. Schedule->Schedules[2].Offset + SCHEDULE_DATA_BYTES)
  6794. ==
  6795. (((PBYTE)Schedule) + ScheduleLength));
  6796. for (j = 0; j < (SCHEDULE_DATA_BYTES * 3); ++j) {
  6797. *(ScheduleData + Schedule->Schedules[0].Offset + j) = 0x0f;
  6798. }
  6799. Schedule->Size = ScheduleLength;
  6800. Replica->Schedule = Schedule;
  6801. //
  6802. // Construct the guid/names from the name
  6803. //
  6804. Replica->MemberName = FrsBuildGName(FrsDsBuildGuidFromName(W->Server),
  6805. FrsWcsDup(W->Server));
  6806. Replica->ReplicaName = FrsBuildGName(FrsDupGuid(Replica->MemberName->Guid),
  6807. FrsWcsDup(W->Replica));
  6808. Replica->SetName = FrsBuildGName(FrsDsBuildGuidFromName(W->Replica),
  6809. FrsWcsDup(W->Replica));
  6810. //
  6811. // Temporary; a new guid is assigned if this is a new set
  6812. //
  6813. Replica->ReplicaRootGuid = FrsDupGuid(Replica->SetName->Guid);
  6814. //
  6815. // Fill in the rest of the fields
  6816. //
  6817. Replica->Root = FrsWcsDup(W->Root);
  6818. Replica->Stage = FrsWcsDup(W->Stage);
  6819. FRS_WCSLWR(Replica->Root);
  6820. FRS_WCSLWR(Replica->Stage);
  6821. Replica->Volume = FrsWcsVolume(W->Root);
  6822. //
  6823. // Syntax of root path is invalid?
  6824. //
  6825. if (!FrsDsVerifyPath(Replica->Root)) {
  6826. DPRINT2(3, ":DS: Invalid root %ws for %ws\n",
  6827. Replica->Root, Replica->SetName->Name);
  6828. EPRINT1(EVENT_FRS_ROOT_NOT_VALID, Replica->Root);
  6829. Replica->Consistent = FALSE;
  6830. }
  6831. //
  6832. // Does the volume exist and is it NTFS?
  6833. //
  6834. WStatus = FrsVerifyVolume(Replica->Root,
  6835. Replica->SetName->Name,
  6836. FILE_PERSISTENT_ACLS | FILE_SUPPORTS_OBJECT_IDS);
  6837. if (!WIN_SUCCESS(WStatus)) {
  6838. DPRINT2_WS(3, ":DS: Root path Volume (%ws) for %ws does not exist or does not support ACLs and Object IDs;",
  6839. Replica->Root, Replica->SetName->Name, WStatus);
  6840. Replica->Consistent = FALSE;
  6841. }
  6842. //
  6843. // Root does not exist or is inaccessable; continue
  6844. //
  6845. WStatus = FrsDoesDirectoryExist(Replica->Root, &FileAttributes);
  6846. if (!WIN_SUCCESS(WStatus)) {
  6847. DPRINT2_WS(3, ":DS: Root path (%ws) for %ws does not exist;",
  6848. Replica->Root, Replica->SetName->Name, WStatus);
  6849. EPRINT1(EVENT_FRS_ROOT_NOT_VALID, Replica->Root);
  6850. Replica->Consistent = FALSE;
  6851. }
  6852. //
  6853. // Syntax of staging path is invalid; continue
  6854. //
  6855. if (!FrsDsVerifyPath(Replica->Stage)) {
  6856. DPRINT2(3, ":DS: Invalid stage %ws for %ws\n",
  6857. Replica->Stage, Replica->SetName->Name);
  6858. EPRINT2(EVENT_FRS_STAGE_NOT_VALID, Replica->Root, Replica->Stage);
  6859. Replica->Consistent = FALSE;
  6860. }
  6861. //
  6862. // Does the staging volume exist and does it support ACLs?
  6863. // ACLs are required to protect against data theft/corruption
  6864. // in the staging dir.
  6865. //
  6866. WStatus = FrsVerifyVolume(Replica->Stage,
  6867. Replica->SetName->Name,
  6868. FILE_PERSISTENT_ACLS);
  6869. if (!WIN_SUCCESS(WStatus)) {
  6870. DPRINT2_WS(3, ":DS: Stage path Volume (%ws) for %ws does not exist or does not support ACLs;",
  6871. Replica->Stage, Replica->SetName->Name, WStatus);
  6872. Replica->Consistent = FALSE;
  6873. }
  6874. //
  6875. // Stage does not exist or is inaccessable; continue
  6876. //
  6877. WStatus = FrsDoesDirectoryExist(Replica->Stage, &FileAttributes);
  6878. if (!WIN_SUCCESS(WStatus)) {
  6879. DPRINT2_WS(3, ":DS: Stage path (%ws) for %ws does not exist;",
  6880. Replica->Stage, Replica->SetName->Name, WStatus);
  6881. EPRINT2(EVENT_FRS_STAGE_NOT_VALID, Replica->Root, Replica->Stage);
  6882. Replica->Consistent = FALSE;
  6883. }
  6884. if (W->IsPrimary) {
  6885. SetFlag(Replica->CnfFlags, CONFIG_FLAG_PRIMARY);
  6886. }
  6887. //
  6888. // File Filter
  6889. //
  6890. Replica->FileFilterList = FRS_DS_COMPOSE_FILTER_LIST(
  6891. W->FileFilterList,
  6892. RegistryFileExclFilterList,
  6893. DEFAULT_FILE_FILTER_LIST);
  6894. Replica->FileInclFilterList = FrsWcsDup(RegistryFileInclFilterList);
  6895. //
  6896. // Directory Filter
  6897. //
  6898. Replica->DirFilterList = FRS_DS_COMPOSE_FILTER_LIST(
  6899. W->DirFilterList,
  6900. RegistryDirExclFilterList,
  6901. DEFAULT_DIR_FILTER_LIST);
  6902. Replica->DirInclFilterList = FrsWcsDup(RegistryDirInclFilterList);
  6903. //
  6904. // Build Inbound cxtions
  6905. //
  6906. Schedule = FrsAlloc(ScheduleLength);
  6907. CopyMemory(Schedule, Replica->Schedule, ScheduleLength);
  6908. Schedule->Schedules[0].Type = SCHEDULE_INTERVAL;
  6909. Schedule->Schedules[2].Type = SCHEDULE_BANDWIDTH;
  6910. for (j = 0; W->InNames[j]; ++j) {
  6911. Cxtion = FrsAllocType(CXTION_TYPE);
  6912. //
  6913. // Construct the guid/names from the name
  6914. //
  6915. Cxtion->Name = FrsBuildGName(FrsDsBuildGuidFromName(W->InNames[j]),
  6916. FrsWcsDup(W->InNames[j]));
  6917. Cxtion->Partner = FrsBuildGName(FrsDsBuildGuidFromName(W->InServers[j]),
  6918. FrsWcsDup(W->InMachines[j]));
  6919. Cxtion->PartnerDnsName = FrsWcsDup(W->InMachines[j]);
  6920. Cxtion->PartnerSid = FrsWcsDup(W->InMachines[j]);
  6921. Cxtion->PartSrvName = FrsWcsDup(W->InServers[j]);
  6922. DPRINT1(1, ":DS: Hardwired cxtion "FORMAT_CXTION_PATH2"\n",
  6923. PRINT_CXTION_PATH2(Replica, Cxtion));
  6924. Cxtion->PartnerPrincName = FrsWcsDup(Cxtion->PartSrvName);
  6925. //
  6926. // Fill in the rest of the fields
  6927. //
  6928. Cxtion->Inbound = TRUE;
  6929. SetCxtionFlag(Cxtion, CXTION_FLAGS_CONSISTENT);
  6930. Cxtion->Schedule = Schedule;
  6931. Schedule = NULL;
  6932. SetCxtionState(Cxtion, CxtionStateUnjoined);
  6933. GTabInsertEntry(Replica->Cxtions, Cxtion, Cxtion->Name->Guid, NULL);
  6934. }
  6935. //
  6936. // Build Outbound cxtions
  6937. //
  6938. Schedule = FrsAlloc(ScheduleLength);
  6939. CopyMemory(Schedule, Replica->Schedule, ScheduleLength);
  6940. Schedule->Schedules[0].Type = SCHEDULE_INTERVAL;
  6941. Schedule->Schedules[2].Type = SCHEDULE_BANDWIDTH;
  6942. for (j = 0; W->OutNames[j]; ++j) {
  6943. Cxtion = FrsAllocType(CXTION_TYPE);
  6944. //
  6945. // Construct the guid/names from the name
  6946. //
  6947. Cxtion->Name = FrsBuildGName(FrsDsBuildGuidFromName(W->OutNames[j]),
  6948. FrsWcsDup(W->OutNames[j]));
  6949. Cxtion->Partner = FrsBuildGName(FrsDsBuildGuidFromName(W->OutServers[j]),
  6950. FrsWcsDup(W->OutMachines[j]));
  6951. Cxtion->PartnerDnsName = FrsWcsDup(W->OutMachines[j]);
  6952. Cxtion->PartnerSid = FrsWcsDup(W->OutMachines[j]);
  6953. Cxtion->PartSrvName = FrsWcsDup(W->OutServers[j]);
  6954. DPRINT1(1, ":DS: Hardwired cxtion "FORMAT_CXTION_PATH2"\n",
  6955. PRINT_CXTION_PATH2(Replica, Cxtion));
  6956. Cxtion->PartnerPrincName = FrsWcsDup(Cxtion->PartSrvName);
  6957. //
  6958. // Fill in the rest of the fields
  6959. //
  6960. Cxtion->Inbound = FALSE;
  6961. SetCxtionFlag(Cxtion, CXTION_FLAGS_CONSISTENT);
  6962. Cxtion->Schedule = Schedule;
  6963. Schedule = NULL;
  6964. SetCxtionState(Cxtion, CxtionStateUnjoined);
  6965. GTabInsertEntry(Replica->Cxtions, Cxtion, Cxtion->Name->Guid, NULL);
  6966. }
  6967. if (Schedule) {
  6968. FrsFree(Schedule);
  6969. }
  6970. //
  6971. // Merge the replica with the active replicas
  6972. //
  6973. RcsMergeReplicaFromDs(Replica);
  6974. }
  6975. RcsEndMergeWithDs();
  6976. }
  6977. #endif DBG
  6978. DWORD
  6979. FrsDsGetSubscribers(
  6980. IN PLDAP Ldap,
  6981. IN PWCHAR SubscriptionDn,
  6982. IN PCONFIG_NODE Parent
  6983. )
  6984. /*++
  6985. Routine Description:
  6986. Recursively scan the DS tree beginning at computer
  6987. Part of NewDs poll APIs.
  6988. Arguments:
  6989. Ldap - opened and bound ldap connection
  6990. SubscriptionDn - distininguished name of subscriptions object
  6991. Parent - parent node
  6992. Return Value:
  6993. WIN32 Status
  6994. --*/
  6995. {
  6996. #undef DEBSUB
  6997. #define DEBSUB "FrsDsGetSubscribers:"
  6998. PWCHAR Attrs[8];
  6999. PLDAPMessage LdapEntry;
  7000. PCONFIG_NODE Node;
  7001. DWORD WStatus = ERROR_SUCCESS;
  7002. DWORD Status = ERROR_SUCCESS;
  7003. PGEN_ENTRY ConflictingNodeEntry = NULL;
  7004. PCONFIG_NODE ConflictingNode = NULL;
  7005. PCONFIG_NODE Winner = NULL;
  7006. PCONFIG_NODE Loser = NULL;
  7007. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  7008. HANDLE StageHandle = INVALID_HANDLE_VALUE;
  7009. DWORD FileAttributes = 0xFFFFFFFF;
  7010. DWORD CreateStatus = ERROR_SUCCESS;
  7011. //
  7012. // Search the DS beginning at Base for entries of (objectCategory=nTFRSSubscriber)
  7013. //
  7014. MK_ATTRS_7(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  7015. ATTR_REPLICA_ROOT, ATTR_REPLICA_STAGE, ATTR_MEMBER_REF);
  7016. if (!FrsDsLdapSearchInit(Ldap, SubscriptionDn, LDAP_SCOPE_ONELEVEL, CATEGORY_SUBSCRIBER,
  7017. Attrs, 0, &FrsSearchContext)) {
  7018. return ERROR_ACCESS_DENIED;
  7019. }
  7020. if (FrsSearchContext.EntriesInPage == 0) {
  7021. DPRINT1(0, ":DS: No NTFRSSubscriber object found under %ws!\n", SubscriptionDn);
  7022. }
  7023. //
  7024. // Scan the entries returned from ldap_search
  7025. //
  7026. for (LdapEntry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  7027. LdapEntry != NULL && WIN_SUCCESS(WStatus);
  7028. LdapEntry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  7029. //
  7030. // Basic node info (guid, name, dn, schedule, and usnchanged)
  7031. //
  7032. Node = FrsDsAllocBasicNode(Ldap, LdapEntry, CONFIG_TYPE_SUBSCRIBER);
  7033. if (!Node) {
  7034. DPRINT(0, ":DS: Subscriber lacks basic info; skipping\n");
  7035. continue;
  7036. }
  7037. //
  7038. // Member reference
  7039. //
  7040. Node->MemberDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_MEMBER_REF);
  7041. if (Node->MemberDn == NULL) {
  7042. DPRINT1(0, ":DS: ERROR - No Member Reference found on subscriber (%ws). Skipping\n", Node->Dn);
  7043. //
  7044. // Add to the poll summary event log.
  7045. //
  7046. FrsDsAddToPollSummary3ws(IDS_POLL_SUM_INVALID_ATTRIBUTE, ATTR_SUBSCRIBER,
  7047. Node->Dn, ATTR_MEMBER_REF);
  7048. FrsFreeType(Node);
  7049. continue;
  7050. }
  7051. FRS_WCSLWR(Node->MemberDn);
  7052. //
  7053. // Root pathname
  7054. //
  7055. Node->Root = FrsDsFindValue(Ldap, LdapEntry, ATTR_REPLICA_ROOT);
  7056. if (Node->Root == NULL) {
  7057. DPRINT1(0, ":DS: ERROR - No Root path found on subscriber (%ws). Skipping\n", Node->Dn);
  7058. FrsFreeType(Node);
  7059. continue;
  7060. }
  7061. FRS_WCSLWR(Node->Root);
  7062. //
  7063. // Staging pathname. No need to traverse reparse points on the staging dir.
  7064. //
  7065. Node->Stage = FrsDsFindValue(Ldap, LdapEntry, ATTR_REPLICA_STAGE);
  7066. if (Node->Stage == NULL) {
  7067. DPRINT1(0, ":DS: ERROR - No Staging path found on subscriber (%ws). Skipping\n", Node->Dn);
  7068. FrsFreeType(Node);
  7069. continue;
  7070. }
  7071. FRS_WCSLWR(Node->Stage);
  7072. //
  7073. // Create the staging directory if it does not exist.
  7074. //
  7075. Status = FrsDoesDirectoryExist(Node->Stage, &FileAttributes);
  7076. if (!WIN_SUCCESS(Status)) {
  7077. CreateStatus = FrsCreateDirectory(Node->Stage);
  7078. DPRINT1_WS(0, ":DS: ERROR - Can't create staging dir %ws;", Node->Stage, CreateStatus);
  7079. }
  7080. //
  7081. // If the staging dir was just created successfully or if it does not have the
  7082. // hidden attribute set then set the security on it.
  7083. //
  7084. if ((!WIN_SUCCESS(Status) && WIN_SUCCESS(CreateStatus)) ||
  7085. (WIN_SUCCESS(Status) && !BooleanFlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN))) {
  7086. //
  7087. // Open the staging directory.
  7088. //
  7089. StageHandle = CreateFile(Node->Stage,
  7090. GENERIC_WRITE | WRITE_DAC | FILE_READ_ATTRIBUTES | FILE_TRAVERSE,
  7091. FILE_SHARE_READ,
  7092. NULL,
  7093. OPEN_EXISTING,
  7094. FILE_FLAG_BACKUP_SEMANTICS,
  7095. NULL);
  7096. if (!HANDLE_IS_VALID(StageHandle)) {
  7097. Status = GetLastError();
  7098. DPRINT1_WS(0, ":DS: WARN - CreateFile(%ws);", Node->Stage, Status);
  7099. } else {
  7100. Status = FrsRestrictAccessToFileOrDirectory(Node->Stage, StageHandle,
  7101. FALSE, // do not inherit acls from parent.
  7102. FALSE);// do not push acls to children.
  7103. DPRINT1_WS(0, ":DS: WARN - FrsRestrictAccessToFileOrDirectory(%ws) (IGNORED)", Node->Stage, Status);
  7104. FRS_CLOSE(StageHandle);
  7105. //
  7106. // Mark the staging dir hidden.
  7107. //
  7108. if (!SetFileAttributes(Node->Stage,
  7109. FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN)) {
  7110. Status = GetLastError();
  7111. DPRINT1_WS(0, ":DS: ERROR - Can't set attrs on staging dir %ws;", Node->Stage, Status);
  7112. }
  7113. }
  7114. }
  7115. //
  7116. // Add the subscriber to the subscriber table.
  7117. //
  7118. // ConflictingNodeEntry = GTabInsertUniqueEntry(SubscriberTable, Node, Node->MemberDn, Node->Root);
  7119. ConflictingNodeEntry = GTabInsertUniqueEntry(SubscriberTable, Node, Node->MemberDn, NULL);
  7120. if (ConflictingNodeEntry) {
  7121. ConflictingNode = ConflictingNodeEntry->Data;
  7122. FrsDsResolveSubscriberConflict(ConflictingNode, Node, &Winner, &Loser);
  7123. if (WSTR_EQ(Winner->Dn, Node->Dn)) {
  7124. //
  7125. // The new one is the winner. Remove old one and insert new one.
  7126. //
  7127. GTabDelete(SubscriberTable,ConflictingNodeEntry->Key1,ConflictingNodeEntry->Key2,NULL);
  7128. GTabInsertUniqueEntry(SubscriberTable, Node, Node->MemberDn, Node->Root);
  7129. FrsFreeType(ConflictingNode);
  7130. } else {
  7131. //
  7132. // The old one is the winner. Leave it in the table.
  7133. //
  7134. FrsFreeType(Node);
  7135. continue;
  7136. }
  7137. }
  7138. //
  7139. // Link into config and add to the running checksum
  7140. //
  7141. FrsDsTreeLink(Parent, Node);
  7142. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeSubscriber", Node);
  7143. }
  7144. FrsDsLdapSearchClose(&FrsSearchContext);
  7145. return WStatus;
  7146. }
  7147. DWORD
  7148. FrsDsGetSubscriptions(
  7149. IN PLDAP Ldap,
  7150. IN PWCHAR ComputerDn,
  7151. IN PCONFIG_NODE Parent
  7152. )
  7153. /*++
  7154. Routine Description:
  7155. Recursively scan the DS tree beginning at computer
  7156. Part of NewDs poll APIs.
  7157. Arguments:
  7158. Ldap - opened and bound ldap connection
  7159. DefaultNc - default naming context
  7160. Parent - parent node
  7161. Return Value:
  7162. WIN32 Status
  7163. --*/
  7164. {
  7165. #undef DEBSUB
  7166. #define DEBSUB "FrsDsGetSubscriptions:"
  7167. PWCHAR Attrs[6];
  7168. PLDAPMessage LdapMsg = NULL;
  7169. PLDAPMessage LdapEntry;
  7170. PCONFIG_NODE Node;
  7171. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  7172. DWORD WStatus = ERROR_SUCCESS;
  7173. //
  7174. // Search the DS beginning at Base for entries of (objectCategory=nTFRSSubscriptions)
  7175. //
  7176. MK_ATTRS_5(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED, ATTR_WORKING);
  7177. if (!FrsDsLdapSearchInit(Ldap, ComputerDn, LDAP_SCOPE_SUBTREE, CATEGORY_SUBSCRIPTIONS,
  7178. Attrs, 0, &FrsSearchContext)) {
  7179. return ERROR_ACCESS_DENIED;
  7180. }
  7181. if (FrsSearchContext.EntriesInPage == 0) {
  7182. DPRINT1(0, ":DS: No NTFRSSubscriptions object found under %ws!.\n", ComputerDn);
  7183. }
  7184. //
  7185. // Scan the entries returned from ldap_search
  7186. //
  7187. for (LdapEntry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  7188. LdapEntry != NULL && WIN_SUCCESS(WStatus);
  7189. LdapEntry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  7190. //
  7191. // Basic node info (guid, name, dn, schedule, and usnchanged)
  7192. //
  7193. Node = FrsDsAllocBasicNode(Ldap, LdapEntry, CONFIG_TYPE_SUBSCRIPTIONS);
  7194. if (!Node) {
  7195. DPRINT(4, ":DS: Subscriptions lacks basic info; skipping\n");
  7196. continue;
  7197. }
  7198. //
  7199. // Working Directory
  7200. //
  7201. Node->Working = FrsDsFindValue(Ldap, LdapEntry, ATTR_WORKING);
  7202. //
  7203. // Link into config and add to the running checksum
  7204. //
  7205. FrsDsTreeLink(Parent, Node);
  7206. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeSubscription", Node);
  7207. //
  7208. // Recurse to the next level in the DS hierarchy
  7209. //
  7210. WStatus = FrsDsGetSubscribers(Ldap, Node->Dn, Node);
  7211. }
  7212. FrsDsLdapSearchClose(&FrsSearchContext);
  7213. return WStatus;
  7214. }
  7215. VOID
  7216. FrsDsAddLdapMod(
  7217. IN PWCHAR AttrType,
  7218. IN PWCHAR AttrValue,
  7219. IN OUT LDAPMod ***pppMod
  7220. )
  7221. /*++
  7222. Routine Description:
  7223. Add an attribute (plus values) to a structure that will eventually be
  7224. used in an ldap_add_s() function to add an object to the DS. The null-
  7225. terminated array referenced by pppMod grows with each call to this
  7226. routine. The array is freed by the caller using FrsDsFreeLdapMod().
  7227. Arguments:
  7228. AttrType - The object class of the object.
  7229. AttrValue - The value of the attribute.
  7230. pppMod - Address of an array of pointers to "attributes". Don't
  7231. give me that look -- this is an LDAP thing.
  7232. Return Value:
  7233. The pppMod array grows by one entry. The caller must free it with
  7234. FrsDsFreeLdapMod().
  7235. --*/
  7236. {
  7237. DWORD NumMod; // Number of entries in the Mod array
  7238. LDAPMod **ppMod; // Address of the first entry in the Mod array
  7239. LDAPMod *Attr; // An attribute structure
  7240. PWCHAR *Values; // An array of pointers to bervals
  7241. if (AttrValue == NULL)
  7242. return;
  7243. //
  7244. // The null-terminated array doesn't exist; create it
  7245. //
  7246. if (*pppMod == NULL) {
  7247. *pppMod = (LDAPMod **)FrsAlloc(sizeof (*pppMod));
  7248. **pppMod = NULL;
  7249. }
  7250. //
  7251. // Increase the array's size by 1
  7252. //
  7253. for (ppMod = *pppMod, NumMod = 2; *ppMod != NULL; ++ppMod, ++NumMod);
  7254. *pppMod = (LDAPMod **)FrsRealloc(*pppMod, sizeof (*pppMod) * NumMod);
  7255. //
  7256. // Add the new attribute + value to the Mod array
  7257. //
  7258. Values = (PWCHAR *)FrsAlloc(sizeof (PWCHAR) * 2);
  7259. Values[0] = FrsWcsDup(AttrValue);
  7260. Values[1] = NULL;
  7261. Attr = (LDAPMod *)FrsAlloc(sizeof (*Attr));
  7262. Attr->mod_values = Values;
  7263. Attr->mod_type = FrsWcsDup(AttrType);
  7264. Attr->mod_op = LDAP_MOD_ADD;
  7265. (*pppMod)[NumMod - 1] = NULL;
  7266. (*pppMod)[NumMod - 2] = Attr;
  7267. }
  7268. VOID
  7269. FrsDsAddLdapBerMod(
  7270. IN PWCHAR AttrType,
  7271. IN PCHAR AttrValue,
  7272. IN DWORD AttrValueLen,
  7273. IN OUT LDAPMod ***pppMod
  7274. )
  7275. /*++
  7276. Routine Description:
  7277. Add an attribute (plus values) to a structure that will eventually be
  7278. used in an ldap_add() function to add an object to the DS. The null-
  7279. terminated array referenced by pppMod grows with each call to this
  7280. routine. The array is freed by the caller using FrsDsFreeLdapMod().
  7281. Arguments:
  7282. AttrType - The object class of the object.
  7283. AttrValue - The value of the attribute.
  7284. AttrValueLen - length of the attribute
  7285. pppMod - Address of an array of pointers to "attributes". Don't
  7286. give me that look -- this is an LDAP thing.
  7287. Return Value:
  7288. The pppMod array grows by one entry. The caller must free it with
  7289. FrsDsFreeLdapMod().
  7290. --*/
  7291. {
  7292. DWORD NumMod; // Number of entries in the Mod array
  7293. LDAPMod **ppMod; // Address of the first entry in the Mod array
  7294. LDAPMod *Attr; // An attribute structure
  7295. PLDAP_BERVAL Berval;
  7296. PLDAP_BERVAL *Values; // An array of pointers to bervals
  7297. if (AttrValue == NULL)
  7298. return;
  7299. //
  7300. // The null-terminated array doesn't exist; create it
  7301. //
  7302. if (*pppMod == NULL) {
  7303. *pppMod = (LDAPMod **)FrsAlloc(sizeof (*pppMod));
  7304. **pppMod = NULL;
  7305. }
  7306. //
  7307. // Increase the array's size by 1
  7308. //
  7309. for (ppMod = *pppMod, NumMod = 2; *ppMod != NULL; ++ppMod, ++NumMod);
  7310. *pppMod = (LDAPMod **)FrsRealloc(*pppMod, sizeof (*pppMod) * NumMod);
  7311. //
  7312. // Construct a berval
  7313. //
  7314. Berval = (PLDAP_BERVAL)FrsAlloc(sizeof(LDAP_BERVAL));
  7315. Berval->bv_len = AttrValueLen;
  7316. Berval->bv_val = (PCHAR)FrsAlloc(AttrValueLen);
  7317. CopyMemory(Berval->bv_val, AttrValue, AttrValueLen);
  7318. //
  7319. // Add the new attribute + value to the Mod array
  7320. //
  7321. Values = (PLDAP_BERVAL *)FrsAlloc(sizeof (PLDAP_BERVAL) * 2);
  7322. Values[0] = Berval;
  7323. Values[1] = NULL;
  7324. Attr = (LDAPMod *)FrsAlloc(sizeof (*Attr));
  7325. Attr->mod_bvalues = Values;
  7326. Attr->mod_type = FrsWcsDup(AttrType);
  7327. Attr->mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  7328. (*pppMod)[NumMod - 1] = NULL;
  7329. (*pppMod)[NumMod - 2] = Attr;
  7330. }
  7331. VOID
  7332. FrsDsFreeLdapMod(
  7333. IN OUT LDAPMod ***pppMod
  7334. )
  7335. /*++
  7336. Routine Description:
  7337. Free the structure built by successive calls to FrsDsAddLdapMod().
  7338. Arguments:
  7339. pppMod - Address of a null-terminated array.
  7340. Return Value:
  7341. *pppMod set to NULL.
  7342. --*/
  7343. {
  7344. DWORD i, j;
  7345. LDAPMod **ppMod;
  7346. if (!pppMod || !*pppMod) {
  7347. return;
  7348. }
  7349. //
  7350. // For each attibute
  7351. //
  7352. ppMod = *pppMod;
  7353. for (i = 0; ppMod[i] != NULL; ++i) {
  7354. //
  7355. // For each value of the attribute
  7356. //
  7357. for (j = 0; (ppMod[i])->mod_values[j] != NULL; ++j) {
  7358. //
  7359. // Free the value
  7360. //
  7361. if (ppMod[i]->mod_op & LDAP_MOD_BVALUES) {
  7362. FrsFree(ppMod[i]->mod_bvalues[j]->bv_val);
  7363. }
  7364. FrsFree((ppMod[i])->mod_values[j]);
  7365. }
  7366. FrsFree((ppMod[i])->mod_values); // Free the array of pointers to values
  7367. FrsFree((ppMod[i])->mod_type); // Free the string identifying the attribute
  7368. FrsFree(ppMod[i]); // Free the attribute
  7369. }
  7370. FrsFree(ppMod); // Free the array of pointers to attributes
  7371. *pppMod = NULL; // Now ready for more calls to FrsDsAddLdapMod()
  7372. }
  7373. PWCHAR
  7374. FrsDsConvertToSettingsDn(
  7375. IN PWCHAR Dn
  7376. )
  7377. /*++
  7378. Routine Description:
  7379. Insure this Dn is for the server's settings and not the server and
  7380. that the Dn is in lower case for any call to wcsstr().
  7381. Arguments:
  7382. Dn - Server or settings dn
  7383. Return Value:
  7384. Settings Dn
  7385. --*/
  7386. {
  7387. #undef DEBSUB
  7388. #define DEBSUB "FrsDsConvertToSettingsDn:"
  7389. PWCHAR SettingsDn;
  7390. DPRINT1(4, ":DS: Begin settings Dn: %ws\n", Dn);
  7391. //
  7392. // No settings; done
  7393. //
  7394. if (!Dn) {
  7395. return Dn;
  7396. }
  7397. //
  7398. // Lower case for wcsstr
  7399. //
  7400. FRS_WCSLWR(Dn);
  7401. if (wcsstr(Dn, CN_NTDS_SETTINGS)) {
  7402. DPRINT1(4, ":DS: End settings Dn: %ws\n", Dn);
  7403. return Dn;
  7404. }
  7405. SettingsDn = FrsDsExtendDn(Dn, CN_NTDS_SETTINGS);
  7406. FRS_WCSLWR(SettingsDn);
  7407. FrsFree(Dn);
  7408. DPRINT1(4, ":DS: End settings Dn: %ws\n", SettingsDn);
  7409. return SettingsDn;
  7410. }
  7411. DWORD
  7412. FrsDsFindComputer(
  7413. IN PLDAP Ldap,
  7414. IN PWCHAR FindDn,
  7415. IN PWCHAR ObjectCategory,
  7416. IN ULONG Scope,
  7417. OUT PCONFIG_NODE *Computer
  7418. )
  7419. /*++
  7420. Routine Description:
  7421. Find *one* computer object for this computer.
  7422. Then look for a subscriptons object and subscriber objects. A
  7423. DS configuration node is allocated for each object found. They are linked
  7424. together and the root of the "computer tree" is returned in Computer.
  7425. Part of NewDs poll APIs.
  7426. Arguments:
  7427. Ldap - opened and bound ldap connection
  7428. FindDn - Base Dn for search
  7429. ObjectCategory - Object class (computer or user)
  7430. A user object serves the same purpose as the computer
  7431. object *sometimes* following a NT4 to NT5 upgrade.
  7432. Scope - Scope of search (currently BASE or SUBTREE)
  7433. Computer - returned computer subtree
  7434. Return Value:
  7435. WIN32 Status
  7436. --*/
  7437. {
  7438. #undef DEBSUB
  7439. #define DEBSUB "FrsDsFindComputer:"
  7440. PLDAPMessage LdapEntry;
  7441. PCONFIG_NODE Node;
  7442. PWCHAR Attrs[8];
  7443. WCHAR Filter[MAX_PATH + 1];
  7444. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  7445. DWORD WStatus = ERROR_SUCCESS;
  7446. *Computer = NULL;
  7447. //
  7448. // Initialize the SubscriberTable.
  7449. //
  7450. if (SubscriberTable != NULL) {
  7451. SubscriberTable = GTabFreeTable(SubscriberTable,NULL);
  7452. }
  7453. SubscriberTable = GTabAllocStringTable();
  7454. //
  7455. // Filter that locates our computer object
  7456. //
  7457. swprintf(Filter, L"(&%s(sAMAccountName=%s$))", ObjectCategory, ComputerName);
  7458. //
  7459. // Search the DS beginning at Base for the entries of class "Filter"
  7460. //
  7461. MK_ATTRS_7(Attrs, ATTR_OBJECT_GUID, ATTR_DN, ATTR_SCHEDULE, ATTR_USN_CHANGED,
  7462. ATTR_SERVER_REF, ATTR_SERVER_REF_BL, ATTR_DNS_HOST_NAME);
  7463. //
  7464. // Note: Is it safe to turn off referrals re: back links?
  7465. // if so, use ldap_get/set_option in winldap.h
  7466. //
  7467. if (!FrsDsLdapSearchInit(Ldap, FindDn, Scope, Filter, Attrs, 0, &FrsSearchContext)) {
  7468. return ERROR_ACCESS_DENIED;
  7469. }
  7470. if (FrsSearchContext.EntriesInPage == 0) {
  7471. DPRINT1(0, ":DS: WARN - There is no computer object in %ws!\n", FindDn);
  7472. }
  7473. //
  7474. // Scan the entries returned from ldap_search
  7475. //
  7476. for (LdapEntry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext);
  7477. LdapEntry != NULL && WIN_SUCCESS(WStatus);
  7478. LdapEntry = FrsDsLdapSearchNext(Ldap, &FrsSearchContext)) {
  7479. //
  7480. // Basic node info (guid, name, dn, schedule, and usnchanged)
  7481. //
  7482. Node = FrsDsAllocBasicNode(Ldap, LdapEntry, CONFIG_TYPE_COMPUTER);
  7483. if (!Node) {
  7484. DPRINT(0, ":DS: Computer lacks basic info; skipping\n");
  7485. continue;
  7486. }
  7487. DPRINT1(2, ":DS: Computer FQDN is %ws\n", Node->Dn);
  7488. //
  7489. // DNS name
  7490. //
  7491. Node->DnsName = FrsDsFindValue(Ldap, LdapEntry, ATTR_DNS_HOST_NAME);
  7492. DPRINT1(2, ":DS: Computer's dns name is %ws\n", Node->DnsName);
  7493. //
  7494. // Server reference
  7495. //
  7496. Node->SettingsDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_SERVER_REF_BL);
  7497. if (!Node->SettingsDn) {
  7498. Node->SettingsDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_SERVER_REF);
  7499. }
  7500. //
  7501. // Make sure it references the settings; not the server
  7502. //
  7503. Node->SettingsDn = FrsDsConvertToSettingsDn(Node->SettingsDn);
  7504. DPRINT1(2, ":DS: Settings reference is %ws\n", Node->SettingsDn);
  7505. //
  7506. // Link into config
  7507. //
  7508. Node->Peer = *Computer;
  7509. *Computer = Node;
  7510. FRS_PRINT_TYPE_DEBSUB(5, ":DS: NodeComputer", Node);
  7511. //
  7512. // Recurse to the next level in the DS hierarchy iff this
  7513. // computer is a member of some replica set
  7514. //
  7515. WStatus = FrsDsGetSubscriptions(Ldap, Node->Dn, Node);
  7516. }
  7517. FrsDsLdapSearchClose(&FrsSearchContext);
  7518. //
  7519. // There should only be one computer object with the indicated
  7520. // SAM account name. Otherwise, we are unable to authenticate
  7521. // properly. And it goes against the DS architecture.
  7522. //
  7523. if (WIN_SUCCESS(WStatus) && *Computer && (*Computer)->Peer) {
  7524. DPRINT(0, ":DS: ERROR - There is more than one computer object!\n");
  7525. WStatus = ERROR_INVALID_PARAMETER;
  7526. }
  7527. //
  7528. // Must have a computer
  7529. //
  7530. if (WIN_SUCCESS(WStatus) && !*Computer) {
  7531. DPRINT1(0, ":DS: WARN - There is no computer object in %ws!\n", FindDn);
  7532. WStatus = ERROR_INVALID_PARAMETER;
  7533. }
  7534. return WStatus;
  7535. }
  7536. DWORD
  7537. FrsDsGetComputer(
  7538. IN PLDAP Ldap,
  7539. OUT PCONFIG_NODE *Computer
  7540. )
  7541. /*++
  7542. Routine Description:
  7543. Look in the Domain naming Context for our computer object.
  7544. Historically we did a deep search for an object with the sam account
  7545. name of our computer (SAM Account name is the netbios name with a $ appended).
  7546. That was expensive so before doing that we first look in the Domain
  7547. Controller container followed by a search of the Computer Container.
  7548. Then the DS guys came up with an API for the preferred way of doing this.
  7549. First call GetComputerObjectName() to get the Fully Qualified Distinguished
  7550. Name (FQDN) for the computer then use that in an LDAP search query (via
  7551. FrsDsFindComputer()). We only fall back on the full search when the
  7552. call to GetComputerObjectName() fails.
  7553. Part of NewDs poll APIs.
  7554. Arguments:
  7555. Ldap - opened and bound ldap connection
  7556. Computer - returned computer subtree
  7557. Return Value:
  7558. WIN32 Status
  7559. --*/
  7560. {
  7561. #undef DEBSUB
  7562. #define DEBSUB "FrsDsGetComputer:"
  7563. WCHAR CompFqdn[MAX_PATH + 1];
  7564. DWORD CompFqdnLen;
  7565. DWORD WStatus = ERROR_SUCCESS;
  7566. //
  7567. // Initialize return value
  7568. //
  7569. *Computer = NULL;
  7570. //
  7571. // Assume success
  7572. //
  7573. WStatus = ERROR_SUCCESS;
  7574. //
  7575. // Use computer's cached fully qualified Dn. This avoids repeated calls
  7576. // to GetComputerObjectName() which wants to rebind to the DS on each call.
  7577. // (it should have taken a binding handle as an arg).
  7578. //
  7579. if (ComputerCachedFqdn) {
  7580. DPRINT1(5, ":DS: ComputerCachedFqdn is %ws\n", ComputerCachedFqdn);
  7581. WStatus = FrsDsFindComputer(Ldap, ComputerCachedFqdn, CATEGORY_ANY,
  7582. LDAP_SCOPE_BASE, Computer);
  7583. if (*Computer) {
  7584. goto CLEANUP;
  7585. }
  7586. DPRINT2(1, ":DS: WARN - Could not find computer in Cachedfqdn %ws; WStatus %s\n",
  7587. ComputerCachedFqdn, ErrLabelW32(WStatus));
  7588. ComputerCachedFqdn = FrsFree(ComputerCachedFqdn);
  7589. }
  7590. //
  7591. // Retrieve the computer's fully qualified Dn
  7592. //
  7593. // NTRAID#70731-2000/03/29-sudarc (Call GetComputerObjectName() from a
  7594. // separate thread so that it does not hang the
  7595. // DS polling thread.)
  7596. //
  7597. // *Note*:
  7598. // The following call to GetComputerObjectName() can hang if the DS
  7599. // hangs. See bug 351139 for an example caused by a bug in another
  7600. // component. One way to protect ourself is to issue this call
  7601. // in its own thread. Then after a timeout period call RpcCancelThread()
  7602. // on the thread.
  7603. //
  7604. CompFqdnLen = MAX_PATH;
  7605. if (GetComputerObjectName(NameFullyQualifiedDN, CompFqdn, &CompFqdnLen)) {
  7606. DPRINT1(4, ":DS: ComputerFqdn is %ws\n", CompFqdn);
  7607. //
  7608. // Use CATEGORY_ANY in the search below because an NT4 to NT5 upgrade
  7609. // could result in the object type for the "computer object" to really
  7610. // be a USER object. So the FQDN above could resolve to a Computer
  7611. // or a User object.
  7612. //
  7613. WStatus = FrsDsFindComputer(Ldap, CompFqdn, CATEGORY_ANY,
  7614. LDAP_SCOPE_BASE, Computer);
  7615. if (*Computer == NULL) {
  7616. DPRINT2(1, ":DS: WARN - Could not find computer in fqdn %ws; WStatus %s\n",
  7617. CompFqdn, ErrLabelW32(WStatus));
  7618. } else {
  7619. //
  7620. // Found our computer object; refresh the cached fqdn.
  7621. //
  7622. FrsFree(ComputerCachedFqdn);
  7623. ComputerCachedFqdn = FrsWcsDup(CompFqdn);
  7624. }
  7625. //
  7626. // We got the fully qualified Dn so we are done. It should have
  7627. // given us a computer object but even if it didn't we won't find it
  7628. // anywhere else.
  7629. //
  7630. goto CLEANUP;
  7631. }
  7632. DPRINT3(1, ":DS: WARN - GetComputerObjectName(%ws); Len %d, WStatus %s\n",
  7633. ComputerName, CompFqdnLen, ErrLabelW32(GetLastError()));
  7634. //
  7635. // FQDN lookup failed so fall back on search of well known containers.
  7636. // First Look in domain controllers container.
  7637. //
  7638. if (DomainControllersDn) {
  7639. WStatus = FrsDsFindComputer(Ldap, DomainControllersDn, CATEGORY_COMPUTER,
  7640. LDAP_SCOPE_SUBTREE, Computer);
  7641. if (*Computer != NULL) {
  7642. goto CLEANUP;
  7643. }
  7644. DPRINT2(1, ":DS: WARN - Could not find computer in dc's %ws; WStatus %s\n",
  7645. DomainControllersDn, ErrLabelW32(WStatus));
  7646. }
  7647. //
  7648. // Look in computer container
  7649. //
  7650. if (ComputersDn) {
  7651. WStatus = FrsDsFindComputer(Ldap, ComputersDn, CATEGORY_COMPUTER,
  7652. LDAP_SCOPE_SUBTREE, Computer);
  7653. if (*Computer != NULL) {
  7654. goto CLEANUP;
  7655. }
  7656. DPRINT2(1, ":DS: WARN - Could not find computer in computers %ws; WStatus %s\n",
  7657. ComputersDn, ErrLabelW32(WStatus));
  7658. }
  7659. //
  7660. // Do a deep search of the default naming context (EXPENSIVE!)
  7661. //
  7662. if (DefaultNcDn) {
  7663. WStatus = FrsDsFindComputer(Ldap, DefaultNcDn, CATEGORY_COMPUTER,
  7664. LDAP_SCOPE_SUBTREE, Computer);
  7665. if (*Computer != NULL) {
  7666. goto CLEANUP;
  7667. }
  7668. DPRINT2(1, ":DS: WARN - Could not find computer in defaultnc %ws; WStatus %s\n",
  7669. DefaultNcDn, ErrLabelW32(WStatus));
  7670. }
  7671. //
  7672. // Getting desperate. Try looking for a user object because an
  7673. // NT4 to NT5 upgrade will sometimes leave the objectCategory
  7674. // as user on the computer object.
  7675. //
  7676. if (DefaultNcDn) {
  7677. WStatus = FrsDsFindComputer(Ldap, DefaultNcDn, CATEGORY_USER,
  7678. LDAP_SCOPE_SUBTREE, Computer);
  7679. if (*Computer != NULL) {
  7680. goto CLEANUP;
  7681. }
  7682. DPRINT2(1, ":DS: WARN - Could not find computer in defaultnc USER %ws; WStatus %s\n",
  7683. DefaultNcDn, ErrLabelW32(WStatus));
  7684. }
  7685. CLEANUP:
  7686. return WStatus;
  7687. }
  7688. DWORD
  7689. FrsDsDeleteSubTree(
  7690. IN PLDAP Ldap,
  7691. IN PWCHAR Dn
  7692. )
  7693. /*++
  7694. Routine Description:
  7695. Delete a DS subtree, including Dn
  7696. Arguments:
  7697. None.
  7698. Return Value:
  7699. None.
  7700. --*/
  7701. {
  7702. #undef DEBSUB
  7703. #define DEBSUB "FrsDsDeleteSubTree:"
  7704. DWORD LStatus;
  7705. PWCHAR Attrs[2];
  7706. PWCHAR NextDn;
  7707. PLDAPMessage LdapMsg = NULL;
  7708. PLDAPMessage LdapEntry = NULL;
  7709. MK_ATTRS_1(Attrs, ATTR_DN);
  7710. LStatus = ldap_search_ext_s(Ldap,
  7711. Dn,
  7712. LDAP_SCOPE_ONELEVEL,
  7713. CATEGORY_ANY,
  7714. Attrs,
  7715. 0,
  7716. NULL,
  7717. NULL,
  7718. &LdapTimeout,
  7719. 0,
  7720. &LdapMsg);
  7721. if (LStatus != LDAP_NO_SUCH_OBJECT) {
  7722. CLEANUP1_LS(4, ":DS: Can't search %ws;", Dn, LStatus, CLEANUP);
  7723. }
  7724. LStatus = LDAP_SUCCESS;
  7725. //
  7726. // Scan the entries returned from ldap_search
  7727. //
  7728. for (LdapEntry = ldap_first_entry(Ldap, LdapMsg);
  7729. LdapEntry != NULL && LStatus == LDAP_SUCCESS;
  7730. LdapEntry = ldap_next_entry(Ldap, LdapEntry)) {
  7731. NextDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_DN);
  7732. LStatus = FrsDsDeleteSubTree(Ldap, NextDn);
  7733. FrsFree(NextDn);
  7734. }
  7735. if (LStatus != LDAP_SUCCESS) {
  7736. goto CLEANUP;
  7737. }
  7738. LStatus = ldap_delete_s(Ldap, Dn);
  7739. if (LStatus != LDAP_NO_SUCH_OBJECT) {
  7740. CLEANUP1_LS(4, ":DS: Can't delete %ws;", Dn, LStatus, CLEANUP);
  7741. }
  7742. //
  7743. // SUCCESS
  7744. //
  7745. LStatus = LDAP_SUCCESS;
  7746. CLEANUP:
  7747. LDAP_FREE_MSG(LdapMsg);
  7748. return LStatus;
  7749. }
  7750. BOOL
  7751. FrsDsDeleteIfEmpty(
  7752. IN PLDAP Ldap,
  7753. IN PWCHAR Dn
  7754. )
  7755. /*++
  7756. Routine Description:
  7757. Delete the Dn if it is an empty container
  7758. Arguments:
  7759. Ldap
  7760. Dn
  7761. Return Value:
  7762. TRUE - Not empty or empty and deleted
  7763. FALSE - Can't search or can't delete
  7764. --*/
  7765. {
  7766. #undef DEBSUB
  7767. #define DEBSUB "FrsDsDeleteIfEmpty:"
  7768. DWORD LStatus;
  7769. PWCHAR Attrs[2];
  7770. PLDAPMessage LdapMsg = NULL;
  7771. MK_ATTRS_1(Attrs, ATTR_DN);
  7772. LStatus = ldap_search_ext_s(Ldap,
  7773. Dn,
  7774. LDAP_SCOPE_ONELEVEL,
  7775. CATEGORY_ANY,
  7776. Attrs,
  7777. 0,
  7778. NULL,
  7779. NULL,
  7780. &LdapTimeout,
  7781. 0,
  7782. &LdapMsg);
  7783. if (LStatus == LDAP_SUCCESS) {
  7784. //
  7785. // If there are any entries under this Dn then we don't want to
  7786. // delete it.
  7787. //
  7788. if (ldap_count_entries(Ldap, LdapMsg) > 0) {
  7789. LDAP_FREE_MSG(LdapMsg);
  7790. return TRUE;
  7791. }
  7792. LDAP_FREE_MSG(LdapMsg);
  7793. LStatus = ldap_delete_s(Ldap, Dn);
  7794. if (LStatus != LDAP_NO_SUCH_OBJECT) {
  7795. CLEANUP1_LS(4, ":DS: Can't delete %ws;", Dn, LStatus, CLEANUP);
  7796. }
  7797. } else if (LStatus != LDAP_NO_SUCH_OBJECT) {
  7798. DPRINT1_LS(4, ":DS: Can't search %ws;", Dn, LStatus);
  7799. LDAP_FREE_MSG(LdapMsg);
  7800. return FALSE;
  7801. } else {
  7802. //
  7803. // ldap_search can return failure but still allocated the LdapMsg buffer.
  7804. //
  7805. LDAP_FREE_MSG(LdapMsg);
  7806. }
  7807. return TRUE;
  7808. CLEANUP:
  7809. return FALSE;
  7810. }
  7811. BOOL
  7812. FrsDsEnumerateSysVolKeys(
  7813. IN PLDAP Ldap,
  7814. IN DWORD Command,
  7815. IN PWCHAR ServicesDn,
  7816. IN PWCHAR SystemDn,
  7817. IN PCONFIG_NODE Computer,
  7818. OUT BOOL *RefetchComputer
  7819. )
  7820. /*++
  7821. Routine Description:
  7822. Scan the sysvol registry keys and process them according to Command.
  7823. REGCMD_CREATE_PRIMARY_DOMAIN - Create domain wide objects
  7824. REGCMD_CREATE_MEMBERS - Create members + subscribers
  7825. REGCMD_DELETE_MEMBERS - delete members + subscribers
  7826. REGCMD_DELETE_KEYS - Done; delete all keys
  7827. Arguments:
  7828. Ldap
  7829. HKey
  7830. Command
  7831. ServicesDn
  7832. SystemDn
  7833. Computer
  7834. RefetchComputer - Objects were altered in the DS, refetch DS info
  7835. Return Value:
  7836. TRUE - No problems
  7837. FALSE - Stop processing the registry keys
  7838. --*/
  7839. {
  7840. #undef DEBSUB
  7841. #define DEBSUB "FrsDsEnumerateSysVolKeys:"
  7842. GUID Guid;
  7843. DWORD WStatus;
  7844. DWORD LStatus;
  7845. ULONG Index;
  7846. BOOL OldNaming;
  7847. BOOL RetStatus;
  7848. HKEY HSeedingsKey = INVALID_HANDLE_VALUE;
  7849. HKEY HKey = INVALID_HANDLE_VALUE;
  7850. LDAPMod **LdapMod = NULL;
  7851. PWCHAR SettingsDn = NULL;
  7852. PWCHAR SystemSettingsDn = NULL;
  7853. PWCHAR SetDn = NULL;
  7854. PWCHAR SystemSetDn = NULL;
  7855. PWCHAR SubsDn = NULL;
  7856. PWCHAR SubDn = NULL;
  7857. PWCHAR SystemSubDn = NULL;
  7858. PWCHAR MemberDn = NULL;
  7859. PWCHAR SystemMemberDn = NULL;
  7860. PWCHAR FileFilterList = NULL;
  7861. PWCHAR DirFilterList = NULL;
  7862. PWCHAR ReplicaSetCommand = NULL;
  7863. PWCHAR ReplicaSetName = NULL;
  7864. PWCHAR ReplicaSetParent = NULL;
  7865. PWCHAR ReplicaSetType = NULL;
  7866. PWCHAR ReplicationRootPath = NULL;
  7867. PWCHAR PrintableRealRoot = NULL;
  7868. PWCHAR SubstituteRealRoot = NULL;
  7869. PWCHAR ReplicationStagePath = NULL;
  7870. PWCHAR PrintableRealStage = NULL;
  7871. PWCHAR SubstituteRealStage = NULL;
  7872. DWORD ReplicaSetPrimary;
  7873. WCHAR RegBuf[MAX_PATH + 1];
  7874. //
  7875. // Open the system volume replica sets key.
  7876. // FRS_CONFIG_SECTION\SysVol
  7877. //
  7878. WStatus = CfgRegOpenKey(FKC_SYSVOL_SECTION_KEY, NULL, 0, &HKey);
  7879. if (!WIN_SUCCESS(WStatus)) {
  7880. DPRINT_WS(4, ":DS: WARN - Cannot open sysvol key.", WStatus);
  7881. return FALSE;
  7882. }
  7883. //
  7884. // ENUMERATE SYSVOL SUBKEYS
  7885. //
  7886. RetStatus = TRUE;
  7887. Index = 0;
  7888. while (RetStatus) {
  7889. WStatus = RegEnumKey(HKey, Index, RegBuf, MAX_PATH + 1);
  7890. if (WStatus == ERROR_NO_MORE_ITEMS) {
  7891. break;
  7892. }
  7893. if (!WIN_SUCCESS(WStatus)) {
  7894. DPRINT_WS(0, ":DS: ERROR - enumerating sysvol keys;", WStatus);
  7895. RetStatus = FALSE;
  7896. break;
  7897. }
  7898. //
  7899. // Delete the registry key
  7900. //
  7901. if (Command == REGCMD_DELETE_KEYS) {
  7902. WStatus = RegDeleteKey(HKey, RegBuf);
  7903. if (!WIN_SUCCESS(WStatus)) {
  7904. DPRINT1_WS(0, ":DS: ERROR - Cannot delete registry key %ws;",
  7905. RegBuf, WStatus);
  7906. RetStatus = FALSE;
  7907. break;
  7908. }
  7909. continue;
  7910. }
  7911. //
  7912. // Open the subkey
  7913. //
  7914. DPRINT1(4, ":DS: Processing SysVol Key: %ws\n", RegBuf);
  7915. //
  7916. // The registry will be updated with the LDAP error code
  7917. //
  7918. LStatus = LDAP_OTHER;
  7919. //
  7920. // READ THE VALUES FROM THE SUBKEY
  7921. //
  7922. // SysVol\<RegBuf>\Replica Set Command
  7923. //
  7924. CfgRegReadString(FKC_SET_N_SYSVOL_COMMAND, RegBuf, 0, &ReplicaSetCommand);
  7925. if (!ReplicaSetCommand) {
  7926. DPRINT(0, ":DS: ERROR - no command; cannot process sysvol\n");
  7927. goto CONTINUE;
  7928. }
  7929. // SysVol\<Guid>\Replica Set Name
  7930. CfgRegReadString(FKC_SET_N_SYSVOL_NAME, RegBuf, 0, &ReplicaSetName);
  7931. if (!ReplicaSetName) {
  7932. DPRINT(4, ":DS: WARN - no name; using subkey name\n");
  7933. ReplicaSetName = FrsWcsDup(RegBuf);
  7934. }
  7935. //
  7936. // Construct Settings, Set, Member, Subscriptions, and Subscriber names
  7937. // (both old and new values)
  7938. //
  7939. SettingsDn = FrsDsExtendDn(ServicesDn, CN_SYSVOLS);
  7940. SystemSettingsDn = FrsDsExtendDn(SystemDn, CN_NTFRS_SETTINGS);
  7941. SetDn = FrsDsExtendDn(SettingsDn, ReplicaSetName);
  7942. SystemSetDn = FrsDsExtendDn(SystemSettingsDn, CN_DOMAIN_SYSVOL);
  7943. MemberDn = FrsDsExtendDn(SetDn, ComputerName);
  7944. SystemMemberDn = FrsDsExtendDn(SystemSetDn, ComputerName);
  7945. SubsDn = FrsDsExtendDn(Computer->Dn, CN_SUBSCRIPTIONS);
  7946. SubDn = FrsDsExtendDn(SubsDn, ReplicaSetName);
  7947. SystemSubDn = FrsDsExtendDn(SubsDn, CN_DOMAIN_SYSVOL);
  7948. //
  7949. // DELETE REPLICA SET
  7950. //
  7951. if (WSTR_EQ(ReplicaSetCommand, L"Delete")) {
  7952. //
  7953. // But only if we are processing deletes during this enumeration
  7954. //
  7955. // Delete what we can; ignore errors
  7956. //
  7957. //
  7958. // All the deletes are done in ntfrsapi.c when we commit demotion.
  7959. // This function is never called with Command = REGCMD_DELETE_MEMBERS
  7960. //
  7961. /*
  7962. if (Command == REGCMD_DELETE_MEMBERS) {
  7963. //
  7964. // DELETE MEMBER
  7965. //
  7966. //
  7967. // Old member name under services in enterprise wide partition
  7968. //
  7969. LStatus = FrsDsDeleteSubTree(Ldap, MemberDn);
  7970. if (LStatus == LDAP_SUCCESS) {*RefetchComputer = TRUE;}
  7971. DPRINT1_LS(4, ":DS: WARN - Can't delete sysvol %ws;", MemberDn, LStatus);
  7972. //
  7973. // New member name under System in domain wide partition
  7974. //
  7975. LStatus = FrsDsDeleteSubTree(Ldap, SystemMemberDn);
  7976. if (LStatus == LDAP_SUCCESS) {*RefetchComputer = TRUE;}
  7977. DPRINT1_LS(4, ":DS: WARN - Can't delete sysvol %ws;", SystemMemberDn, LStatus);
  7978. //
  7979. // DELETE SET
  7980. //
  7981. //
  7982. // Ignore errors; no real harm leaving the set
  7983. // and settings around.
  7984. //
  7985. if (!FrsDsDeleteIfEmpty(Ldap, SetDn)) {
  7986. DPRINT1(4, ":DS: WARN - Can't delete sysvol %ws\n", SetDn);
  7987. }
  7988. if (!FrsDsDeleteIfEmpty(Ldap, SystemSetDn)) {
  7989. DPRINT1(4, ":DS: WARN - Can't delete sysvol %ws\n", SystemSetDn);
  7990. }
  7991. //
  7992. // DELETE SETTINGS (don't delete new settings, there
  7993. // may be other settings beneath it (such as DFS settings))
  7994. //
  7995. if (!FrsDsDeleteIfEmpty(Ldap, SettingsDn)) {
  7996. DPRINT1(4, ":DS: WARN - Can't delete sysvol %ws\n", SettingsDn);
  7997. }
  7998. LStatus = FrsDsDeleteSubTree(Ldap, SubDn);
  7999. if (LStatus == LDAP_SUCCESS) {*RefetchComputer = TRUE;}
  8000. DPRINT1_LS(4, ":DS: WARN - Can't delete sysvol %ws;", SubDn, LStatus);
  8001. LStatus = FrsDsDeleteSubTree(Ldap, SystemSubDn);
  8002. if (LStatus == LDAP_SUCCESS) {*RefetchComputer = TRUE;}
  8003. DPRINT1_LS(4, ":DS: WARN - Can't delete sysvol %ws;", SystemSubDn, LStatus);
  8004. //
  8005. // Ignore errors; no real harm leaving the subscriptions
  8006. //
  8007. if (!FrsDsDeleteIfEmpty(Ldap, SubsDn)) {
  8008. DPRINT1(4, ":DS: WARN - Can't delete sysvol %ws\n", SubsDn);
  8009. }
  8010. }
  8011. */
  8012. LStatus = LDAP_SUCCESS;
  8013. goto CONTINUE;
  8014. }
  8015. //
  8016. // UNKNOWN COMMAND
  8017. //
  8018. else if (WSTR_NE(ReplicaSetCommand, L"Create")) {
  8019. DPRINT1(0, ":DS: ERROR - Don't understand sysvol command %ws; cannot process sysvol\n",
  8020. ReplicaSetCommand);
  8021. goto CONTINUE;
  8022. }
  8023. //
  8024. // CREATE
  8025. //
  8026. //
  8027. // Not processing creates this scan
  8028. //
  8029. if (Command != REGCMD_CREATE_PRIMARY_DOMAIN && Command != REGCMD_CREATE_MEMBERS) {
  8030. LStatus = LDAP_SUCCESS;
  8031. goto CONTINUE;
  8032. }
  8033. //
  8034. // Finish gathering the registry values for a Create
  8035. //
  8036. WStatus = CfgRegReadString(FKC_SET_N_SYSVOL_TYPE, RegBuf, 0, &ReplicaSetType);
  8037. CLEANUP_WS(0, ":DS: ERROR - no type; cannot process sysvol.", WStatus, CONTINUE);
  8038. WStatus = CfgRegReadDWord(FKC_SET_N_SYSVOL_PRIMARY, RegBuf, 0, &ReplicaSetPrimary);
  8039. CLEANUP_WS(0, ":DS: ERROR - no primary; cannot process sysvol.", WStatus, CONTINUE);
  8040. WStatus = CfgRegReadString(FKC_SET_N_SYSVOL_ROOT, RegBuf, 0, &ReplicationRootPath);
  8041. CLEANUP_WS(0, ":DS: ERROR - no root; cannot process sysvol.", WStatus, CONTINUE);
  8042. WStatus = CfgRegReadString(FKC_SET_N_SYSVOL_STAGE, RegBuf, 0, &ReplicationStagePath);
  8043. CLEANUP_WS(0, ":DS: ERROR - no stage; cannot process sysvol.", WStatus, CONTINUE);
  8044. WStatus = CfgRegReadString(FKC_SET_N_SYSVOL_PARENT, RegBuf, 0, &ReplicaSetParent);
  8045. DPRINT_WS(0, ":DS: WARN - no parent; cannot process seeding sysvol", WStatus);
  8046. if (Command == REGCMD_CREATE_PRIMARY_DOMAIN) {
  8047. //
  8048. // Not the primary domain sysvol
  8049. //
  8050. if (!ReplicaSetPrimary ||
  8051. WSTR_NE(ReplicaSetType, NTFRSAPI_REPLICA_SET_TYPE_DOMAIN)) {
  8052. LStatus = LDAP_SUCCESS;
  8053. goto CONTINUE;
  8054. }
  8055. //
  8056. // Domain wide Settings -- may already exist
  8057. //
  8058. FrsDsAddLdapMod(ATTR_CLASS, ATTR_NTFRS_SETTINGS, &LdapMod);
  8059. DPRINT1(4, ":DS: Creating Sysvol System Settings %ws\n", CN_NTFRS_SETTINGS);
  8060. LStatus = ldap_add_s(Ldap, SystemSettingsDn, LdapMod);
  8061. FrsDsFreeLdapMod(&LdapMod);
  8062. if (LStatus == LDAP_SUCCESS) {
  8063. *RefetchComputer = TRUE;
  8064. }
  8065. if (LStatus != LDAP_ALREADY_EXISTS && LStatus != LDAP_SUCCESS) {
  8066. DPRINT1_LS(0, ":DS: ERROR - Can't create %ws:", SystemSettingsDn, LStatus);
  8067. //
  8068. // May be an error like "Access Denied". As long as we
  8069. // can create objects under it; ignore errors. It should
  8070. // have been pre-created by default, anyway.
  8071. //
  8072. // goto CONTINUE;
  8073. }
  8074. //
  8075. // Domain wide Set -- may already exist
  8076. //
  8077. WStatus = UuidCreateNil(&Guid);
  8078. CLEANUP_WS(0, ":DS: ERROR - no UUID Created; cannot process sysvol.", WStatus, CONTINUE);
  8079. FrsDsAddLdapMod(ATTR_CLASS, ATTR_REPLICA_SET, &LdapMod);
  8080. FrsDsAddLdapMod(ATTR_SET_TYPE, FRS_RSTYPE_DOMAIN_SYSVOLW, &LdapMod);
  8081. //
  8082. // Create the replica set object with the default file
  8083. // and dir filter lists only if current default is non-null.
  8084. //
  8085. FileFilterList = FRS_DS_COMPOSE_FILTER_LIST(NULL,
  8086. RegistryFileExclFilterList,
  8087. DEFAULT_FILE_FILTER_LIST);
  8088. if (wcslen(FileFilterList) > 0) {
  8089. FrsDsAddLdapMod(ATTR_FILE_FILTER, FileFilterList, &LdapMod);
  8090. }
  8091. DirFilterList = FRS_DS_COMPOSE_FILTER_LIST(NULL,
  8092. RegistryDirExclFilterList,
  8093. DEFAULT_DIR_FILTER_LIST);
  8094. if (wcslen(DirFilterList) > 0) {
  8095. FrsDsAddLdapMod(ATTR_DIRECTORY_FILTER, DirFilterList, &LdapMod);
  8096. }
  8097. FrsDsAddLdapBerMod(ATTR_NEW_SET_GUID, (PCHAR)&Guid, sizeof(GUID), &LdapMod);
  8098. FrsDsAddLdapBerMod(ATTR_NEW_VERSION_GUID, (PCHAR)&Guid, sizeof(GUID), &LdapMod);
  8099. DPRINT1(4, ":DS: Creating Domain Set %ws\n", ReplicaSetName);
  8100. LStatus = ldap_add_s(Ldap, SystemSetDn, LdapMod);
  8101. FrsDsFreeLdapMod(&LdapMod);
  8102. if (LStatus == LDAP_SUCCESS) {
  8103. *RefetchComputer = TRUE;
  8104. }
  8105. if (LStatus != LDAP_ALREADY_EXISTS) {
  8106. CLEANUP1_LS(0, ":DS: ERROR - Can't create %ws:",
  8107. SystemSetDn, LStatus, CONTINUE);
  8108. }
  8109. LStatus = LDAP_SUCCESS;
  8110. goto CONTINUE;
  8111. }
  8112. if (Command != REGCMD_CREATE_MEMBERS) {
  8113. DPRINT1(0, ":DS: ERROR - Don't understand %d; can't process sysvols\n",
  8114. Command);
  8115. goto CONTINUE;
  8116. }
  8117. //
  8118. // CREATE MEMBER
  8119. //
  8120. // Member -- may already exist
  8121. // Delete old member in case it was left lying around after
  8122. // a demotion. This can happen because the service doesn't
  8123. // have permissions to alter the DS after a promotion.
  8124. // Leaving the old objects lying around after the demotion
  8125. // is confusing but doesn't cause replication to behave
  8126. // incorrectly.
  8127. //
  8128. DPRINT1(4, ":DS: Creating Member %ws\n", ComputerName);
  8129. OldNaming = FALSE;
  8130. //
  8131. // Delete old member
  8132. //
  8133. LStatus = FrsDsDeleteSubTree(Ldap, MemberDn);
  8134. CLEANUP1_LS(0, ":DS: ERROR - Can't free member %ws:",
  8135. ComputerName, LStatus, CONTINUE);
  8136. LStatus = FrsDsDeleteSubTree(Ldap, SystemMemberDn);
  8137. CLEANUP1_LS(0, ":DS: ERROR - Can't free system member %ws:",
  8138. ComputerName, LStatus, CONTINUE);
  8139. //
  8140. // Create new member
  8141. //
  8142. FrsDsAddLdapMod(ATTR_CLASS, ATTR_MEMBER, &LdapMod);
  8143. FrsDsAddLdapMod(ATTR_COMPUTER_REF, Computer->Dn, &LdapMod);
  8144. if (Computer->SettingsDn) {
  8145. FrsDsAddLdapMod(ATTR_SERVER_REF, Computer->SettingsDn, &LdapMod);
  8146. }
  8147. LStatus = ldap_add_s(Ldap, SystemMemberDn, LdapMod);
  8148. FrsDsFreeLdapMod(&LdapMod);
  8149. if (LStatus == LDAP_SUCCESS) {
  8150. *RefetchComputer = TRUE;
  8151. }
  8152. if (LStatus != LDAP_ALREADY_EXISTS && LStatus != LDAP_SUCCESS) {
  8153. //
  8154. // Try old B2 naming conventions
  8155. //
  8156. DPRINT1_LS(4, ":DS: WARN - Can't create system member ws:",
  8157. ComputerName, LStatus);
  8158. FrsDsAddLdapMod(ATTR_CLASS, ATTR_MEMBER, &LdapMod);
  8159. FrsDsAddLdapMod(ATTR_COMPUTER_REF, Computer->Dn, &LdapMod);
  8160. if (Computer->SettingsDn) {
  8161. FrsDsAddLdapMod(ATTR_SERVER_REF, Computer->SettingsDn, &LdapMod);
  8162. }
  8163. LStatus = ldap_add_s(Ldap, MemberDn, LdapMod);
  8164. FrsDsFreeLdapMod(&LdapMod);
  8165. if (LStatus == LDAP_SUCCESS) {
  8166. *RefetchComputer = TRUE;
  8167. }
  8168. if (LStatus != LDAP_ALREADY_EXISTS) {
  8169. CLEANUP1_LS(0, ":DS: ERROR - Can't create old member %ws:",
  8170. ComputerName, LStatus, CONTINUE);
  8171. }
  8172. OldNaming = TRUE;
  8173. }
  8174. //
  8175. // CREATE PRIMARY MEMBER REFERENCE
  8176. //
  8177. if (ReplicaSetPrimary) {
  8178. FrsDsAddLdapMod(ATTR_PRIMARY_MEMBER,
  8179. (OldNaming) ? MemberDn : SystemMemberDn,
  8180. &LdapMod);
  8181. DPRINT2(4, ":DS: Creating Member Reference %ws for %ws\n",
  8182. ComputerName, ReplicaSetName);
  8183. LdapMod[0]->mod_op = LDAP_MOD_REPLACE;
  8184. LStatus = ldap_modify_s(Ldap, (OldNaming) ? SetDn : SystemSetDn, LdapMod);
  8185. FrsDsFreeLdapMod(&LdapMod);
  8186. if (LStatus == LDAP_SUCCESS) {
  8187. *RefetchComputer = TRUE;
  8188. }
  8189. if (LStatus != LDAP_ATTRIBUTE_OR_VALUE_EXISTS) {
  8190. CLEANUP2_LS(0, ":DS: ERROR - Can't create priamry reference %ws\\%ws:",
  8191. ReplicaSetName, ComputerName, LStatus, CONTINUE);
  8192. }
  8193. }
  8194. //
  8195. // Translate the symlinks. NtFrs requires true pathname to
  8196. // its directories (<drive letter>:\...)
  8197. // FrsChaseSymbolicLink returns both the PrintName and the SubstituteName.
  8198. // We use the PrintName as it is the Dos Type name of the destination.
  8199. // Substitute Name is ignored.
  8200. //
  8201. WStatus = FrsChaseSymbolicLink(ReplicationRootPath, &PrintableRealRoot, &SubstituteRealRoot);
  8202. if (!WIN_SUCCESS(WStatus)) {
  8203. DPRINT2(0, ":DS: ERROR - Accessing %ws; cannot process sysvol: WStatus = %d",
  8204. ReplicationRootPath, WStatus);
  8205. RetStatus = FALSE;
  8206. goto CONTINUE;
  8207. }
  8208. WStatus = FrsChaseSymbolicLink(ReplicationStagePath, &PrintableRealStage, &SubstituteRealStage);
  8209. if (!WIN_SUCCESS(WStatus)) {
  8210. DPRINT2(0, ":DS: ERROR - Accessing %ws; cannot process sysvol: WStatus = %d",
  8211. ReplicationRootPath, WStatus);
  8212. RetStatus = FALSE;
  8213. goto CONTINUE;
  8214. }
  8215. //
  8216. // Subscriptions (if needed)
  8217. //
  8218. DPRINT1(4, ":DS: Creating Subscriptions for %ws\n", ComputerName);
  8219. FrsDsAddLdapMod(ATTR_CLASS, ATTR_SUBSCRIPTIONS, &LdapMod);
  8220. FrsDsAddLdapMod(ATTR_WORKING, WorkingPath, &LdapMod);
  8221. LStatus = ldap_add_s(Ldap, SubsDn, LdapMod);
  8222. FrsDsFreeLdapMod(&LdapMod);
  8223. if (LStatus == LDAP_SUCCESS) {
  8224. *RefetchComputer = TRUE;
  8225. }
  8226. if (LStatus != LDAP_ALREADY_EXISTS) {
  8227. CLEANUP1_LS(0, ":DS: ERROR - Can't create %ws:",
  8228. SubsDn, LStatus, CONTINUE);
  8229. }
  8230. //
  8231. // Subscriber -- may alread exist
  8232. // Delete old subscriber in case it was left lying around
  8233. // after a demotion. This can happen because the service
  8234. // doesn't have permissions to alter the DS after a promotion.
  8235. // Leaving the old objects lying around after the demotion
  8236. // is confusing but doesn't cause replication to behave
  8237. // incorrectly; any sysvol in the DS without a corresponding
  8238. // sysvol in the DB is ignored by the Ds polling thread.
  8239. //
  8240. DPRINT1(4, ":DS: Creating Subscriber for %ws\n", ComputerName);
  8241. LStatus = FrsDsDeleteSubTree(Ldap, SubDn);
  8242. CLEANUP1_LS(4, ":DS: WARN - Can't delete %ws:", SubDn, LStatus, CONTINUE);
  8243. LStatus = FrsDsDeleteSubTree(Ldap, SystemSubDn);
  8244. CLEANUP1_LS(4, ":DS: WARN - Can't delete %ws:", SystemSubDn, LStatus, CONTINUE);
  8245. FrsDsAddLdapMod(ATTR_CLASS, ATTR_SUBSCRIBER, &LdapMod);
  8246. FrsDsAddLdapMod(ATTR_REPLICA_ROOT, PrintableRealRoot, &LdapMod);
  8247. FrsDsAddLdapMod(ATTR_REPLICA_STAGE, PrintableRealStage, &LdapMod);
  8248. FrsDsAddLdapMod(ATTR_MEMBER_REF,
  8249. (OldNaming) ? MemberDn : SystemMemberDn,
  8250. &LdapMod);
  8251. LStatus = ldap_add_s(Ldap, SystemSubDn, LdapMod);
  8252. FrsDsFreeLdapMod(&LdapMod);
  8253. if (LStatus == LDAP_SUCCESS) {
  8254. *RefetchComputer = TRUE;
  8255. }
  8256. if (LStatus != LDAP_ALREADY_EXISTS) {
  8257. CLEANUP1_LS(4, ":DS: ERROR - Can't create %ws:",
  8258. SystemSubDn, LStatus, CONTINUE);
  8259. }
  8260. //
  8261. // Seeding information
  8262. //
  8263. //
  8264. // Create the key for all seeding sysvols
  8265. //
  8266. WStatus = CfgRegOpenKey(FKC_SYSVOL_SEEDING_SECTION_KEY,
  8267. NULL,
  8268. FRS_RKF_CREATE_KEY,
  8269. &HSeedingsKey);
  8270. CLEANUP1_WS(0, ":DS: ERROR - Cannot create seedings key for %ws;",
  8271. ReplicaSetName, WStatus, SKIP_SEEDING);
  8272. //
  8273. // Create the seeding subkey for this sysvol
  8274. //
  8275. RegDeleteKey(HSeedingsKey, ReplicaSetName);
  8276. RegDeleteKey(HSeedingsKey, CN_DOMAIN_SYSVOL);
  8277. if (ReplicaSetParent) {
  8278. //
  8279. // Save the Replica Set Parent for this replica set under the
  8280. // "Sysvol Seeding\<rep set name>\Replica Set Parent"
  8281. //
  8282. WStatus = CfgRegWriteString(FKC_SYSVOL_SEEDING_N_PARENT,
  8283. (OldNaming) ? ReplicaSetName : CN_DOMAIN_SYSVOL,
  8284. FRS_RKF_CREATE_KEY,
  8285. ReplicaSetParent);
  8286. DPRINT1_WS(0, "WARN - Cannot create parent value for %ws;",
  8287. (OldNaming) ? ReplicaSetName : CN_DOMAIN_SYSVOL, WStatus);
  8288. }
  8289. //
  8290. // Save the Replica Set name for this replica set under the
  8291. // "Sysvol Seeding\<rep set name>\Replica Set Name"
  8292. //
  8293. WStatus = CfgRegWriteString(FKC_SYSVOL_SEEDING_N_RSNAME,
  8294. (OldNaming) ? ReplicaSetName : CN_DOMAIN_SYSVOL,
  8295. FRS_RKF_CREATE_KEY,
  8296. ReplicaSetName);
  8297. DPRINT1_WS(0, "WARN - Cannot create name value for %ws;",
  8298. (OldNaming) ? ReplicaSetName : CN_DOMAIN_SYSVOL, WStatus);
  8299. SKIP_SEEDING:
  8300. LStatus = LDAP_SUCCESS;
  8301. CONTINUE:
  8302. FRS_REG_CLOSE(HSeedingsKey);
  8303. //
  8304. // Something went wrong. Put the LDAP error status into the
  8305. // registry key for this replica set and move on to the next.
  8306. //
  8307. if (LStatus != LDAP_SUCCESS) {
  8308. CfgRegWriteDWord(FKC_SET_N_SYSVOL_STATUS, RegBuf, 0, LStatus);
  8309. RetStatus = FALSE;
  8310. }
  8311. //
  8312. // CLEANUP
  8313. //
  8314. ReplicaSetCommand = FrsFree(ReplicaSetCommand);
  8315. ReplicaSetName = FrsFree(ReplicaSetName);
  8316. ReplicaSetParent = FrsFree(ReplicaSetParent);
  8317. ReplicaSetType = FrsFree(ReplicaSetType);
  8318. ReplicationRootPath = FrsFree(ReplicationRootPath);
  8319. PrintableRealRoot = FrsFree(PrintableRealRoot);
  8320. SubstituteRealRoot = FrsFree(SubstituteRealRoot);
  8321. ReplicationStagePath = FrsFree(ReplicationStagePath);
  8322. PrintableRealStage = FrsFree(PrintableRealStage);
  8323. SubstituteRealStage = FrsFree(SubstituteRealStage);
  8324. SettingsDn = FrsFree(SettingsDn);
  8325. SystemSettingsDn = FrsFree(SystemSettingsDn);
  8326. SetDn = FrsFree(SetDn);
  8327. SystemSetDn = FrsFree(SystemSetDn);
  8328. SubsDn = FrsFree(SubsDn);
  8329. SubDn = FrsFree(SubDn);
  8330. SystemSubDn = FrsFree(SystemSubDn);
  8331. MemberDn = FrsFree(MemberDn);
  8332. SystemMemberDn = FrsFree(SystemMemberDn);
  8333. FileFilterList = FrsFree(FileFilterList);
  8334. DirFilterList = FrsFree(DirFilterList);
  8335. //
  8336. // Next SubKey
  8337. //
  8338. ++Index;
  8339. } // End while (RetStatus)
  8340. if (HANDLE_IS_VALID(HKey)) {
  8341. //
  8342. // The flush here will make sure that the key is written to the disk.
  8343. // These are critical registry operations and we don't want the lazy flusher
  8344. // to delay the writes.
  8345. //
  8346. RegFlushKey(HKey);
  8347. FRS_REG_CLOSE(HKey);
  8348. }
  8349. return RetStatus;
  8350. }
  8351. DWORD
  8352. FrsDsCreateSysVols(
  8353. IN PLDAP Ldap,
  8354. IN PWCHAR ServicesDn,
  8355. IN PCONFIG_NODE Computer,
  8356. OUT BOOL *RefetchComputer
  8357. )
  8358. /*++
  8359. Routine Description:
  8360. Process the commands left in the Sysvol registry key by dcpromo.
  8361. Ignore the sysvol registry key if this machine is not a DC!
  8362. NOTE: this means the registry keys for a "delete sysvol"
  8363. after a demotion are pretty much ignored. So why have them?
  8364. Its historical and there is too little time before B3 to make
  8365. such a dramatic change. Besides, we may find a use for them.
  8366. And, to make matters worse, the "delete sysvol" keys could
  8367. not be processed because the ldap_delete() returned insufficient
  8368. rights errors since this computer is no longer a DC.
  8369. REGCMD_DELETE_MEMBERS is no longer used as all deletion is done
  8370. in ntfrsapi.c when demotion is committed.
  8371. Arguments:
  8372. Ldap
  8373. ServicesDn
  8374. Computer
  8375. RefetchComputer - Objects were altered in the DS, refetch DS info
  8376. Return Value:
  8377. None.
  8378. --*/
  8379. {
  8380. #undef DEBSUB
  8381. #define DEBSUB "FrsDsCreateSysVols:"
  8382. DWORD WStatus;
  8383. DWORD SysVolInfoIsCommitted;
  8384. HKEY HKey = INVALID_HANDLE_VALUE;
  8385. //
  8386. // Refetch the computer subtree iff the contents of the DS
  8387. // are altered by this function
  8388. //
  8389. *RefetchComputer = FALSE;
  8390. //
  8391. // Already checked the registry or not a DC; done
  8392. //
  8393. if (DsCreateSysVolsHasRun || !IsADc) {
  8394. return ERROR_SUCCESS;
  8395. }
  8396. DPRINT(5, ":DS: Checking for SysVols commands\n");
  8397. //
  8398. // Open the system volume replica sets key.
  8399. // FRS_CONFIG_SECTION\SysVol
  8400. //
  8401. WStatus = CfgRegOpenKey(FKC_SYSVOL_SECTION_KEY, NULL, 0, &HKey);
  8402. if (!WIN_SUCCESS(WStatus)) {
  8403. DPRINT_WS(4, ":DS: WARN - Cannot open sysvol key.", WStatus);
  8404. DPRINT(4, ":DS: ERROR - Can't check for sysvols\n");
  8405. return WStatus;
  8406. }
  8407. WStatus = CfgRegReadDWord(FKC_SYSVOL_INFO_COMMITTED, NULL, 0, &SysVolInfoIsCommitted);
  8408. CLEANUP_WS(4, ":DS: Sysvol info is not committed.", WStatus, done);
  8409. DPRINT1(4, ":DS: Sysvol info is committed (%d)\n", SysVolInfoIsCommitted);
  8410. //
  8411. // Must have a computer; try again later
  8412. //
  8413. if (!Computer) {
  8414. DPRINT(4, ":DS: No computer; retry sysvols later\n");
  8415. WStatus = ERROR_RETRY;
  8416. goto cleanup;
  8417. }
  8418. //
  8419. // Must have a server reference; try again later
  8420. //
  8421. if (!Computer->SettingsDn && RunningAsAService) {
  8422. DPRINT1(4, ":DS: %ws does not have a server reference; retry sysvols later\n",
  8423. Computer->Name->Name);
  8424. WStatus = ERROR_RETRY;
  8425. goto cleanup;
  8426. }
  8427. //
  8428. // assume failure
  8429. //
  8430. WIN_SET_FAIL(WStatus);
  8431. //
  8432. // Don't create the settings or set if this computer is not a DC
  8433. //
  8434. if (IsADc &&
  8435. !FrsDsEnumerateSysVolKeys(Ldap, REGCMD_CREATE_PRIMARY_DOMAIN,
  8436. ServicesDn, SystemDn, Computer, RefetchComputer)) {
  8437. goto cleanup;
  8438. }
  8439. //
  8440. // Don't create the member if this computer is not a DC
  8441. //
  8442. if (IsADc &&
  8443. !FrsDsEnumerateSysVolKeys(Ldap, REGCMD_CREATE_MEMBERS,
  8444. ServicesDn, SystemDn, Computer, RefetchComputer)) {
  8445. goto cleanup;
  8446. }
  8447. //
  8448. // Don't delete the sysvol if this computer is a DC.
  8449. //
  8450. // The following code is never executed because if we are not a DC then
  8451. // the function returns after the first check.
  8452. //
  8453. /*
  8454. if (!IsADc &&
  8455. !FrsDsEnumerateSysVolKeys(Ldap, REGCMD_DELETE_MEMBERS,
  8456. ServicesDn, SystemDn, Computer, RefetchComputer)) {
  8457. goto cleanup;
  8458. }
  8459. */
  8460. //
  8461. // Discard the dcpromo keys
  8462. //
  8463. if (!FrsDsEnumerateSysVolKeys(Ldap, REGCMD_DELETE_KEYS,
  8464. ServicesDn, SystemDn, Computer, RefetchComputer)) {
  8465. goto cleanup;
  8466. }
  8467. //
  8468. // sysvol info has been processed; don't process again
  8469. //
  8470. RegDeleteValue(HKey, SYSVOL_INFO_IS_COMMITTED);
  8471. done:
  8472. DsCreateSysVolsHasRun = TRUE;
  8473. WStatus = ERROR_SUCCESS;
  8474. cleanup:
  8475. //
  8476. // Cleanup
  8477. //
  8478. if (HANDLE_IS_VALID(HKey)) {
  8479. //
  8480. // The flush here will make sure that the key is written to the disk.
  8481. // These are critical registry operations and we don't want the lazy flusher
  8482. // to delay the writes.
  8483. //
  8484. RegFlushKey(HKey);
  8485. FRS_REG_CLOSE(HKey);
  8486. }
  8487. return WStatus;
  8488. }
  8489. PWCHAR
  8490. FrsDsPrincNameToBiosName(
  8491. IN PWCHAR PrincName
  8492. )
  8493. /*++
  8494. Routine Description:
  8495. Convert the principal name (domain.dns.name\SamAccountName) into
  8496. its equivalent NetBios name (SamAccountName - $).
  8497. Arguments:
  8498. PrincName - Domain Dns Name \ Sam Account Name
  8499. Return Value:
  8500. Sam Account Name - trailing $
  8501. --*/
  8502. {
  8503. #undef DEBSUB
  8504. #define DEBSUB "FrsDsPrincNameToBiosName:"
  8505. DWORD Len;
  8506. PWCHAR c;
  8507. PWCHAR BiosName = NULL;
  8508. if (!PrincName || !*PrincName) {
  8509. goto CLEANUP;
  8510. }
  8511. //
  8512. // Find the first char past the first whack
  8513. //
  8514. for (c = PrincName; *c && *c != L'\\'; ++c);
  8515. if (!*c) {
  8516. //
  8517. // No whack; use the entire principal name
  8518. //
  8519. c = PrincName;
  8520. } else {
  8521. //
  8522. // Skip the whack
  8523. //
  8524. ++c;
  8525. }
  8526. //
  8527. // Elide the trailing $
  8528. //
  8529. Len = wcslen(c);
  8530. if (c[Len - 1] == L'$') {
  8531. --Len;
  8532. }
  8533. //
  8534. // Copy the chars between the whack and the dollar (append trailing null)
  8535. //
  8536. BiosName = FrsAlloc((Len + 1) * sizeof(WCHAR));
  8537. CopyMemory(BiosName, c, Len * sizeof(WCHAR));
  8538. BiosName[Len] = L'\0';
  8539. CLEANUP:
  8540. DPRINT2(4, ":DS: PrincName %ws to BiosName %ws\n", PrincName, BiosName);
  8541. return BiosName;
  8542. }
  8543. VOID
  8544. FrsDsMergeConfigWithReplicas(
  8545. IN PLDAP Ldap,
  8546. IN PCONFIG_NODE Sites
  8547. )
  8548. /*++
  8549. Routine Description:
  8550. Convert the portions of the DS tree that define the topology
  8551. and state for this machine into replicas and merge them with
  8552. the active replicas.
  8553. Arguments:
  8554. Sites
  8555. Return Value:
  8556. None.
  8557. --*/
  8558. {
  8559. #undef DEBSUB
  8560. #define DEBSUB "FrsDsMergeConfigWithReplicas:"
  8561. PCONFIG_NODE Site;
  8562. PCONFIG_NODE Settings;
  8563. PCONFIG_NODE Set;
  8564. PCONFIG_NODE Server;
  8565. PCONFIG_NODE Node;
  8566. PCONFIG_NODE RevNode;
  8567. BOOL Inbound;
  8568. BOOL IsSysvol;
  8569. PCXTION Cxtion;
  8570. PREPLICA Replica;
  8571. PREPLICA DbReplica;
  8572. //
  8573. // Coordinate with replica command server
  8574. //
  8575. RcsBeginMergeWithDs();
  8576. //
  8577. // For every server
  8578. //
  8579. for (Site = Sites; Site; Site = Site->Peer) {
  8580. for (Settings = Site->Children; Settings; Settings = Settings->Peer) {
  8581. for (Set = Settings->Children; Set; Set = Set->Peer) {
  8582. for (Server = Set->Children; Server && !DsIsShuttingDown; Server = Server->Peer) {
  8583. //
  8584. // This server does not match this machine's name; continue
  8585. //
  8586. if (!Server->ThisComputer) {
  8587. continue;
  8588. }
  8589. //
  8590. // MATCH
  8591. //
  8592. //
  8593. // CHECK FOR SYSVOL CONSISTENCY
  8594. // Leave the current DB state alone if a sysvol
  8595. // appears from the DS and the sysvol registry
  8596. // keys were not processed or the computer is not
  8597. // a dc.
  8598. //
  8599. if (FRS_RSTYPE_IS_SYSVOLW(Set->SetType)) {
  8600. //
  8601. // Not a DC or sysvol registry keys not processed
  8602. // Tombstone existing sysvols
  8603. // Ignore new sysvols
  8604. //
  8605. if (!IsADc || !DsCreateSysVolsHasRun) {
  8606. continue;
  8607. }
  8608. }
  8609. //
  8610. // Create a replica set
  8611. //
  8612. Replica = FrsAllocType(REPLICA_TYPE);
  8613. //
  8614. // Replica name (Set Name + Member Guid)
  8615. Replica->ReplicaName = FrsBuildGName(FrsDupGuid(Server->Name->Guid),
  8616. FrsWcsDup(Set->Name->Name));
  8617. //
  8618. // Member name + guid
  8619. //
  8620. Replica->MemberName = FrsDupGName(Server->Name);
  8621. //
  8622. // Set name + guid
  8623. //
  8624. Replica->SetName = FrsDupGName(Set->Name);
  8625. //
  8626. // Root guid (hammered onto the root directory)
  8627. // Temporary; a new guid is assigned if this is a new
  8628. // set.
  8629. //
  8630. Replica->ReplicaRootGuid = FrsDupGuid(Replica->SetName->Guid);
  8631. //
  8632. // File Filter
  8633. //
  8634. Replica->FileFilterList = FRS_DS_COMPOSE_FILTER_LIST(
  8635. Set->FileFilterList,
  8636. RegistryFileExclFilterList,
  8637. DEFAULT_FILE_FILTER_LIST);
  8638. Replica->FileInclFilterList = FrsWcsDup(RegistryFileInclFilterList);
  8639. //
  8640. // Directory Filter
  8641. //
  8642. Replica->DirFilterList = FRS_DS_COMPOSE_FILTER_LIST(
  8643. Set->DirFilterList,
  8644. RegistryDirExclFilterList,
  8645. DEFAULT_DIR_FILTER_LIST);
  8646. Replica->DirInclFilterList = FrsWcsDup(RegistryDirInclFilterList);
  8647. //
  8648. // Root and stage
  8649. //
  8650. Replica->Root = FrsWcsDup(Server->Root);
  8651. Replica->Stage = FrsWcsDup(Server->Stage);
  8652. FRS_WCSLWR(Replica->Root); // for wcsstr()
  8653. FRS_WCSLWR(Replica->Stage); // for wcsstr()
  8654. //
  8655. // Volume.
  8656. //
  8657. // Replica->Volume = FrsWcsVolume(Server->Root);
  8658. //
  8659. // Does the Set's primary member link match this
  8660. // member's Dn? Is this the primary member?
  8661. //
  8662. if (Set->MemberDn) {
  8663. ClearFlag(Replica->CnfFlags, CONFIG_FLAG_PRIMARY_UNDEFINED);
  8664. if(WSTR_EQ(Server->Dn, Set->MemberDn)) {
  8665. SetFlag(Replica->CnfFlags, CONFIG_FLAG_PRIMARY);
  8666. }
  8667. } else {
  8668. SetFlag(Replica->CnfFlags, CONFIG_FLAG_PRIMARY_UNDEFINED);
  8669. //
  8670. // DFS is not currently (Sept 2002) using the primary
  8671. // member, so it is okay if there is none.
  8672. // For now, don't spam the event log with this warning.
  8673. //
  8674. // FrsDsAddToPollSummary1ws(IDS_POLL_SUM_PRIMARY_UNDEFINED,
  8675. // Replica->ReplicaName->Name
  8676. // );
  8677. }
  8678. //
  8679. // Consistent
  8680. //
  8681. Replica->Consistent = Server->Consistent;
  8682. //
  8683. // Replica Set Type
  8684. //
  8685. if (Set->SetType) {
  8686. Replica->ReplicaSetType = wcstoul(Set->SetType, NULL, 10);
  8687. } else {
  8688. Replica->ReplicaSetType = FRS_RSTYPE_OTHER;
  8689. }
  8690. //
  8691. // FRS replica set object Flags
  8692. //
  8693. Replica->FrsRsoFlags = Set->FrsRsoFlags;
  8694. //
  8695. // Set default Schedule for replica set. Priority order is:
  8696. // 1. Server (sysvols only)
  8697. // 2. ReplicaSet object
  8698. // 3. Settings object
  8699. // 4. Site object.
  8700. //
  8701. Node = (Server->Schedule) ? Server :
  8702. (Set->Schedule) ? Set :
  8703. (Settings->Schedule) ? Settings :
  8704. (Site->Schedule) ? Site : NULL;
  8705. if (Node) {
  8706. Replica->Schedule = FrsAlloc(Node->ScheduleLength);
  8707. CopyMemory(Replica->Schedule, Node->Schedule, Node->ScheduleLength);
  8708. }
  8709. //
  8710. // Sysvol needs seeding
  8711. //
  8712. // The CnfFlags are ignored if the set already exists.
  8713. // Hence, only newly created sysvols are seeded.
  8714. //
  8715. IsSysvol = FRS_RSTYPE_IS_SYSVOL(Replica->ReplicaSetType);
  8716. if (IsSysvol &&
  8717. !BooleanFlagOn(Replica->CnfFlags, CONFIG_FLAG_PRIMARY)) {
  8718. SetFlag(Replica->CnfFlags, CONFIG_FLAG_SEEDING);
  8719. }
  8720. //
  8721. // Go through the connections and fix the schedule for
  8722. // two way replication.
  8723. //
  8724. for (Node = Server->Children; Node; Node = Node->Peer) {
  8725. if (!Node->Consistent) {
  8726. continue;
  8727. }
  8728. //
  8729. // If the NTDSCONN_OPT_TWOWAY_SYNC flag is set on the connection then
  8730. // merge the schedule on this connection with the schedule on the connection
  8731. // that is in the opposite direction and use the resultant schedule on the
  8732. // connection in the opposite direction.
  8733. //
  8734. if (Node->CxtionOptions & NTDSCONN_OPT_TWOWAY_SYNC) {
  8735. Inbound = !Node->Inbound;
  8736. //
  8737. // Loop through the connections and find the connection in
  8738. // the opposite direction.
  8739. //
  8740. for (RevNode = Server->Children; RevNode; RevNode = RevNode->Peer) {
  8741. if ((RevNode->Inbound == Inbound) &&
  8742. !_wcsicmp(Node->PartnerDn, RevNode->PartnerDn)) {
  8743. DPRINT1(4,"Two-way replication: Setting merged schedule on %ws\n",RevNode->Dn);
  8744. FrsDsMergeTwoWaySchedules(&Node->Schedule,
  8745. &Node->ScheduleLength,
  8746. &RevNode->Schedule,
  8747. &RevNode->ScheduleLength,
  8748. &Replica->Schedule);
  8749. break;
  8750. }
  8751. }
  8752. }
  8753. }
  8754. //
  8755. // Copy over the cxtions
  8756. //
  8757. for (Node = Server->Children; Node; Node = Node->Peer) {
  8758. if (!Node->Consistent) {
  8759. continue;
  8760. }
  8761. Cxtion = FrsAllocType(CXTION_TYPE);
  8762. Cxtion->Inbound = Node->Inbound;
  8763. if (Node->Consistent) {
  8764. SetCxtionFlag(Cxtion, CXTION_FLAGS_CONSISTENT);
  8765. }
  8766. Cxtion->Name = FrsDupGName(Node->Name);
  8767. Cxtion->Partner = FrsBuildGName(
  8768. FrsDupGuid(Node->PartnerName->Guid),
  8769. FrsDsPrincNameToBiosName(Node->PrincName));
  8770. //
  8771. // Partner's DNS name from ATTR_DNS_HOST_NAME on the computer
  8772. // object. Register an event if the attribute is missing
  8773. // or unavailable and try using the netbios name.
  8774. //
  8775. if (Node->PartnerDnsName) {
  8776. Cxtion->PartnerDnsName = FrsWcsDup(Node->PartnerDnsName);
  8777. } else {
  8778. if (Cxtion->Partner->Name && Cxtion->Partner->Name[0]) {
  8779. EPRINT3(EVENT_FRS_NO_DNS_ATTRIBUTE,
  8780. Cxtion->Partner->Name,
  8781. ATTR_DNS_HOST_NAME,
  8782. (Node->PartnerCoDn) ? Node->PartnerCoDn :
  8783. Cxtion->Partner->Name);
  8784. Cxtion->PartnerDnsName = FrsWcsDup(Cxtion->Partner->Name);
  8785. } else {
  8786. Cxtion->PartnerDnsName = FrsWcsDup(L"<unknown>");
  8787. }
  8788. }
  8789. //
  8790. // Partner's SID name from DsCrackNames() on the computer
  8791. // object. Register an event if the SID is unavailable.
  8792. //
  8793. if (Node->PartnerSid) {
  8794. Cxtion->PartnerSid = FrsWcsDup(Node->PartnerSid);
  8795. } else {
  8796. //
  8797. // Print the eventlog message only if DsBindingsAreValid is TRUE.
  8798. // If it is FALSE it means that the handle is invalid and we are
  8799. // scheduled to rebind at the next poll. In that case the rebind will
  8800. // probably fix the problem silently.
  8801. //
  8802. if (Cxtion->Partner->Name && Cxtion->Partner->Name[0] && DsBindingsAreValid) {
  8803. EPRINT3(EVENT_FRS_NO_SID,
  8804. Replica->Root,
  8805. Cxtion->Partner->Name,
  8806. (Node->PartnerCoDn) ? Node->PartnerCoDn :
  8807. Cxtion->Partner->Name);
  8808. }
  8809. Cxtion->PartnerSid = FrsWcsDup(L"<unknown>");
  8810. }
  8811. Cxtion->PartnerPrincName = FrsWcsDup(Node->PrincName);
  8812. Cxtion->PartSrvName = FrsWcsDup(Node->PrincName);
  8813. //
  8814. // Use the schedule on the cxtion object if provided.
  8815. // Otherwise it will default to the schedule on the replica struct
  8816. // that was set above.
  8817. //
  8818. if (Node->Schedule) {
  8819. Cxtion->Schedule = FrsAlloc(Node->ScheduleLength);
  8820. CopyMemory(Cxtion->Schedule, Node->Schedule, Node->ScheduleLength);
  8821. }
  8822. //
  8823. // Treat the schedule as a trigger schedule if the partner
  8824. // is in another site, if this is a sysvol, and if the node
  8825. // has a schedule.
  8826. //
  8827. // A missing schedule means, "always on" for both
  8828. // stop/start and trigger schedules.
  8829. //
  8830. if (IsSysvol && !Node->SameSite && Node->Schedule) {
  8831. SetCxtionFlag(Cxtion, CXTION_FLAGS_TRIGGER_SCHEDULE);
  8832. }
  8833. SetCxtionState(Cxtion, CxtionStateUnjoined);
  8834. GTabInsertEntry(Replica->Cxtions, Cxtion, Cxtion->Name->Guid, NULL);
  8835. //
  8836. // Copy over the value of options attribute of the connection object.
  8837. //
  8838. Cxtion->Options = Node->CxtionOptions;
  8839. Cxtion->Priority = FRSCONN_GET_PRIORITY(Cxtion->Options);
  8840. }
  8841. //
  8842. // Merge the replica with the active replicas
  8843. //
  8844. RcsMergeReplicaFromDs(Replica);
  8845. } } } }
  8846. RcsEndMergeWithDs();
  8847. //
  8848. // The above code is only executed when the DS changes. This should
  8849. // be an infrequent occurance. Any code we loaded to process the merge
  8850. // can now be discarded without undue impact on active replication.
  8851. //
  8852. SetProcessWorkingSetSize(ProcessHandle, (SIZE_T)-1, (SIZE_T)-1);
  8853. }
  8854. VOID
  8855. FrsDsPollDs(
  8856. VOID
  8857. )
  8858. /*++
  8859. Routine Description:
  8860. New way to get the current configuration from the DS and merge it with
  8861. the active replicas.
  8862. Part of NewDs poll APIs.
  8863. Arguments:
  8864. None.
  8865. Return Value:
  8866. None.
  8867. --*/
  8868. {
  8869. #undef DEBSUB
  8870. #define DEBSUB "FrsDsPollDs:"
  8871. BOOL RefetchComputer;
  8872. DWORD WStatus = ERROR_SUCCESS;
  8873. PCONFIG_NODE Services = NULL;
  8874. PCONFIG_NODE Computer = NULL;
  8875. PVOID Key = NULL;
  8876. PGEN_ENTRY Entry = NULL;
  8877. //
  8878. // Increment the DS Polls Counter
  8879. //
  8880. PM_INC_CTR_SERVICE(PMTotalInst, DSPolls, 1);
  8881. //
  8882. // Empty the VolSerialNumberToDriveTable before every poll so we have
  8883. // fresh information every time. The table is built as needed.
  8884. //
  8885. if (VolSerialNumberToDriveTable != NULL) {
  8886. GTabEmptyTable(VolSerialNumberToDriveTable, FrsFree);
  8887. }
  8888. #if DBG
  8889. //
  8890. // For test purposes, you can run without a DS
  8891. //
  8892. if (NoDs) {
  8893. //
  8894. // kick off the rest of the service
  8895. //
  8896. MainInit();
  8897. if (!MainInitHasRun) {
  8898. FRS_ASSERT(MainInitHasRun == TRUE);
  8899. }
  8900. //
  8901. // Use the hardwired config
  8902. //
  8903. if (IniFileName) {
  8904. DPRINT(0, ":DS: Hard wired config from ini file.\n");
  8905. FrsDsUseHardWired(LoadedWired);
  8906. } else {
  8907. DPRINT(0, ":DS: David's hard wired config.\n");
  8908. //
  8909. // Complete config
  8910. //
  8911. FrsDsUseHardWired(DavidWired);
  8912. #if 0
  8913. Sleep(60 * 1000);
  8914. //
  8915. // Take out the server 2 (E:)
  8916. //
  8917. FrsDsUseHardWired(DavidWired2);
  8918. Sleep(60 * 1000);
  8919. //
  8920. // Put back in E and but take out all cxtions
  8921. //
  8922. //FrsDsUseHardWired();
  8923. //Sleep(5 * 1000);
  8924. //
  8925. // Put everything back in
  8926. //
  8927. FrsDsUseHardWired(DavidWired);
  8928. Sleep(60 * 1000);
  8929. #endif
  8930. //
  8931. // Repeat in 30 seconds
  8932. //
  8933. DsPollingShortInterval = 30 * 1000;
  8934. DsPollingLongInterval = 30 * 1000;
  8935. DsPollingInterval = 30 * 1000;
  8936. }
  8937. //
  8938. // Periodically check the local resources like disk space etc.
  8939. //
  8940. FrsCheckLocalResources();
  8941. return;
  8942. }
  8943. #endif DBG
  8944. //
  8945. // Backup/Restore
  8946. //
  8947. WStatus = FrsProcessBackupRestore();
  8948. if (!WIN_SUCCESS(WStatus)) {
  8949. goto CLEANUP;
  8950. }
  8951. //
  8952. // Open and bind an ldap connection to the DS
  8953. //
  8954. if (!FrsDsOpenDs()) {
  8955. if (DsIsShuttingDown) {
  8956. goto CLEANUP;
  8957. }
  8958. DPRINT(4, ":DS: Wait 5 seconds and retry DS open.\n");
  8959. WaitForSingleObject(ShutDownEvent, 5 * 1000);
  8960. if (!FrsDsOpenDs()) {
  8961. if (DsIsShuttingDown) {
  8962. goto CLEANUP;
  8963. }
  8964. DPRINT(4, ":DS: Wait 30 seconds and retry DS open.\n");
  8965. WaitForSingleObject(ShutDownEvent, 30 * 1000);
  8966. if (!FrsDsOpenDs()) {
  8967. if (DsIsShuttingDown) {
  8968. goto CLEANUP;
  8969. }
  8970. DPRINT(4, ":DS: Wait 180 seconds and retry DS open.\n");
  8971. WaitForSingleObject(ShutDownEvent, 3 * 60 * 1000);
  8972. if (!FrsDsOpenDs()) {
  8973. //
  8974. // Add to the poll summary event log.
  8975. //
  8976. FrsDsAddToPollSummary(IDS_POLL_SUM_DSBIND_FAIL);
  8977. goto CLEANUP;
  8978. }
  8979. }
  8980. }
  8981. }
  8982. //
  8983. // Keep a running checksum of the change usns for this polling cycle
  8984. // Ignore configurations whose checksum is not the same for two
  8985. // polling intervals (DS is in flux).
  8986. //
  8987. ThisChange = 0;
  8988. NextChange = 0;
  8989. //
  8990. // User side of the configuration. This function will build two table of subscribers.
  8991. // SubscribersByRootPath and SubscribersByMemberRef. It will resolve any duplicate
  8992. // conflicts.
  8993. //
  8994. //
  8995. // Initialize the PartnerComputerTable.
  8996. //
  8997. if (PartnerComputerTable != NULL) {
  8998. //
  8999. // Members of the PartnerComputerTable need to be freed seperately
  9000. // as they are not part of the tree. So call FrsFreeType for
  9001. // each node.
  9002. //
  9003. PartnerComputerTable = GTabFreeTable(PartnerComputerTable, FrsFreeType);
  9004. }
  9005. PartnerComputerTable = GTabAllocStringTable();
  9006. //
  9007. // Initialize the AllCxtionsTable.
  9008. //
  9009. if (AllCxtionsTable != NULL) {
  9010. AllCxtionsTable = GTabFreeTable(AllCxtionsTable, NULL);
  9011. }
  9012. AllCxtionsTable = GTabAllocStringAndBoolTable();
  9013. WStatus = FrsDsGetComputer(gLdap, &Computer);
  9014. if (!WIN_SUCCESS(WStatus)) {
  9015. //
  9016. // Add to the poll summary event log.
  9017. //
  9018. FrsDsAddToPollSummary(IDS_POLL_SUM_NO_COMPUTER);
  9019. goto CLEANUP;
  9020. }
  9021. if (!Computer) {
  9022. DPRINT(4, ":DS: NO COMPUTER OBJECT!\n");
  9023. //
  9024. // Add to the poll summary event log.
  9025. //
  9026. FrsDsAddToPollSummary(IDS_POLL_SUM_NO_COMPUTER);
  9027. }
  9028. //
  9029. // Register (once) our SPN using the global ds binding handle.
  9030. //
  9031. if (Computer) {
  9032. FrsDsRegisterSpn(gLdap, Computer);
  9033. }
  9034. //
  9035. // Create the sysvols, if any
  9036. //
  9037. if (IsADc && !DsCreateSysVolsHasRun) {
  9038. WStatus = FrsDsCreateSysVols(gLdap, ServicesDn, Computer, &RefetchComputer);
  9039. if (!WIN_SUCCESS(WStatus)) {
  9040. DPRINT1(4, ":DS: IGNORE Can't process sysvols; WStatus %s!\n", ErrLabelW32(WStatus));
  9041. WStatus = ERROR_SUCCESS;
  9042. } else if (RefetchComputer) {
  9043. //
  9044. // FrsDsCreateSysVols() may add/del objects from the user
  9045. // side of the configuration; refetch just in case.
  9046. //
  9047. ThisChange = 0;
  9048. NextChange = 0;
  9049. SubscriberTable = GTabFreeTable(SubscriberTable, NULL);
  9050. FrsDsFreeTree(Computer);
  9051. WStatus = FrsDsGetComputer(gLdap, &Computer);
  9052. if (!WIN_SUCCESS(WStatus)) {
  9053. goto CLEANUP;
  9054. }
  9055. }
  9056. }
  9057. //
  9058. // Is there any possibility that a replica set exists or that
  9059. // an old replica set should be deleted?
  9060. //
  9061. if (!FrsDsDoesUserWantReplication(Computer)) {
  9062. //
  9063. // Nope, no new, existing, or deleted sets
  9064. //
  9065. DPRINT(4, ":DS: Nothing to do; don't start the rest of the system.\n");
  9066. //
  9067. // Add to the poll summary event log.
  9068. //
  9069. FrsDsAddToPollSummary(IDS_POLL_SUM_NO_REPLICASETS);
  9070. WStatus = ERROR_RETRY;
  9071. goto CLEANUP;
  9072. }
  9073. //
  9074. // kick off the rest of the service
  9075. //
  9076. MainInit();
  9077. if (!MainInitHasRun) {
  9078. FRS_ASSERT(MainInitHasRun == TRUE);
  9079. }
  9080. //
  9081. // Admin side of the configuration
  9082. //
  9083. WStatus = FrsDsGetServices(gLdap, Computer, &Services);
  9084. if (Services == NULL) {
  9085. goto CLEANUP;
  9086. }
  9087. //
  9088. // Increment the DS Polls with and without changes Counters
  9089. //
  9090. if ((LastChange == 0)|| (ThisChange != LastChange)) {
  9091. PM_INC_CTR_SERVICE(PMTotalInst, DSPollsWChanges, 1);
  9092. }
  9093. else {
  9094. PM_INC_CTR_SERVICE(PMTotalInst, DSPollsWOChanges, 1);
  9095. }
  9096. //
  9097. // Don't use the config if the DS is in flux unless
  9098. // this is the first successful polling cycle.
  9099. //
  9100. if (DsPollingInterval != DsPollingShortInterval &&
  9101. LastChange && ThisChange != LastChange) {
  9102. DPRINT(4, ":DS: Skipping noisy topology\n");
  9103. LastChange = ThisChange;
  9104. //
  9105. // Check for a stable DS configuration after a short interval
  9106. //
  9107. DsPollingInterval = DsPollingShortInterval;
  9108. goto CLEANUP;
  9109. } else {
  9110. LastChange = ThisChange;
  9111. }
  9112. //
  9113. // No reason to continue polling the DS quickly; we have all
  9114. // of the stable information currently in the DS.
  9115. //
  9116. // DsPollingInterval = DsPollingLongInterval;
  9117. //
  9118. // Don't process the same topology repeatedly
  9119. //
  9120. // NTRAID#23652-2000/03/29-sudarc (Perf - FRS merges the DS configuration
  9121. // with its interval DB everytime it polls.)
  9122. //
  9123. // *Note*: Disable ActiveChange for now; too unreliable and too
  9124. // many error conditions in replica.c, createdb.c, ...
  9125. // besides, configs are so small that cpu savings are minimal
  9126. // plus; rejoins not issued every startreplica()
  9127. //
  9128. ActiveChange = 0;
  9129. if (ActiveChange && NextChange == ActiveChange) {
  9130. DPRINT(4, ":DS: Skipping previously processed topology\n");
  9131. goto CLEANUP;
  9132. }
  9133. //
  9134. // *Note*: Inconsistencies detected below should reset ActiveChange
  9135. // to 0 if the inconsistencies may clear up with time and
  9136. // don't require a DS change to clear up or fix
  9137. //
  9138. ActiveChange = NextChange;
  9139. //
  9140. // Check for valid paths
  9141. //
  9142. FrsDsCheckServerPaths(Services);
  9143. //
  9144. // Create the server principal name for each cxtion
  9145. //
  9146. FrsDsCreatePartnerPrincName(Services);
  9147. //
  9148. // Check the schedules
  9149. //
  9150. FrsDsCheckSchedules(Services);
  9151. FrsDsCheckSchedules(Computer);
  9152. //
  9153. // Now comes the tricky part. The above checks were made without
  9154. // regard to a nodes consistency. Now is the time to propagate
  9155. // inconsistencies throughout the tree to avoid inconsistencies
  9156. // caused by inconsistencies. E.g., a valid cxtion with an
  9157. // inconsistent partner.
  9158. //
  9159. //
  9160. // Push the parent's inconsistent state to its children
  9161. //
  9162. FrsDsPushInConsistenciesDown(Services);
  9163. //
  9164. // Merge the new config with the active replicas
  9165. //
  9166. DPRINT(4, ":DS: Begin merging Ds with Db\n");
  9167. FrsDsMergeConfigWithReplicas(gLdap, Services);
  9168. DPRINT(4, ":DS: End merging Ds with Db\n");
  9169. //
  9170. // Periodically check the local resources like disk space etc.
  9171. //
  9172. FrsCheckLocalResources();
  9173. if(NeedNewPartnerTable) {
  9174. //
  9175. // Clear the flag
  9176. //
  9177. NeedNewPartnerTable = FALSE;
  9178. FrsDsCreateNewValidPartnerTableStruct();
  9179. }
  9180. FrsDsCleanupOldValidPartnerTableStructList();
  9181. CLEANUP:
  9182. //
  9183. // Free the tables that were pointing into the tree.
  9184. // This just frees the entries in the table not the nodes.
  9185. // but the nodes can not be freed before freeing the
  9186. // tables as the compare functions are needed while.
  9187. // emptying the table.
  9188. //
  9189. SubscriberTable = GTabFreeTable(SubscriberTable, NULL);
  9190. SetTable = GTabFreeTable(SetTable, NULL);
  9191. CxtionTable = GTabFreeTable(CxtionTable, NULL);
  9192. AllCxtionsTable = GTabFreeTable(AllCxtionsTable, NULL);
  9193. //
  9194. // Members of the PartnerComputerTable need to be freed seperately
  9195. // as they are not part of the tree. So call FrsFreeType for
  9196. // each node.
  9197. //
  9198. PartnerComputerTable = GTabFreeTable(PartnerComputerTable, FrsFreeType);
  9199. MemberTable = GTabFreeTable(MemberTable, NULL);
  9200. if (MemberSearchFilter != NULL) {
  9201. MemberSearchFilter = FrsFree(MemberSearchFilter);
  9202. }
  9203. //
  9204. // Free the incore resources of the config retrieved from the DS
  9205. //
  9206. FrsDsFreeTree(Services);
  9207. FrsDsFreeTree(Computer);
  9208. if (!WIN_SUCCESS(WStatus)) {
  9209. FrsDsCloseDs();
  9210. }
  9211. //
  9212. // If there were any errors or warnings generated during this poll then
  9213. // write the summary to the eventlog.
  9214. //
  9215. if ((DsPollSummaryBuf != NULL) && (DsPollSummaryBufLen > 0)) {
  9216. EPRINT2(EVENT_FRS_DS_POLL_ERROR_SUMMARY,
  9217. (IsADc) ? ComputerDnsName :
  9218. (DsDomainControllerName ? DsDomainControllerName : L"<null>"),
  9219. DsPollSummaryBuf);
  9220. DsPollSummaryBuf = FrsFree(DsPollSummaryBuf);
  9221. DsPollSummaryBufLen = 0;
  9222. DsPollSummaryMaxBufLen = 0;
  9223. }
  9224. }
  9225. DWORD
  9226. FrsDsSetDsPollingInterval(
  9227. IN ULONG UseShortInterval,
  9228. IN ULONG LongInterval,
  9229. IN ULONG ShortInterval
  9230. )
  9231. /*++
  9232. Routine Description:
  9233. Set the long and short polling intervals and kick of a new
  9234. polling cycle. If both intervals are set, then the new polling
  9235. cycle uses the short interval (short takes precedence over
  9236. long). A value of -1 sets the interval to its current value.
  9237. No new polling cycle is initiated if a polling cycle is in progress.
  9238. Arguments:
  9239. UseShortInterval - if non-zero, switch to short. Otherwise, long.
  9240. LongInterval - Long interval in minutes
  9241. ShortInterval - Short interval in minutes
  9242. Return Value:
  9243. Win32 Status
  9244. --*/
  9245. {
  9246. #undef DEBSUB
  9247. #define DEBSUB "FrsDsSetDsPollingInterval:"
  9248. DWORD WStatus;
  9249. DPRINT3(4, ":DS: Setting the polling intervals to %d/%d (use %s)\n",
  9250. LongInterval, ShortInterval, (UseShortInterval) ? "Short" : "Long");
  9251. //
  9252. // Don't change the polling intervals; simply kick off a new cycle
  9253. //
  9254. if (!LongInterval && !ShortInterval) {
  9255. DsPollingInterval = (UseShortInterval) ? DsPollingShortInterval :
  9256. DsPollingLongInterval;
  9257. SetEvent(DsPollEvent);
  9258. return ERROR_SUCCESS;
  9259. }
  9260. //
  9261. // ADJUST LONG INTERVAL
  9262. //
  9263. if (LongInterval) {
  9264. // FRS_CONFIG_SECTION\DS Polling Long Interval in Minutes
  9265. WStatus = CfgRegWriteDWord(FKC_DS_POLLING_LONG_INTERVAL,
  9266. NULL,
  9267. FRS_RKF_RANGE_SATURATE,
  9268. LongInterval);
  9269. CLEANUP_WS(4, ":DS: DS Polling Long Interval not written.", WStatus, RETURN);
  9270. //
  9271. // Adjust the long polling rate
  9272. //
  9273. DsPollingLongInterval = LongInterval * (60 * 1000);
  9274. }
  9275. //
  9276. // ADJUST SHORT INTERVAL
  9277. //
  9278. if (ShortInterval) {
  9279. //
  9280. // Sanity check
  9281. //
  9282. if (LongInterval && (ShortInterval > LongInterval)) {
  9283. ShortInterval = LongInterval;
  9284. }
  9285. // FRS_CONFIG_SECTION\DS Polling Short Interval in Minutes
  9286. WStatus = CfgRegWriteDWord(FKC_DS_POLLING_SHORT_INTERVAL,
  9287. NULL,
  9288. FRS_RKF_RANGE_SATURATE,
  9289. ShortInterval);
  9290. CLEANUP_WS(4, ":DS: DS Polling Short Interval not written.", WStatus, RETURN);
  9291. //
  9292. // Adjust the Short polling rate
  9293. //
  9294. DsPollingShortInterval = ShortInterval * (60 * 1000);
  9295. }
  9296. //
  9297. // Initiate a polling cycle
  9298. //
  9299. DsPollingInterval = (UseShortInterval) ? DsPollingShortInterval :
  9300. DsPollingLongInterval;
  9301. SetEvent(DsPollEvent);
  9302. return ERROR_SUCCESS;
  9303. RETURN:
  9304. return WStatus;
  9305. }
  9306. DWORD
  9307. FrsDsGetDsPollingInterval(
  9308. OUT ULONG *Interval,
  9309. OUT ULONG *LongInterval,
  9310. OUT ULONG *ShortInterval
  9311. )
  9312. /*++
  9313. Routine Description:
  9314. Return the current polling intervals.
  9315. Arguments:
  9316. Interval - Current interval in minutes
  9317. LongInterval - Long interval in minutes
  9318. ShortInterval - Short interval in minutes
  9319. Return Value:
  9320. Win32 Status
  9321. --*/
  9322. {
  9323. #undef DEBSUB
  9324. #define DEBSUB "FrsDsGetDsPollingInterval:"
  9325. *Interval = DsPollingInterval / (60 * 1000);
  9326. *LongInterval = DsPollingLongInterval / (60 * 1000);
  9327. *ShortInterval = DsPollingShortInterval / (60 * 1000);
  9328. return ERROR_SUCCESS;
  9329. }
  9330. #define DS_POLLING_MAX_SHORTS (8)
  9331. DWORD
  9332. FrsDsMainDsCs(
  9333. IN PVOID Ignored
  9334. )
  9335. /*++
  9336. Routine Description:
  9337. Entry point for a DS poller thread
  9338. Arguments:
  9339. Ignored
  9340. Return Value:
  9341. None.
  9342. --*/
  9343. {
  9344. #undef DEBSUB
  9345. #define DEBSUB "FrsDsMainDsCs:"
  9346. DWORD WStatus;
  9347. DWORD DsPollingShorts = 0;
  9348. HANDLE WaitHandles[2];
  9349. DPRINT(0, ":DS: DsCs is starting.\n");
  9350. //
  9351. // DsPollingLongInterval
  9352. //
  9353. CfgRegReadDWord(FKC_DS_POLLING_LONG_INTERVAL, NULL, 0, &DsPollingLongInterval);
  9354. //
  9355. // Registry is specified in minutes; convert to milliseconds
  9356. //
  9357. DsPollingLongInterval *= (60 * 1000);
  9358. //
  9359. // DsPollingShortInterval
  9360. //
  9361. CfgRegReadDWord(FKC_DS_POLLING_SHORT_INTERVAL, NULL, 0, &DsPollingShortInterval);
  9362. //
  9363. // Registry is specified in minutes; convert to milliseconds
  9364. //
  9365. DsPollingShortInterval *= (60 * 1000);
  9366. DPRINT2(4, ":DS: DS long/short polling interval is %d/%d minutes\n",
  9367. (DsPollingLongInterval / 1000) / 60,
  9368. (DsPollingShortInterval / 1000) / 60);
  9369. DsPollingInterval = DsPollingShortInterval;
  9370. //
  9371. // Initialize the client side ldap search timeout value.
  9372. //
  9373. LdapTimeout.tv_sec = LdapSearchTimeoutInMinutes * 60;
  9374. //
  9375. // Handles to wait on
  9376. //
  9377. WaitHandles[0] = DsPollEvent;
  9378. WaitHandles[1] = ShutDownEvent;
  9379. //
  9380. // Set the registry keys and values necessary for the functioning of
  9381. // PERFMON and load the counter values into the registry
  9382. //
  9383. // Moved from main.c because this function invokes another exe that
  9384. // may cause frs to exceed its service startup time limit; resulting
  9385. // in incorrect "service cannot start" messages during intensive
  9386. // cpu activity (although frs does eventually start).
  9387. //
  9388. // NTRAID#70743-2000/03/29-sudarc (Retry initialization of perfmon registry keys
  9389. // if it fails during startup.)
  9390. //
  9391. DPRINT(0, "Init Perfmon registry keys (PmInitPerfmonRegistryKeys()).\n");
  9392. WStatus = PmInitPerfmonRegistryKeys();
  9393. DPRINT_WS(0, "ERROR - PmInitPerfmonRegistryKeys();", WStatus);
  9394. DPRINT(0, ":DS: FrsDs has started.\n");
  9395. try {
  9396. try {
  9397. //
  9398. // While the service is not shutting down
  9399. //
  9400. while (!FrsIsShuttingDown && !DsIsShuttingDown) {
  9401. //
  9402. // Reload registry parameters that can change while service is
  9403. // running.
  9404. //
  9405. DbgQueryDynamicConfigParams();
  9406. //
  9407. // What is this computer's role in the domain?
  9408. //
  9409. WStatus = FrsDsGetRole();
  9410. if (WIN_SUCCESS(WStatus) && !IsAMember) {
  9411. //
  9412. // Nothing to do
  9413. // BUT dcpromo may have started us so we
  9414. // must at least keep the service running.
  9415. //
  9416. // Perhaps we could die after running awhile
  9417. // if we still aren't a member?
  9418. //
  9419. // DPRINT(4, "Not a member, shutting down\n");
  9420. // FrsIsShuttingDown = TRUE;
  9421. // SetEvent(ShutDownEvent);
  9422. // break;
  9423. }
  9424. //
  9425. // Retrieve info from the DS and merge it with the
  9426. // acitve replicas
  9427. //
  9428. DPRINT(4, ":DS: Polling the DS\n");
  9429. if (IsAMember) {
  9430. FrsDsPollDs();
  9431. }
  9432. //
  9433. // No reason to hold memory if there isn't anything
  9434. // to do but wait for another ds polling cycle.
  9435. //
  9436. if (!MainInitHasRun) {
  9437. SetProcessWorkingSetSize(ProcessHandle, (SIZE_T)-1, (SIZE_T)-1);
  9438. }
  9439. //
  9440. // Poll often if a dc
  9441. //
  9442. if (IsADc) {
  9443. DsPollingInterval = DsPollingShortInterval;
  9444. }
  9445. //
  9446. // Wait for a bit or until the service is shutdown
  9447. //
  9448. DPRINT1(4, ":DS: Poll the DS in %d minutes\n",
  9449. DsPollingInterval / (60 * 1000));
  9450. ResetEvent(DsPollEvent);
  9451. if (!FrsIsShuttingDown && !DsIsShuttingDown) {
  9452. WaitForMultipleObjects(2, WaitHandles, FALSE, DsPollingInterval);
  9453. }
  9454. //
  9455. // The long interval can be reset to insure a high
  9456. // poll rate. The short interval is temporary; go
  9457. // back to long intervals after a few short intervals.
  9458. //
  9459. if (DsPollingInterval == DsPollingShortInterval) {
  9460. if (++DsPollingShorts > DS_POLLING_MAX_SHORTS) {
  9461. DsPollingInterval = DsPollingLongInterval;
  9462. DsPollingShorts = 0;
  9463. }
  9464. } else {
  9465. DsPollingShorts = 0;
  9466. }
  9467. }
  9468. } except (EXCEPTION_EXECUTE_HANDLER) {
  9469. GET_EXCEPTION_CODE(WStatus);
  9470. DPRINT_WS(0, ":DS: DsCs exception.", WStatus);
  9471. }
  9472. } finally {
  9473. //
  9474. // Shutdown
  9475. //
  9476. if (WIN_SUCCESS(WStatus)) {
  9477. if (AbnormalTermination()) {
  9478. WStatus = ERROR_OPERATION_ABORTED;
  9479. }
  9480. }
  9481. DPRINT_WS(0, ":DS: DsCs finally.", WStatus);
  9482. FrsDsCloseDs();
  9483. SetEvent(DsShutDownComplete);
  9484. DPRINT(0, ":DS: DsCs is exiting.\n");
  9485. }
  9486. return WStatus;
  9487. }
  9488. VOID
  9489. FrsDsInitialize(
  9490. VOID
  9491. )
  9492. /*++
  9493. Routine Description:
  9494. Initialize the thread that polls the DS
  9495. Arguments:
  9496. None.
  9497. Return Value:
  9498. TRUE - DS Poller has started
  9499. FALSE - Can't poll the DS
  9500. --*/
  9501. {
  9502. #undef DEBSUB
  9503. #define DEBSUB "FrsDsInitialize:"
  9504. //
  9505. // Synchronizes with sysvol seeding
  9506. //
  9507. INITIALIZE_CRITICAL_SECTION(&MergingReplicasWithDs);
  9508. //
  9509. // Kick off the thread that polls the DS
  9510. //
  9511. ThSupCreateThread(L"FrsDs", NULL, FrsDsMainDsCs, ThSupExitWithTombstone);
  9512. }