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.

2481 lines
73 KiB

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <winldap.h>
  5. #include <schedule.h>
  6. #include <mkdsx.h>
  7. #include <frsrpc.h>
  8. #include <ntdsapi.h>
  9. //Global data
  10. BOOL bVerboseMode = FALSE;
  11. BOOL bVerboseModeSearch = FALSE;
  12. PWCHAR DcName = NULL;
  13. PLDAP pLdap = NULL;
  14. BOOL bDebugMode = FALSE;
  15. BOOL bAffectAll = FALSE;
  16. PBYTE SchedMask = NULL;
  17. PBYTE SchedOverride = NULL;
  18. //
  19. // The following come from ntdsapi.h but were not defined in win2K so for
  20. // the SP versions we define them here.
  21. //
  22. #ifndef NTDSCONN_OPT_IS_GENERATED
  23. #define NTDSCONN_OPT_IS_GENERATED ( 1 << 0 ) /* object generated by DS, not admin */
  24. #endif
  25. #ifndef NTDSCONN_OPT_TWOWAY_SYNC
  26. #define NTDSCONN_OPT_TWOWAY_SYNC ( 1 << 1 ) /* force sync in opposite direction at end of sync */
  27. #endif
  28. #ifndef NTDSCONN_OPT_OVERRIDE_NOTIFY_DEFAULT
  29. #define NTDSCONN_OPT_OVERRIDE_NOTIFY_DEFAULT (1 << 2 ) // Do not use defaults to determine notification
  30. #endif
  31. #ifndef NTDSCONN_OPT_USE_NOTIFY
  32. #define NTDSCONN_OPT_USE_NOTIFY (1 << 3) // Does source notify destination
  33. #endif
  34. //
  35. // Static arrays to conver the value of the options attribute to
  36. // string for display.
  37. //
  38. #define NTDSCONN_OPT_NUM 6
  39. DWORD NtdsConnOptions[NTDSCONN_OPT_NUM] = {0,
  40. NTDSCONN_OPT_IS_GENERATED,
  41. NTDSCONN_OPT_TWOWAY_SYNC,
  42. NTDSCONN_OPT_OVERRIDE_NOTIFY_DEFAULT,
  43. NTDSCONN_OPT_USE_NOTIFY,
  44. NTDSCONN_OPT_IGNORE_SCHEDULE_MASK};
  45. WCHAR NtdsConnOptionsStr[][MAX_PATH] = {L"Not Used",
  46. L"IsGenerated",
  47. L"TwoWaySync",
  48. L"OverrideNotifyDefault",
  49. L"UseNotify",
  50. L"IgnoreSchedule"};
  51. //
  52. // Mask of allowed connection options.
  53. //
  54. #define MKDSXE_VALID_CONN_OPTIONS_MASK (NTDSCONN_OPT_IS_GENERATED | \
  55. NTDSCONN_OPT_TWOWAY_SYNC | \
  56. NTDSCONN_OPT_OVERRIDE_NOTIFY_DEFAULT | \
  57. NTDSCONN_OPT_USE_NOTIFY | \
  58. NTDSCONN_OPT_IGNORE_SCHEDULE_MASK | \
  59. FRSCONN_PRIORITY_MASK)
  60. //
  61. // Only priority 0-7 is supported.
  62. //
  63. #define MKDSXE_MAX_CONN_PRIORITY 7
  64. VOID
  65. PrintScheduleGrid(
  66. PUCHAR ScheduleData,
  67. DWORD Mask
  68. );
  69. VOID
  70. PrintSchedule(
  71. PSCHEDULE Schedule,
  72. DWORD Mask
  73. );
  74. DWORD
  75. BindToDC (
  76. IN PWCHAR pszDC,
  77. OUT PLDAP *ppLDAP
  78. )
  79. /*++
  80. Routine Description:
  81. Sets up an LDAP connection to the specified server
  82. Arguments:
  83. pwszDC - DS DC to bind to
  84. ppLDAP - The LDAP connection information is returned here
  85. Return Value:
  86. ERROR_SUCCESS - Success
  87. --*/
  88. {
  89. DWORD dwErr = ERROR_SUCCESS;
  90. ULONG ulOptions;
  91. //
  92. // if ldap_open is called with a server name the api will call DsGetDcName
  93. // passing the server name as the domainname parm...bad, because
  94. // DsGetDcName will make a load of DNS queries based on the server name,
  95. // it is designed to construct these queries from a domain name...so all
  96. // these queries will be bogus, meaning they will waste network bandwidth,
  97. // time to fail, and worst case cause expensive on demand links to come up
  98. // as referrals/forwarders are contacted to attempt to resolve the bogus
  99. // names. By setting LDAP_OPT_AREC_EXCLUSIVE to on using ldap_set_option
  100. // after the ldap_init but before any other operation using the ldap
  101. // handle from ldap_init, the delayed connection setup will not call
  102. // DsGetDcName, just gethostbyname, or if an IP is passed, the ldap client
  103. // will detect that and use the address directly.
  104. //
  105. // *ppLDAP = ldap_open(pszDC, LDAP_PORT);
  106. *ppLDAP = ldap_init(pszDC, LDAP_PORT);
  107. if(*ppLDAP == NULL)
  108. {
  109. dwErr = ERROR_PATH_NOT_FOUND;
  110. }
  111. else
  112. {
  113. //
  114. // set the options.
  115. //
  116. ulOptions = PtrToUlong(LDAP_OPT_ON);
  117. ldap_set_option(*ppLDAP, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions);
  118. //
  119. // Do a bind...
  120. //
  121. dwErr = ldap_bind_s(*ppLDAP,
  122. NULL,
  123. NULL,
  124. LDAP_AUTH_NEGOTIATE);
  125. }
  126. return(dwErr);
  127. }
  128. PWCHAR
  129. FrsWcsDup(
  130. PWCHAR OldStr
  131. )
  132. /*++
  133. Routine Description:
  134. Duplicate a string using our memory allocater
  135. Arguments:
  136. OldArg - string to duplicate
  137. Return Value:
  138. Duplicated string. Free with FrsFree().
  139. --*/
  140. {
  141. PWCHAR NewStr;
  142. //
  143. // E.g., when duplicating NodePartner when none exists
  144. //
  145. if (OldStr == NULL)
  146. return NULL;
  147. NewStr = (PWCHAR)malloc((wcslen(OldStr) + 1) * sizeof(WCHAR));
  148. wcscpy(NewStr, OldStr);
  149. return NewStr;
  150. }
  151. VOID
  152. AddMod(
  153. PWCHAR AttrType,
  154. PWCHAR AttrValue,
  155. LDAPMod ***pppMod
  156. )
  157. /*++
  158. Routine Description:
  159. Add an attribute (plus values) to a structure that will eventually be
  160. used in an ldap_add() function to add an object to the DS. The null-
  161. terminated array referenced by pppMod grows with each call to this
  162. routine. The array is freed by the caller using FreeMod().
  163. Arguments:
  164. AttrType - The object class of the object.
  165. AttrValue - The value of the attribute.
  166. pppMod - Address of an array of pointers to "attributes". Don't
  167. give me that look -- this is an LDAP thing.
  168. Return Value:
  169. The pppMod array grows by one entry. The caller must free it with
  170. FreeMod().
  171. --*/
  172. {
  173. DWORD NumMod; // Number of entries in the Mod array
  174. LDAPMod **ppMod; // Address of the first entry in the Mod array
  175. LDAPMod *Attr; // An attribute structure
  176. PWCHAR *Values; // An array of pointers to bervals
  177. if (AttrValue == NULL)
  178. return;
  179. //
  180. // The null-terminated array doesn't exist; create it
  181. //
  182. if (*pppMod == NULL) {
  183. *pppMod = (LDAPMod **)malloc(sizeof (*pppMod));
  184. **pppMod = NULL;
  185. }
  186. //
  187. // Increase the array's size by 1
  188. //
  189. for (ppMod = *pppMod, NumMod = 2; *ppMod != NULL; ++ppMod, ++NumMod);
  190. *pppMod = (LDAPMod **)realloc(*pppMod, sizeof (*pppMod) * NumMod);
  191. //
  192. // Add the new attribute + value to the Mod array
  193. //
  194. Values = (PWCHAR *)malloc(sizeof (PWCHAR) * 2);
  195. Values[0] = _wcsdup(AttrValue);
  196. Values[1] = NULL;
  197. Attr = (LDAPMod *)malloc(sizeof (*Attr));
  198. Attr->mod_values = Values;
  199. Attr->mod_type = _wcsdup(AttrType);
  200. // Attr->mod_op = LDAP_MOD_ADD;
  201. Attr->mod_op = LDAP_MOD_REPLACE;
  202. (*pppMod)[NumMod - 1] = NULL;
  203. (*pppMod)[NumMod - 2] = Attr;
  204. }
  205. VOID
  206. AddBerMod(
  207. PWCHAR AttrType,
  208. PCHAR AttrValue,
  209. DWORD AttrValueLen,
  210. LDAPMod ***pppMod
  211. )
  212. /*++
  213. Routine Description:
  214. Add an attribute (plus values) to a structure that will eventually be
  215. used in an ldap_add() function to add an object to the DS. The null-
  216. terminated array referenced by pppMod grows with each call to this
  217. routine. The array is freed by the caller using FreeMod().
  218. Arguments:
  219. AttrType - The object class of the object.
  220. AttrValue - The value of the attribute.
  221. AttrValueLen - length of the attribute
  222. pppMod - Address of an array of pointers to "attributes". Don't
  223. give me that look -- this is an LDAP thing.
  224. Return Value:
  225. The pppMod array grows by one entry. The caller must free it with
  226. FreeMod().
  227. --*/
  228. {
  229. DWORD NumMod; // Number of entries in the Mod array
  230. LDAPMod **ppMod; // Address of the first entry in the Mod array
  231. LDAPMod *Attr; // An attribute structure
  232. PLDAP_BERVAL Berval;
  233. PLDAP_BERVAL *Values; // An array of pointers to bervals
  234. if (AttrValue == NULL)
  235. return;
  236. //
  237. // The null-terminated array doesn't exist; create it
  238. //
  239. if (*pppMod == NULL) {
  240. *pppMod = (LDAPMod **)malloc(sizeof (*pppMod));
  241. **pppMod = NULL;
  242. }
  243. //
  244. // Increase the array's size by 1
  245. //
  246. for (ppMod = *pppMod, NumMod = 2; *ppMod != NULL; ++ppMod, ++NumMod);
  247. *pppMod = (LDAPMod **)realloc(*pppMod, sizeof (*pppMod) * NumMod);
  248. //
  249. // Construct a berval
  250. //
  251. Berval = (PLDAP_BERVAL)malloc(sizeof(LDAP_BERVAL));
  252. Berval->bv_len = AttrValueLen;
  253. Berval->bv_val = (PCHAR)malloc(AttrValueLen);
  254. CopyMemory(Berval->bv_val, AttrValue, AttrValueLen);
  255. //
  256. // Add the new attribute + value to the Mod array
  257. //
  258. Values = (PLDAP_BERVAL *)malloc(sizeof (PLDAP_BERVAL) * 2);
  259. Values[0] = Berval;
  260. Values[1] = NULL;
  261. Attr = (LDAPMod *)malloc(sizeof (*Attr));
  262. Attr->mod_bvalues = Values;
  263. Attr->mod_type = _wcsdup(AttrType);
  264. Attr->mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  265. (*pppMod)[NumMod - 1] = NULL;
  266. (*pppMod)[NumMod - 2] = Attr;
  267. }
  268. VOID
  269. FreeMod(
  270. LDAPMod ***pppMod
  271. )
  272. /*++
  273. Routine Description:
  274. Free the structure built by successive calls to AddMod().
  275. Arguments:
  276. pppMod - Address of a null-terminated array.
  277. Return Value:
  278. *pppMod set to NULL.
  279. --*/
  280. {
  281. DWORD i, j;
  282. LDAPMod **ppMod;
  283. if (!pppMod || !*pppMod) {
  284. return;
  285. }
  286. //
  287. // For each attibute
  288. //
  289. ppMod = *pppMod;
  290. for (i = 0; ppMod[i] != NULL; ++i) {
  291. //
  292. // For each value of the attribute
  293. //
  294. for (j = 0; (ppMod[i])->mod_values[j] != NULL; ++j) {
  295. //
  296. // Free the value
  297. //
  298. if (ppMod[i]->mod_op & LDAP_MOD_BVALUES) {
  299. free(ppMod[i]->mod_bvalues[j]->bv_val);
  300. }
  301. free((ppMod[i])->mod_values[j]);
  302. }
  303. free((ppMod[i])->mod_values); // Free the array of pointers to values
  304. free((ppMod[i])->mod_type); // Free the string identifying the attribute
  305. free(ppMod[i]); // Free the attribute
  306. }
  307. free(ppMod); // Free the array of pointers to attributes
  308. *pppMod = NULL; // Now ready for more calls to AddMod()
  309. }
  310. BOOL
  311. LdapSearch(
  312. PLDAP ldap,
  313. PWCHAR Base,
  314. ULONG Scope,
  315. PWCHAR Filter,
  316. PWCHAR Attrs[],
  317. ULONG AttrsOnly,
  318. LDAPMessage **Res,
  319. BOOL Quiet
  320. )
  321. /*++
  322. Routine Description:
  323. Issue the ldap ldap_search_s call, check for errors, and check for
  324. a shutdown in progress.
  325. Arguments:
  326. ldap
  327. Base
  328. Scope
  329. Filter
  330. Attrs
  331. AttrsOnly
  332. Res
  333. Return Value:
  334. The ldap array of values or NULL if the Base, DesiredAttr, or its values
  335. does not exist. The ldap array is freed with ldap_value_free().
  336. --*/
  337. {
  338. DWORD LStatus;
  339. //
  340. // Issue the ldap search
  341. //
  342. LStatus = ldap_search_s(ldap,
  343. Base,
  344. Scope,
  345. Filter,
  346. Attrs,
  347. AttrsOnly,
  348. Res);
  349. //
  350. // Check for errors
  351. //
  352. if (LStatus != LDAP_SUCCESS) {
  353. if (!Quiet) {
  354. DPRINT4("WARN - Error searching %ws for %ws; LStatus %d: %ws\n",
  355. Base, Filter, LStatus, ldap_err2string(LStatus));
  356. }
  357. return FALSE;
  358. }
  359. //
  360. // Return TRUE if not shutting down
  361. //
  362. return TRUE;
  363. }
  364. PWCHAR *
  365. GetValues(
  366. IN PLDAP Ldap,
  367. IN PWCHAR Dn,
  368. IN PWCHAR DesiredAttr,
  369. IN BOOL Quiet
  370. )
  371. /*++
  372. Routine Description:
  373. Return the DS values for one attribute in an object.
  374. Arguments:
  375. ldap - An open, bound ldap port.
  376. Base - The "pathname" of a DS object.
  377. DesiredAttr - Return values for this attribute.
  378. Quiet
  379. Return Value:
  380. An array of char pointers that represents the values for the attribute.
  381. The caller must free the array with ldap_value_free(). NULL if unsuccessful.
  382. --*/
  383. {
  384. PWCHAR Attr;
  385. BerElement *Ber;
  386. PLDAPMessage LdapMsg;
  387. PLDAPMessage LdapEntry;
  388. PWCHAR Attrs[2];
  389. PWCHAR *Values = NULL;
  390. //
  391. // Search Base for all of its attributes + values
  392. //
  393. Attrs[0] = DesiredAttr;
  394. Attrs[1] = NULL;
  395. //
  396. // Issue the ldap search
  397. //
  398. if (!LdapSearch(Ldap,
  399. Dn,
  400. LDAP_SCOPE_BASE,
  401. CLASS_ANY,
  402. Attrs,
  403. 0,
  404. &LdapMsg,
  405. Quiet)) {
  406. return NULL;
  407. }
  408. LdapEntry = ldap_first_entry(Ldap, LdapMsg);
  409. if (LdapEntry) {
  410. Attr = ldap_first_attribute(Ldap, LdapEntry, &Ber);
  411. if (Attr) {
  412. Values = ldap_get_values(Ldap, LdapEntry, Attr);
  413. }
  414. }
  415. ldap_msgfree(LdapMsg);
  416. return Values;
  417. }
  418. BOOL
  419. LdapSearchInit(
  420. PLDAP ldap,
  421. PWCHAR Base,
  422. ULONG Scope,
  423. PWCHAR Filter,
  424. PWCHAR Attrs[],
  425. ULONG AttrsOnly,
  426. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext,
  427. BOOL Quiet
  428. )
  429. /*++
  430. Routine Description:
  431. Issue the ldap ldap_search_s call, check for errors, and check for
  432. a shutdown in progress.
  433. Arguments:
  434. ldap
  435. Base
  436. Scope
  437. Filter
  438. Attrs
  439. AttrsOnly
  440. Res
  441. FrsSearchContext
  442. Return Value:
  443. The ldap array of values or NULL if the Base, DesiredAttr, or its values
  444. does not exist. The ldap array is freed with ldap_value_free().
  445. --*/
  446. {
  447. DWORD LStatus = LDAP_SUCCESS;
  448. PLDAPControl ServerControls[2];
  449. berval cookie1 = { 0, NULL };
  450. FrsSearchContext->bOpen = FALSE;
  451. FrsSearchContext->LdapMsg = NULL;
  452. FrsSearchContext->EntriesInPage = 0;
  453. FrsSearchContext->CurrentEntry = 0;
  454. FrsSearchContext->TotalEntries = 0;
  455. FrsSearchContext->Filter = FrsWcsDup(Filter);
  456. FrsSearchContext->BaseDn = FrsWcsDup(Base);
  457. FrsSearchContext->Scope = Scope;
  458. FrsSearchContext->PageSize = FRS_LDAP_SEARCH_PAGESIZE;
  459. FrsSearchContext->Attrs = Attrs;
  460. LStatus = ldap_create_page_control(ldap,
  461. FrsSearchContext->PageSize,
  462. &cookie1,
  463. FALSE, // is critical
  464. &ServerControls[0]
  465. );
  466. ServerControls[1] = NULL;
  467. if (LStatus != LDAP_SUCCESS) {
  468. DPRINT1("ldap_create_page_control returned error. LStatus = 0x%x\n", LStatus);
  469. }
  470. LStatus = ldap_search_ext_s(ldap,
  471. FrsSearchContext->BaseDn,
  472. FrsSearchContext->Scope,
  473. FrsSearchContext->Filter,
  474. FrsSearchContext->Attrs,
  475. FALSE,
  476. ServerControls,
  477. NULL,
  478. NULL,
  479. 0,
  480. &FrsSearchContext->LdapMsg);
  481. if (LStatus == LDAP_SUCCESS) {
  482. FrsSearchContext->EntriesInPage = ldap_count_entries(ldap, FrsSearchContext->LdapMsg);
  483. FrsSearchContext->CurrentEntry = 0;
  484. FrsSearchContext->bOpen = TRUE;
  485. }
  486. return (LStatus == LDAP_SUCCESS);
  487. }
  488. PLDAPMessage
  489. LdapSearchGetNextEntry(
  490. PLDAP ldap,
  491. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  492. )
  493. /*++
  494. Routine Description:
  495. Issue the ldap ldap_search_s call, check for errors, and check for
  496. a shutdown in progress.
  497. Arguments:
  498. ldap
  499. FrsSearchContext
  500. Return Value:
  501. The ldap array of values or NULL if the Base, DesiredAttr, or its values
  502. does not exist. The ldap array is freed with ldap_value_free().
  503. --*/
  504. {
  505. FrsSearchContext->CurrentEntry += 1;
  506. if ( FrsSearchContext->CurrentEntry == 1 ) {
  507. FrsSearchContext->CurrentLdapMsg = ldap_first_entry(ldap ,FrsSearchContext->LdapMsg);
  508. } else {
  509. FrsSearchContext->CurrentLdapMsg = ldap_next_entry(ldap ,FrsSearchContext->CurrentLdapMsg);
  510. }
  511. return FrsSearchContext->CurrentLdapMsg;
  512. }
  513. DWORD
  514. LdapSearchGetNextPage(
  515. PLDAP ldap,
  516. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  517. )
  518. /*++
  519. Routine Description:
  520. Issue the ldap ldap_search_s call, check for errors, and check for
  521. a shutdown in progress.
  522. Arguments:
  523. ldap
  524. FrsSearchContext
  525. Return Value:
  526. The ldap array of values or NULL if the Base, DesiredAttr, or its values
  527. does not exist. The ldap array is freed with ldap_value_free().
  528. --*/
  529. {
  530. DWORD LStatus = LDAP_SUCCESS;
  531. berval * CurrCookie = NULL;
  532. PLDAPControl * CurrControls = NULL;
  533. ULONG retcode = 0;
  534. PLDAPControl ServerControls[2];
  535. // Get the server control from the message, and make a new control with the cookie from the server
  536. LStatus = ldap_parse_result(ldap, FrsSearchContext->LdapMsg, &retcode,NULL,NULL,NULL,&CurrControls,FALSE);
  537. ldap_msgfree(FrsSearchContext->LdapMsg);
  538. FrsSearchContext->LdapMsg = NULL;
  539. if (LStatus == LDAP_SUCCESS) {
  540. LStatus = ldap_parse_page_control(ldap, CurrControls, &FrsSearchContext->TotalEntries, &CurrCookie);
  541. // under Exchange 5.5, before SP 2, this will fail with LDAP_CONTROL_NOT_FOUND when there are
  542. // no more search results. With Exchange 5.5 SP 2, this succeeds, and gives us a cookie that will
  543. // cause us to start over at the beginning of the search results.
  544. }
  545. if (LStatus == LDAP_SUCCESS) {
  546. if ( CurrCookie->bv_len == 0 && CurrCookie->bv_val == 0 ) {
  547. // under Exchange 5.5, SP 2, this means we're at the end of the results.
  548. // if we pass in this cookie again, we will start over at the beginning of the search results.
  549. LStatus = LDAP_CONTROL_NOT_FOUND;
  550. }
  551. ServerControls[0] = NULL;
  552. ServerControls[1] = NULL;
  553. if (LStatus == LDAP_SUCCESS) {
  554. LStatus = ldap_create_page_control(ldap,
  555. FrsSearchContext->PageSize,
  556. CurrCookie,
  557. FALSE,
  558. ServerControls);
  559. }
  560. ldap_controls_free(CurrControls);
  561. CurrControls = NULL;
  562. ber_bvfree(CurrCookie);
  563. CurrCookie = NULL;
  564. }
  565. // continue the search with the new cookie
  566. if (LStatus == LDAP_SUCCESS) {
  567. LStatus = ldap_search_ext_s(ldap,
  568. FrsSearchContext->BaseDn,
  569. FrsSearchContext->Scope,
  570. FrsSearchContext->Filter,
  571. FrsSearchContext->Attrs,
  572. FALSE,
  573. ServerControls,
  574. NULL,
  575. NULL,
  576. 0,
  577. &FrsSearchContext->LdapMsg);
  578. if ( (LStatus != LDAP_SUCCESS) && (LStatus != LDAP_CONTROL_NOT_FOUND) )
  579. {
  580. // LDAP_CONTROL_NOT_FOUND means that we have reached the end of the search results
  581. // in Exchange 5.5, before SP 2 (the server doesn't return a page control when there
  582. // are no more pages, so we get LDAP_CONTROL_NOT_FOUND when we try to extract the page
  583. // control from the search results).
  584. }
  585. }
  586. if (LStatus == LDAP_SUCCESS) {
  587. FrsSearchContext->EntriesInPage = ldap_count_entries(ldap, FrsSearchContext->LdapMsg);
  588. FrsSearchContext->CurrentEntry = 0;
  589. }
  590. return LdapMapErrorToWin32(LStatus);
  591. }
  592. PLDAPMessage
  593. LdapSearchNext(
  594. PLDAP ldap,
  595. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  596. )
  597. /*++
  598. Routine Description:
  599. Issue the ldap ldap_search_s call, check for errors, and check for
  600. a shutdown in progress.
  601. Arguments:
  602. ldap
  603. FrsSearchContext
  604. Return Value:
  605. The ldap array of values or NULL if the Base, DesiredAttr, or its values
  606. does not exist. The ldap array is freed with ldap_value_free().
  607. --*/
  608. {
  609. DWORD WStatus = ERROR_SUCCESS;
  610. PLDAPMessage NextEntry = NULL;
  611. if (FrsSearchContext->bOpen != TRUE)
  612. {
  613. NextEntry = NULL;
  614. }
  615. else
  616. {
  617. if (FrsSearchContext->EntriesInPage > FrsSearchContext->CurrentEntry )
  618. {
  619. // return the next entry from the current page
  620. return LdapSearchGetNextEntry(ldap, FrsSearchContext);
  621. }
  622. else
  623. {
  624. // see if there are more pages of results to get
  625. WStatus = LdapSearchGetNextPage(ldap, FrsSearchContext);
  626. if (WStatus == ERROR_SUCCESS)
  627. {
  628. return LdapSearchGetNextEntry(ldap, FrsSearchContext);
  629. }
  630. }
  631. }
  632. return NextEntry;
  633. }
  634. VOID
  635. LdapSearchClose(
  636. PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
  637. )
  638. /*++
  639. Routine Description:
  640. Issue the ldap ldap_search_s call, check for errors, and check for
  641. a shutdown in progress.
  642. Arguments:
  643. ldap
  644. FrsSearchContext
  645. Return Value:
  646. The ldap array of values or NULL if the Base, DesiredAttr, or its values
  647. does not exist. The ldap array is freed with ldap_value_free().
  648. --*/
  649. {
  650. FrsSearchContext->bOpen = FALSE;
  651. FrsSearchContext->EntriesInPage = 0;
  652. FrsSearchContext->CurrentEntry = 0;
  653. FrsSearchContext->TotalEntries = 0;
  654. FREE(FrsSearchContext->Filter);
  655. FREE(FrsSearchContext->BaseDn);
  656. if (FrsSearchContext->LdapMsg != NULL) {
  657. ldap_msgfree(FrsSearchContext->LdapMsg);
  658. }
  659. }
  660. PWCHAR
  661. GetRootDn(
  662. PLDAP Ldap,
  663. PWCHAR NamingContext
  664. )
  665. /*++
  666. Routine Description:
  667. Arguments:
  668. Return Value:
  669. --*/
  670. {
  671. PWCHAR Root; // DS pathname of configuration container
  672. PWCHAR *Values; // values from the attribute "namingContexts"
  673. DWORD NumVals; // number of values
  674. //
  675. // Return all of the values for the attribute namingContexts
  676. //
  677. Values = GetValues(Ldap, ATTR_ROOT, ATTR_NAMING_CONTEXTS, FALSE);
  678. if (Values == NULL)
  679. return NULL;
  680. //
  681. // Find the naming context that begins with CN=Configuration
  682. //
  683. NumVals = ldap_count_values(Values);
  684. while (NumVals--) {
  685. Root = wcsstr(Values[NumVals], NamingContext);
  686. if (Root != NULL && Root == Values[NumVals]) {
  687. Root = FrsWcsDup(Root);
  688. ldap_value_free(Values);
  689. return Root;
  690. }
  691. }
  692. DPRINT1("ERROR - COULD NOT FIND %ws\n", NamingContext);
  693. ldap_value_free(Values);
  694. return NULL;
  695. }
  696. PWCHAR
  697. ExtendDn(
  698. PWCHAR Dn,
  699. PWCHAR Cn
  700. )
  701. /*++
  702. Routine Description:
  703. Extend an existing DN with a new CN= component.
  704. Arguments:
  705. Dn - distinguished name
  706. Cn - common name
  707. Return Value:
  708. CN=Cn,Dn
  709. --*/
  710. {
  711. ULONG Len;
  712. PWCHAR NewDn;
  713. Len = wcslen(L"CN=,") + wcslen(Dn) + wcslen(Cn) + 1;
  714. NewDn = (PWCHAR)malloc(Len * sizeof(WCHAR));
  715. wcscpy(NewDn, L"CN=");
  716. wcscat(NewDn, Cn);
  717. wcscat(NewDn, L",");
  718. wcscat(NewDn, Dn);
  719. return NewDn;
  720. }
  721. PVOID *
  722. FindValues(
  723. PLDAPMessage LdapEntry,
  724. PWCHAR DesiredAttr,
  725. BOOL DoBerValues
  726. )
  727. /*++
  728. Routine Description:
  729. Return the DS values for one attribute in an entry.
  730. Arguments:
  731. Ldap - An open, bound ldap port.
  732. LdapEntry - An ldap entry returned by ldap_search_s()
  733. DesiredAttr - Return values for this attribute.
  734. DoVerValues - Return the bervals
  735. Return Value:
  736. An array of char pointers that represents the values for the attribute.
  737. The caller must free the array with ldap_value_free(). NULL if unsuccessful.
  738. --*/
  739. {
  740. PWCHAR LdapAttr; // Retrieved from an ldap entry
  741. BerElement *Ber; // Needed for scanning attributes
  742. //
  743. // Search the entry for the desired attribute
  744. //
  745. for (LdapAttr = ldap_first_attribute(pLdap, LdapEntry, &Ber);
  746. LdapAttr != NULL;
  747. LdapAttr = ldap_next_attribute(pLdap, LdapEntry, Ber)) {
  748. if (_wcsicmp(DesiredAttr, LdapAttr) == 0) {
  749. //
  750. // Return the values for DesiredAttr
  751. //
  752. if (DoBerValues) {
  753. return (PVOID *)ldap_get_values_len(pLdap, LdapEntry, LdapAttr);
  754. } else {
  755. return (PVOID *)ldap_get_values(pLdap, LdapEntry, LdapAttr);
  756. }
  757. }
  758. }
  759. return NULL;
  760. }
  761. PWCHAR
  762. FindValue(
  763. PLDAPMessage LdapEntry,
  764. PWCHAR DesiredAttr
  765. )
  766. /*++
  767. Routine Description:
  768. Return a copy of the first DS value for one attribute in an entry.
  769. Arguments:
  770. Ldap - An open, bound ldap port.
  771. LdapEntry - An ldap entry returned by ldap_search_s()
  772. DesiredAttr - Return values for this attribute.
  773. Return Value:
  774. A zero-terminated string or NULL if the attribute or its value
  775. doesn't exist. The string is freed with FREE().
  776. --*/
  777. {
  778. PWCHAR Val;
  779. PWCHAR *Values;
  780. // Get ldap's array of values
  781. Values = (PWCHAR *)FindValues(LdapEntry, DesiredAttr, FALSE);
  782. // Copy the first value (if any)
  783. if (Values) {
  784. Val = new WCHAR[wcslen(Values[0]) + 1];
  785. wcscpy(Val, Values[0]);
  786. } else {
  787. Val = NULL;
  788. }
  789. // Free ldap's array of values
  790. ldap_value_free(Values);
  791. return Val;
  792. }
  793. BOOL
  794. FindBerValue(
  795. PLDAPMessage Entry,
  796. PWCHAR DesiredAttr,
  797. ULONG *Len,
  798. VOID **Value
  799. )
  800. /*++
  801. Routine Description:
  802. Return a copy of the attributes object's schedule
  803. Arguments:
  804. ldap - An open, bound ldap port.
  805. Entry - An ldap entry returned by ldap_search_s()
  806. DesiredAttr - desired attribute
  807. Len - length of Value
  808. Value - binary value
  809. Return Value:
  810. The address of a schedule or NULL. Free with FrsFree().
  811. --*/
  812. {
  813. PLDAP_BERVAL *Values;
  814. *Len = 0;
  815. *Value = NULL;
  816. //
  817. // Get ldap's array of values
  818. //
  819. Values = (PLDAP_BERVAL *)FindValues(Entry, DesiredAttr, TRUE);
  820. if (!Values) {
  821. return FALSE;
  822. }
  823. //
  824. // Return a copy of the schedule
  825. //
  826. *Len = Values[0]->bv_len;
  827. if (*Len) {
  828. *Value = new WCHAR[*Len];
  829. CopyMemory(*Value, Values[0]->bv_val, *Len);
  830. }
  831. ldap_value_free_len(Values);
  832. return TRUE;
  833. }
  834. DWORD
  835. FormMemberDn(
  836. IN PWCHAR ReplicaSetName,
  837. IN PWCHAR ComputerDnsName,
  838. OUT PWCHAR *pMemberDn
  839. )
  840. /*++
  841. Routine Description:
  842. Form the member Dn given the computername and the replica set name.
  843. Arguments:
  844. Return Value:
  845. ERROR_SUCCESS - Success
  846. --*/
  847. {
  848. DWORD Status = ERROR_SUCCESS;
  849. WCHAR SearchFilter[MAX_PATH];
  850. ULONG NoOfSets = 0;
  851. PLDAPMessage LdapEntry = NULL;
  852. PWCHAR ConfigDn = NULL;
  853. PWCHAR ServicesDn = NULL;
  854. PWCHAR FrsTestDn = NULL;
  855. PWCHAR DomainDn = NULL;
  856. PWCHAR SystemDn = NULL;
  857. PWCHAR FrsDn = NULL;
  858. PWCHAR SetDn = NULL;
  859. PWCHAR MemberDn = NULL;
  860. PWCHAR ComputerDn = NULL;
  861. PWCHAR *Values = NULL;
  862. PWCHAR Attrs[3];
  863. PWCHAR Attrs2[4];
  864. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  865. Attrs[0] = ATTR_DN;
  866. Attrs[1] = ATTR_CN;
  867. Attrs[2] = NULL;
  868. DomainDn = GetRootDn(pLdap, DOMAIN_NAMING_CONTEXT);
  869. if (DomainDn == NULL) {
  870. if (bVerboseModeSearch) printf("GetRootDn-1 returned NULL\n");
  871. return MKDSXE_NO_T0_NTDS_SETTINGS;
  872. }
  873. SystemDn = ExtendDn(DomainDn, CN_SYSTEM);
  874. FrsDn = ExtendDn(SystemDn, CN_NTFRS_SETTINGS);
  875. wcscpy(SearchFilter, L"(&(");
  876. wcscat(SearchFilter, ATTR_CN);
  877. wcscat(SearchFilter, L"=");
  878. wcscat(SearchFilter, ReplicaSetName);
  879. wcscat(SearchFilter, L")");
  880. wcscat(SearchFilter, CLASS_NTFRS_REPLICA_SET);
  881. wcscat(SearchFilter, L")");
  882. if (bVerboseModeSearch) printf("\nSubtree search at: %ws\n", FrsDn);
  883. if (bVerboseModeSearch) printf("Filter : %ws\n", SearchFilter);
  884. if (!LdapSearchInit(pLdap,
  885. FrsDn,
  886. LDAP_SCOPE_SUBTREE,
  887. SearchFilter,
  888. Attrs,
  889. 0,
  890. &FrsSearchContext,
  891. FALSE)) {
  892. Status = MKDSXE_NO_T0_NTDS_SETTINGS ;
  893. if (bVerboseModeSearch) printf("Subtree-1 search failed\n");
  894. goto RETURN;
  895. }
  896. NoOfSets = FrsSearchContext.EntriesInPage;
  897. if (NoOfSets == 0) {
  898. LdapSearchClose(&FrsSearchContext);
  899. //
  900. // Replica set was not found under system. Look under
  901. // CN=NTFRS Test Settings,CN=Services,CN=Configuration,DC=...
  902. //
  903. ConfigDn = GetRootDn(pLdap, CONFIG_NAMING_CONTEXT);
  904. if (ConfigDn == NULL) {
  905. if (bVerboseModeSearch) printf("GetRootDn-2 returned NULL\n");
  906. Status = MKDSXE_NO_T0_NTDS_SETTINGS;
  907. goto RETURN;
  908. }
  909. ServicesDn = ExtendDn(ConfigDn, CN_SERVICES);
  910. FrsTestDn = ExtendDn(ServicesDn, CN_TEST_SETTINGS);
  911. if (bVerboseModeSearch) printf("\nSubtree search at: %ws\n", FrsTestDn);
  912. if (bVerboseModeSearch) printf("Filter : %ws\n", SearchFilter);
  913. if (!LdapSearchInit(pLdap,
  914. FrsTestDn,
  915. LDAP_SCOPE_SUBTREE,
  916. SearchFilter,
  917. Attrs,
  918. 0,
  919. &FrsSearchContext,
  920. FALSE)) {
  921. Status = MKDSXE_NO_T0_NTDS_SETTINGS ;
  922. if (bVerboseModeSearch) printf("Subtree-2 search failed\n");
  923. goto RETURN;
  924. }
  925. NoOfSets = FrsSearchContext.EntriesInPage;
  926. }
  927. if (NoOfSets != 1) {
  928. DPRINT0("Error finding replica set.\n");
  929. return MKDSXE_NO_T0_NTDS_SETTINGS;
  930. }
  931. //
  932. // Scan the entries returned from ldap_search
  933. //
  934. for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
  935. LdapEntry != NULL;
  936. LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
  937. SetDn = FindValue(LdapEntry, ATTR_DN);
  938. }
  939. DPRINT1("Replica Set Dn:%ws\n", SetDn);
  940. LdapSearchClose(&FrsSearchContext);
  941. Attrs2[0] = ATTR_DN;
  942. Attrs2[1] = ATTR_CN;
  943. Attrs2[2] = ATTR_COMPUTER_REF;
  944. Attrs2[3] = NULL;
  945. if (bVerboseModeSearch) printf("\nSubtree search at: %ws\n", SetDn);
  946. if (bVerboseModeSearch) printf("Filter : %ws\n", CLASS_MEMBER);
  947. if (!LdapSearchInit(pLdap,
  948. SetDn,
  949. LDAP_SCOPE_ONELEVEL,
  950. CLASS_MEMBER,
  951. Attrs2,
  952. 0,
  953. &FrsSearchContext,
  954. FALSE)) {
  955. Status = MKDSXE_NO_T0_NTDS_SETTINGS;
  956. if (bVerboseModeSearch) printf("Error finding member.\n");
  957. goto RETURN;
  958. }
  959. //
  960. // Scan the entries returned from ldap_search
  961. //
  962. for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
  963. LdapEntry != NULL;
  964. LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
  965. MemberDn = FindValue(LdapEntry, ATTR_DN);
  966. ComputerDn = FindValue(LdapEntry, ATTR_COMPUTER_REF);
  967. if (bVerboseModeSearch) printf("\n MemberDN scan : %ws\n", MemberDn);
  968. if (bVerboseModeSearch) printf(" ComputerDn is : %ws\n", (ComputerDn == NULL) ? L"null" : ComputerDn);
  969. if (ComputerDn != NULL) {
  970. Values = GetValues(pLdap, ComputerDn, ATTR_DNS_HOST_NAME, TRUE);
  971. FREE(ComputerDn);
  972. if (bVerboseModeSearch) printf(" Computer DNS is : %ws\n", (Values != NULL) && (Values[0] != NULL) ? Values[0] : L"null");
  973. if ((Values != NULL) && (Values[0] != NULL) && !_wcsicmp(Values[0], ComputerDnsName)) {
  974. ldap_value_free(Values);
  975. *pMemberDn = FrsWcsDup(MemberDn);
  976. FREE(MemberDn);
  977. break;
  978. }
  979. }
  980. FREE(MemberDn);
  981. }
  982. if (*pMemberDn == NULL) {
  983. if (bVerboseModeSearch) printf("No member found in scan.\n");
  984. Status = MKDSXE_NO_T0_NTDS_SETTINGS;
  985. }
  986. LdapSearchClose(&FrsSearchContext);
  987. RETURN:
  988. FREE(SetDn);
  989. FREE(FrsDn);
  990. FREE(SystemDn);
  991. FREE(DomainDn);
  992. FREE(FrsTestDn);
  993. FREE(ServicesDn);
  994. FREE(ConfigDn);
  995. return Status;
  996. }
  997. DWORD
  998. FormNTDSSettingsDn(
  999. IN PWCHAR ToSite,
  1000. IN PWCHAR ToServer,
  1001. OUT PWCHAR *pNTDSSettingsDn
  1002. )
  1003. /*++
  1004. Routine Description:
  1005. Form the settings dn for the to server.
  1006. Arguments:
  1007. Return Value:
  1008. ERROR_SUCCESS - Success
  1009. --*/
  1010. {
  1011. DWORD Status = ERROR_SUCCESS;
  1012. PWCHAR ConfigDn = NULL;
  1013. PWCHAR SitesDn = NULL;
  1014. PWCHAR ToSiteDn = NULL;
  1015. PWCHAR ServersDn = NULL;
  1016. PWCHAR ToServerDn = NULL;
  1017. PWCHAR Attrs[2];
  1018. PLDAPMessage LdapMsg;
  1019. Attrs[0] = ATTR_DN;
  1020. Attrs[1] = NULL;
  1021. ConfigDn = GetRootDn(pLdap, CONFIG_NAMING_CONTEXT);
  1022. if (ConfigDn == NULL) {
  1023. return MKDSXE_NO_T0_NTDS_SETTINGS;
  1024. }
  1025. SitesDn = ExtendDn(ConfigDn, CN_SITES);
  1026. ToSiteDn = ExtendDn(SitesDn, ToSite);
  1027. ServersDn = ExtendDn(ToSiteDn, L"Servers");
  1028. ToServerDn = ExtendDn(ServersDn, ToServer);
  1029. *pNTDSSettingsDn = ExtendDn(ToServerDn, L"NTDS Settings");
  1030. if (!LdapSearch(pLdap,
  1031. *pNTDSSettingsDn,
  1032. LDAP_SCOPE_BASE,
  1033. CLASS_NTDS_SETTINGS,
  1034. Attrs,
  1035. 0,
  1036. &LdapMsg,
  1037. TRUE)){
  1038. Status = MKDSXE_NO_T0_NTDS_SETTINGS;
  1039. }
  1040. FREE(ConfigDn);
  1041. FREE(SitesDn);
  1042. FREE(ToSiteDn);
  1043. FREE(ServersDn);
  1044. FREE(ToServerDn);
  1045. ldap_msgfree(LdapMsg);
  1046. return Status;
  1047. }
  1048. PBYTE
  1049. ReadScheduleFile(
  1050. PWCHAR FileName
  1051. )
  1052. /*++
  1053. Routine Description:
  1054. Read a Hex formated byte array for a 7x24 schedule from a file.
  1055. Convert to a 7x24 binary schedule.
  1056. Arguments:
  1057. FileName - Name of schedule file.
  1058. Return Value:
  1059. ptr to schedule data array or NULL if invalid param.
  1060. --*/
  1061. {
  1062. HANDLE FileHandle;
  1063. PBYTE SchedData;
  1064. DWORD WStatus;
  1065. DWORD BytesRead;
  1066. DWORD DataByte, i, Day, Hour;
  1067. ULONG Remaining;
  1068. PCHAR pTC;
  1069. CHAR SchedText[FRST_SIZE_OF_SCHEDULE_GRID*3 + 50];
  1070. SchedData = new BYTE[FRST_SIZE_OF_SCHEDULE_GRID];
  1071. if (SchedData == NULL) {
  1072. printf("Error allocating memory.\n");
  1073. return NULL;
  1074. }
  1075. FileHandle = CreateFileW(
  1076. FileName, // lpszName
  1077. GENERIC_READ | GENERIC_WRITE, // fdwAccess
  1078. FILE_SHARE_READ | FILE_SHARE_WRITE, // fdwShareMode
  1079. NULL, // lpsa
  1080. OPEN_EXISTING, // fdwCreate
  1081. FILE_ATTRIBUTE_NORMAL, // fdwAttrAndFlags
  1082. NULL); // hTemplateFile
  1083. if (!HANDLE_IS_VALID(FileHandle)) {
  1084. WStatus = GetLastError();
  1085. printf("Error opening %ws WStatus: %d\n", FileName, WStatus);
  1086. FREE(SchedData);
  1087. return NULL;
  1088. }
  1089. if (!ReadFile(FileHandle, SchedText, sizeof(SchedText), &BytesRead, NULL)) {
  1090. WStatus = GetLastError();
  1091. printf("Error reading %ws WStatus: %d\n", FileName, WStatus);
  1092. FREE(SchedData);
  1093. FRS_CLOSE(FileHandle);
  1094. return NULL;
  1095. }
  1096. //
  1097. // remove any white-space chars, including CR-LF, used for formatting.
  1098. //
  1099. Remaining = BytesRead;
  1100. pTC = SchedText;
  1101. while (Remaining > 0) {
  1102. if (isspace((LONG) *pTC)) {
  1103. memcpy(pTC, pTC+1, Remaining-1);
  1104. } else {
  1105. pTC++;
  1106. }
  1107. Remaining -= 1;
  1108. }
  1109. BytesRead = pTC - SchedText;
  1110. if (BytesRead < FRST_SIZE_OF_SCHEDULE_GRID*2) {
  1111. printf("Error reading %ws Expecting %d bytes, Actual was %d bytes. Could be too much whitespace.\n",
  1112. FileName, FRST_SIZE_OF_SCHEDULE_GRID*2, BytesRead);
  1113. FREE(SchedData);
  1114. FRS_CLOSE(FileHandle);
  1115. return NULL;
  1116. }
  1117. //
  1118. // Result should be exactly the right size.
  1119. //
  1120. if (BytesRead != FRST_SIZE_OF_SCHEDULE_GRID*2) {
  1121. printf("Error reading %ws Expecting %d bytes, Actual was %d bytes\n",
  1122. FileName, FRST_SIZE_OF_SCHEDULE_GRID*2, BytesRead);
  1123. FREE(SchedData);
  1124. FRS_CLOSE(FileHandle);
  1125. return NULL;
  1126. }
  1127. //
  1128. // Convert to binary.
  1129. //
  1130. for (i=0; i<FRST_SIZE_OF_SCHEDULE_GRID; i++) {
  1131. if (sscanf(&SchedText[i*2], "%2x", &DataByte) != 1) {
  1132. SchedText[i*2+2] = '\0';
  1133. printf("Error reading %ws Invalid hex data (%s) for schedule byte %d.\n",
  1134. FileName, &SchedText[i*2], i);
  1135. FREE(SchedData);
  1136. FRS_CLOSE(FileHandle);
  1137. return NULL;
  1138. }
  1139. SchedData[i] = (BYTE) DataByte;
  1140. }
  1141. FRS_CLOSE(FileHandle);
  1142. return SchedData;
  1143. }
  1144. DWORD
  1145. BuildSchedule(
  1146. PBYTE *ppSchedule,
  1147. DWORD Interval,
  1148. DWORD Stagger,
  1149. DWORD Offset,
  1150. PBYTE CustSchedData
  1151. )
  1152. /*++
  1153. Routine Description:
  1154. Build the schedule structure. Using interval, stagger and offset.
  1155. Then apply mask and override.
  1156. If CustSchedData is non-null then just use that.
  1157. Arguments:
  1158. ppSchedule - pointer to return the schedule in.
  1159. Return Value:
  1160. winerror.
  1161. --*/
  1162. {
  1163. BYTE ScheduleData[FRST_SIZE_OF_SCHEDULE_GRID];
  1164. UINT i, Day, Hour;
  1165. DWORD Status = ERROR_SUCCESS;
  1166. *ppSchedule = new BYTE[FRST_SIZE_OF_SCHEDULE];
  1167. if (*ppSchedule == NULL) {
  1168. return ERROR_NOT_ENOUGH_MEMORY;
  1169. }
  1170. //
  1171. // Set the values for the elements of the SCHEDULE structure.
  1172. //
  1173. ((PSCHEDULE)*ppSchedule)->Size = FRST_SIZE_OF_SCHEDULE;
  1174. ((PSCHEDULE)*ppSchedule)->Bandwidth = 0;
  1175. ((PSCHEDULE)*ppSchedule)->NumberOfSchedules = 1;
  1176. ((PSCHEDULE)*ppSchedule)->Schedules->Type = SCHEDULE_INTERVAL;
  1177. ((PSCHEDULE)*ppSchedule)->Schedules->Offset = sizeof(SCHEDULE);
  1178. if (CustSchedData != NULL) {
  1179. //
  1180. // For custom schedules, we're done.
  1181. //
  1182. memcpy((*ppSchedule)+sizeof(SCHEDULE), CustSchedData, FRST_SIZE_OF_SCHEDULE_GRID);
  1183. return ERROR_SUCCESS;
  1184. }
  1185. //
  1186. // Building the ScheduleData array depending on the requested schedule.
  1187. //
  1188. for (i=0 ; i<FRST_SIZE_OF_SCHEDULE_GRID ; ++i) {
  1189. if (Interval == 0) {
  1190. ScheduleData[i] = 0x00;
  1191. } else if (i < Offset) {
  1192. ScheduleData[i] = 0x00;
  1193. } else if ( (i-Offset) % (Interval * Stagger) == 0) {
  1194. ScheduleData[i] = 0x01;
  1195. } else {
  1196. ScheduleData[i] = 0x00;
  1197. }
  1198. }
  1199. //
  1200. // Use the supplied schedule mask to turn off regions of the above schedule.
  1201. //
  1202. if (SchedMask != NULL) {
  1203. for (i=0 ; i<FRST_SIZE_OF_SCHEDULE_GRID ; ++i) {
  1204. ScheduleData[i] &= ~SchedMask[i];
  1205. }
  1206. }
  1207. //
  1208. // Apply the supplied override schedule to regions of the above schedule.
  1209. //
  1210. if (SchedOverride != NULL) {
  1211. for (i=0 ; i<FRST_SIZE_OF_SCHEDULE_GRID ; ++i) {
  1212. ScheduleData[i] |= SchedOverride[i];
  1213. }
  1214. }
  1215. memcpy((*ppSchedule)+sizeof(SCHEDULE),ScheduleData, FRST_SIZE_OF_SCHEDULE_GRID);
  1216. return Status;
  1217. }
  1218. DWORD
  1219. UpdateConnection(
  1220. PWCHAR FromNTDSSettingsDn,
  1221. PWCHAR ToNTDSSettingsDn,
  1222. BOOL bEnabled,
  1223. DWORD Options,
  1224. PBYTE pSchedule
  1225. )
  1226. /*++
  1227. Routine Description:
  1228. Update the connection parameters.
  1229. Arguments:
  1230. Return Value:
  1231. --*/
  1232. {
  1233. LDAPMod **Mod = NULL;
  1234. DWORD LStatus = LDAP_SUCCESS;
  1235. DWORD Status = ERROR_SUCCESS;
  1236. PWCHAR CxtionDn = NULL;
  1237. PWCHAR Attrs[6];
  1238. PLDAPMessage LdapMsg = NULL;
  1239. PLDAPMessage LdapEntry = NULL;
  1240. WCHAR SearchFilter[MAX_PATH];
  1241. PWCHAR CurEnabled = NULL;
  1242. DWORD NoOfCxtions;
  1243. PSCHEDULE Schedule = NULL;
  1244. DWORD ScheduleLen;
  1245. BOOL bNeedsUpdate = FALSE;
  1246. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  1247. WCHAR OptionsStr[10];
  1248. PWCHAR OptionsVal = NULL;
  1249. UINT i;
  1250. Attrs[0] = ATTR_DN;
  1251. Attrs[1] = ATTR_FROM_SERVER;
  1252. Attrs[2] = ATTR_ENABLED_CXTION;
  1253. Attrs[3] = ATTR_SCHEDULE;
  1254. Attrs[4] = ATTR_OPTIONS;
  1255. Attrs[5] = NULL;
  1256. wcscpy(SearchFilter, L"(&(");
  1257. wcscat(SearchFilter, ATTR_FROM_SERVER);
  1258. wcscat(SearchFilter, L"=");
  1259. wcscat(SearchFilter, FromNTDSSettingsDn);
  1260. wcscat(SearchFilter, L")");
  1261. wcscat(SearchFilter, CLASS_CXTION);
  1262. wcscat(SearchFilter, L")");
  1263. if (!LdapSearchInit(pLdap,
  1264. ToNTDSSettingsDn,
  1265. LDAP_SCOPE_ONELEVEL,
  1266. SearchFilter,
  1267. Attrs,
  1268. 0,
  1269. &FrsSearchContext,
  1270. FALSE)) {
  1271. return MKDSXE_CXTION_OBJ_UPDATE_FAILED;
  1272. }
  1273. NoOfCxtions = FrsSearchContext.EntriesInPage;
  1274. if (NoOfCxtions == 0) {
  1275. DPRINT0("Error updating; connection not found.\n");
  1276. return MKDSXE_CXTION_NOT_FOUND_UPDATE;
  1277. }
  1278. if (NoOfCxtions > 1) {
  1279. DPRINT0("Error updating; duplicate connections found.\n");
  1280. return MKDSXE_CXTION_DUPS_FOUND_UPDATE;
  1281. }
  1282. //
  1283. // Scan the entries returned from ldap_search
  1284. //
  1285. for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
  1286. LdapEntry != NULL;
  1287. LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
  1288. CxtionDn = FindValue(LdapEntry, ATTR_DN);
  1289. CurEnabled = FindValue(LdapEntry, ATTR_ENABLED_CXTION);
  1290. OptionsVal = FindValue(LdapEntry, ATTR_OPTIONS);
  1291. FindBerValue(LdapEntry, ATTR_SCHEDULE, &ScheduleLen, (VOID **)&Schedule);
  1292. }
  1293. LdapSearchClose(&FrsSearchContext);
  1294. DPRINT1("Connection Dn:%ws\n", CxtionDn);
  1295. // Check ATTR_ENABLED_CXTION
  1296. if (bEnabled) {
  1297. if ((CurEnabled == NULL) || wcscmp(CurEnabled, ATTR_TRUE)) {
  1298. AddMod(ATTR_ENABLED_CXTION, ATTR_TRUE, &Mod);
  1299. bNeedsUpdate = TRUE;
  1300. DPRINT0(" New enabledCxtion:TRUE\n");
  1301. }
  1302. } else {
  1303. if ((CurEnabled == NULL) || wcscmp(CurEnabled, ATTR_FALSE)) {
  1304. AddMod(ATTR_ENABLED_CXTION, ATTR_FALSE, &Mod);
  1305. bNeedsUpdate = TRUE;
  1306. DPRINT0(" New enabledCxtion:FALSE\n");
  1307. }
  1308. }
  1309. // Check ATTR_OPTIONS
  1310. if (wcstoul(OptionsVal, NULL, 10) != Options) {
  1311. _itow(Options, OptionsStr, 10);
  1312. AddMod(ATTR_OPTIONS, OptionsStr, &Mod);
  1313. DPRINT1(" options:0x%08x [ ", Options);
  1314. for (i = 1 ; i<NTDSCONN_OPT_NUM;++i) {
  1315. if (NtdsConnOptions[i] & Options) {
  1316. DPRINT1("%ws ", NtdsConnOptionsStr[i]);
  1317. }
  1318. }
  1319. if (Options & FRSCONN_PRIORITY_MASK) {
  1320. DPRINT1("Priority%d ", (Options & FRSCONN_PRIORITY_MASK) >> 28);
  1321. }
  1322. DPRINT0("]\n");
  1323. bNeedsUpdate = TRUE;
  1324. }
  1325. FREE(OptionsVal);
  1326. // Check ATTR_SCHEDULE
  1327. if (pSchedule != NULL) {
  1328. if ((Schedule == NULL) ||
  1329. (FRST_SIZE_OF_SCHEDULE != ScheduleLen) ||
  1330. (memcmp(Schedule, pSchedule, FRST_SIZE_OF_SCHEDULE))) {
  1331. bNeedsUpdate = TRUE;
  1332. AddBerMod(ATTR_SCHEDULE,(PCHAR)pSchedule,FRST_SIZE_OF_SCHEDULE,&Mod);
  1333. DPRINT0(" New schedule:\n");
  1334. PrintSchedule((PSCHEDULE)pSchedule, 0x0F);
  1335. }
  1336. }
  1337. if (bNeedsUpdate) {
  1338. if (bDebugMode) {
  1339. DPRINT1("LStatus = ldap_modify_s(pLdap, %ws, Mod);\n", CxtionDn);
  1340. } else {
  1341. LStatus = ldap_modify_s(pLdap, CxtionDn, Mod);
  1342. if (LStatus != LDAP_SUCCESS) {
  1343. DPRINT2("ERROR - Can't update %ws: %ws\n",
  1344. CxtionDn, ldap_err2string(LStatus));
  1345. Status = MKDSXE_CXTION_OBJ_UPDATE_FAILED;
  1346. }
  1347. }
  1348. } else {
  1349. DPRINT0("No update required\n");
  1350. }
  1351. FREE(CxtionDn);
  1352. FREE(CurEnabled);
  1353. FREE(Schedule);
  1354. FreeMod(&Mod);
  1355. return Status;
  1356. }
  1357. DWORD
  1358. CreateNewConnection(
  1359. PWCHAR CxtionName,
  1360. PWCHAR FromNTDSSettingsDn,
  1361. PWCHAR ToNTDSSettingsDn,
  1362. BOOL bEnabled,
  1363. DWORD Options,
  1364. PBYTE pSchedule
  1365. )
  1366. /*++
  1367. Routine Description:
  1368. Create a new connection.
  1369. Arguments:
  1370. Return Value:
  1371. --*/
  1372. {
  1373. LDAPMod **Mod = NULL;
  1374. DWORD LStatus = LDAP_SUCCESS;
  1375. DWORD Status = ERROR_SUCCESS;
  1376. PWCHAR CxtionDn = NULL;
  1377. WCHAR OptionsStr[10];
  1378. UINT i;
  1379. CxtionDn = ExtendDn(ToNTDSSettingsDn, CxtionName);
  1380. DPRINT1("Connection Dn:%ws\n", CxtionDn);
  1381. AddMod(ATTR_CLASS, ATTR_CXTION, &Mod);
  1382. AddMod(ATTR_FROM_SERVER, FromNTDSSettingsDn, &Mod);
  1383. DPRINT1(" fromServer:%ws\n", FromNTDSSettingsDn);
  1384. if (bEnabled) {
  1385. AddMod(ATTR_ENABLED_CXTION, ATTR_TRUE, &Mod);
  1386. DPRINT0(" enabledCxtion:TRUE\n");
  1387. } else {
  1388. AddMod(ATTR_ENABLED_CXTION, ATTR_FALSE, &Mod);
  1389. DPRINT0(" enabledCxtion:FALSE\n");
  1390. }
  1391. //
  1392. // If options are specified then use that options otherwise
  1393. // set options to 0.
  1394. //
  1395. if (Options == 0xffffffff) {
  1396. Options = 0;
  1397. }
  1398. _itow(Options, OptionsStr, 10);
  1399. AddMod(ATTR_OPTIONS, OptionsStr, &Mod);
  1400. DPRINT1(" options:0x%08x [ ", Options);
  1401. for (i = 1 ; i<NTDSCONN_OPT_NUM;++i) {
  1402. if (NtdsConnOptions[i] & Options) {
  1403. DPRINT1("%ws ", NtdsConnOptionsStr[i]);
  1404. }
  1405. }
  1406. if (Options & FRSCONN_PRIORITY_MASK) {
  1407. DPRINT1("Priority%d ", (Options & FRSCONN_PRIORITY_MASK) >> 28);
  1408. }
  1409. DPRINT0("]\n");
  1410. if (pSchedule != NULL) {
  1411. AddBerMod(ATTR_SCHEDULE,(PCHAR)pSchedule,FRST_SIZE_OF_SCHEDULE,&Mod);
  1412. PrintSchedule((PSCHEDULE)pSchedule, 0x0F);
  1413. }
  1414. if (bDebugMode) {
  1415. DPRINT1("LStatus = ldap_add_s(pLdap, %ws, Mod);\n", CxtionDn);
  1416. } else {
  1417. LStatus = ldap_add_s(pLdap, CxtionDn, Mod);
  1418. if (LStatus == LDAP_ALREADY_EXISTS) {
  1419. //
  1420. // If the object already exists then convert the create to an update.
  1421. // This is to allow the user to run the data file with creates twice without
  1422. // generating errors but only fixing the cxtions that have changed.
  1423. //
  1424. Status = UpdateConnection(FromNTDSSettingsDn, ToNTDSSettingsDn,
  1425. bEnabled, Options, pSchedule);
  1426. } else if (LStatus != LDAP_SUCCESS) {
  1427. DPRINT2("ERROR - Can't create %ws: %ws\n",
  1428. CxtionDn, ldap_err2string(LStatus));
  1429. Status = MKDSXE_CXTION_OBJ_CRE_FAILED;
  1430. }
  1431. }
  1432. FREE(CxtionDn);
  1433. FreeMod(&Mod);
  1434. return Status;
  1435. }
  1436. DWORD
  1437. DelConnection(
  1438. PWCHAR FromNTDSSettingsDn,
  1439. PWCHAR ToNTDSSettingsDn
  1440. )
  1441. /*++
  1442. Routine Description:
  1443. Create a new connection.
  1444. Arguments:
  1445. Return Value:
  1446. --*/
  1447. {
  1448. DWORD LStatus = LDAP_SUCCESS;
  1449. DWORD Status = ERROR_SUCCESS;
  1450. PWCHAR CxtionDn = NULL;
  1451. PWCHAR Attrs[3];
  1452. PLDAPMessage LdapMsg = NULL;
  1453. PLDAPMessage LdapEntry = NULL;
  1454. WCHAR SearchFilter[MAX_PATH];
  1455. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  1456. DWORD NoOfCxtions;
  1457. Attrs[0] = ATTR_DN;
  1458. Attrs[1] = ATTR_FROM_SERVER;
  1459. Attrs[2] = NULL;
  1460. if (bAffectAll) {
  1461. wcscpy(SearchFilter, CLASS_CXTION);
  1462. } else {
  1463. wcscpy(SearchFilter, L"(&(");
  1464. wcscat(SearchFilter, ATTR_FROM_SERVER);
  1465. wcscat(SearchFilter, L"=");
  1466. wcscat(SearchFilter, FromNTDSSettingsDn);
  1467. wcscat(SearchFilter, L")");
  1468. wcscat(SearchFilter, CLASS_CXTION);
  1469. wcscat(SearchFilter, L")");
  1470. }
  1471. if (!LdapSearchInit(pLdap,
  1472. ToNTDSSettingsDn,
  1473. LDAP_SCOPE_ONELEVEL,
  1474. SearchFilter,
  1475. Attrs,
  1476. 0,
  1477. &FrsSearchContext,
  1478. FALSE)) {
  1479. return MKDSXE_CXTION_DELETE_FAILED;
  1480. }
  1481. NoOfCxtions = FrsSearchContext.EntriesInPage;
  1482. if (NoOfCxtions == 0) {
  1483. DPRINT0("Warning deleting; connection not found.\n");
  1484. return MKDSXE_CXTION_NOT_FOUND_DELETE;
  1485. }
  1486. if (bAffectAll != TRUE) {
  1487. if (NoOfCxtions > 1) {
  1488. DPRINT0("Duplicate connections found. Deleting all.\n");
  1489. Status = MKDSXE_MULTIPLE_CXTIONS_DELETED;
  1490. }
  1491. }
  1492. //
  1493. // Scan the entries returned from ldap_search
  1494. //
  1495. for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
  1496. LdapEntry != NULL;
  1497. LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
  1498. CxtionDn = FindValue(LdapEntry, ATTR_DN);
  1499. DPRINT1("Deleting Dn:%ws\n", CxtionDn);
  1500. if (bDebugMode) {
  1501. DPRINT1("LStatus = ldap_delete_s(pLdap, %ws);\n", CxtionDn);
  1502. } else {
  1503. LStatus = ldap_delete_s(pLdap, CxtionDn);
  1504. if (LStatus != LDAP_SUCCESS) {
  1505. DPRINT2("ERROR - Can't delete %ws: %ws\n",
  1506. CxtionDn, ldap_err2string(LStatus));
  1507. Status = MKDSXE_CXTION_DELETE_FAILED;
  1508. }
  1509. }
  1510. FREE(CxtionDn);
  1511. }
  1512. LdapSearchClose(&FrsSearchContext);
  1513. return Status;
  1514. }
  1515. DWORD
  1516. DumpConnection(
  1517. PWCHAR FromNTDSSettingsDn,
  1518. PWCHAR ToNTDSSettingsDn
  1519. )
  1520. /*++
  1521. Routine Description:
  1522. Create a new connection.
  1523. Arguments:
  1524. Return Value:
  1525. --*/
  1526. {
  1527. DWORD LStatus;
  1528. DWORD Status = ERROR_SUCCESS;
  1529. PWCHAR CxtionDn = NULL;
  1530. PWCHAR Attrs[6];
  1531. PLDAPMessage LdapMsg = NULL;
  1532. PLDAPMessage LdapEntry = NULL;
  1533. PWCHAR Val = NULL;
  1534. WCHAR SearchFilter[MAX_PATH];
  1535. FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
  1536. DWORD NoOfCxtions;
  1537. DWORD ScheduleLen;
  1538. PSCHEDULE Schedule = NULL;
  1539. BOOL SaveVerbose;
  1540. DWORD options = 0;
  1541. UINT i;
  1542. Attrs[0] = ATTR_DN;
  1543. Attrs[1] = ATTR_FROM_SERVER;
  1544. Attrs[2] = ATTR_ENABLED_CXTION;
  1545. Attrs[3] = ATTR_SCHEDULE;
  1546. Attrs[4] = ATTR_OPTIONS;
  1547. Attrs[5] = NULL;
  1548. if (bAffectAll) {
  1549. wcscpy(SearchFilter, CLASS_CXTION);
  1550. } else {
  1551. wcscpy(SearchFilter, L"(&(");
  1552. wcscat(SearchFilter, ATTR_FROM_SERVER);
  1553. wcscat(SearchFilter, L"=");
  1554. wcscat(SearchFilter, FromNTDSSettingsDn);
  1555. wcscat(SearchFilter, L")");
  1556. wcscat(SearchFilter, CLASS_CXTION);
  1557. wcscat(SearchFilter, L")");
  1558. }
  1559. if (!LdapSearchInit(pLdap,
  1560. ToNTDSSettingsDn,
  1561. LDAP_SCOPE_ONELEVEL,
  1562. SearchFilter,
  1563. Attrs,
  1564. 0,
  1565. &FrsSearchContext,
  1566. FALSE)) {
  1567. return MKDSXE_CXTION_DUMP_FAILED;
  1568. }
  1569. NoOfCxtions = FrsSearchContext.EntriesInPage;
  1570. if (NoOfCxtions == 0) {
  1571. DPRINT0("Error dumping; connection not found.\n");
  1572. return MKDSXE_CXTION_NOT_FOUND_DUMP;
  1573. }
  1574. if (bAffectAll != TRUE) {
  1575. if (NoOfCxtions > 1) {
  1576. printf("Duplicate connections found. Dumping all.\n");
  1577. Status = MKDSXE_MULTIPLE_CXTIONS_DUMPED;
  1578. }
  1579. }
  1580. //
  1581. // Scan the entries returned from ldap_search
  1582. //
  1583. for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
  1584. LdapEntry != NULL;
  1585. LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
  1586. // ATTR_DN
  1587. Val = FindValue(LdapEntry, ATTR_DN);
  1588. printf("Dn:%ws\n", Val);
  1589. FREE(Val);
  1590. // ATTR_FROM_SERVER
  1591. Val = FindValue(LdapEntry, ATTR_FROM_SERVER);
  1592. printf(" fromServer:%ws\n", Val);
  1593. FREE(Val);
  1594. // ATTR_ENABLED_CXTION
  1595. Val = FindValue(LdapEntry, ATTR_ENABLED_CXTION);
  1596. printf(" enabledCxtion:%ws\n", Val);
  1597. FREE(Val);
  1598. // ATTR_OPTIONS
  1599. Val = FindValue(LdapEntry, ATTR_OPTIONS);
  1600. if (Val) {
  1601. options = _wtoi(Val);
  1602. printf(" options:0x%08x [ ", options);
  1603. for (i = 1 ; i<NTDSCONN_OPT_NUM;++i) {
  1604. if (NtdsConnOptions[i] & options) {
  1605. printf("%ws ", NtdsConnOptionsStr[i]);
  1606. }
  1607. }
  1608. if (options & FRSCONN_PRIORITY_MASK) {
  1609. printf("Priority%d ", (options & FRSCONN_PRIORITY_MASK) >> 28);
  1610. }
  1611. printf("]\n");
  1612. }
  1613. FREE(Val);
  1614. // ATTR_SCHEDULE
  1615. FindBerValue(LdapEntry, ATTR_SCHEDULE, &ScheduleLen, (VOID **)&Schedule);
  1616. if (Schedule) {
  1617. SaveVerbose = bVerboseMode;
  1618. bVerboseMode = TRUE;
  1619. PrintSchedule(Schedule, 0x0F);
  1620. bVerboseMode = SaveVerbose;
  1621. delete(Schedule);
  1622. }
  1623. }
  1624. LdapSearchClose(&FrsSearchContext);
  1625. return Status;
  1626. }
  1627. VOID
  1628. PrintScheduleGrid(
  1629. PUCHAR ScheduleData,
  1630. DWORD Mask
  1631. )
  1632. /*++
  1633. Routine Description:
  1634. Print the schedule grid.
  1635. Arguments:
  1636. Schedule
  1637. Mask for each byte.
  1638. Return Value:
  1639. NONE
  1640. --*/
  1641. {
  1642. DWORD Day, Hour;
  1643. for (Day = 0; Day < 7; ++Day) {
  1644. printf(" Day %1d: ",Day + 1);
  1645. for (Hour = 0; Hour < 24; ++Hour) {
  1646. printf("%1x", *(ScheduleData + (Day * 24) + Hour) & Mask);
  1647. }
  1648. printf("\n");
  1649. }
  1650. }
  1651. VOID
  1652. PrintSchedule(
  1653. PSCHEDULE Schedule,
  1654. DWORD Mask
  1655. )
  1656. /*++
  1657. Routine Description:
  1658. Print the schedule.
  1659. Arguments:
  1660. Schedule
  1661. Mask for each byte.
  1662. Return Value:
  1663. NONE
  1664. --*/
  1665. {
  1666. PUCHAR ScheduleData;
  1667. DWORD i;
  1668. if (bVerboseMode) {
  1669. printf(" schedule:\n");
  1670. for (i = 0; i < Schedule->NumberOfSchedules; ++i) {
  1671. ScheduleData = ((PUCHAR)Schedule) + Schedule->Schedules[i].Offset;
  1672. if (Schedule->Schedules[i].Type != SCHEDULE_INTERVAL) {
  1673. continue;
  1674. }
  1675. PrintScheduleGrid(ScheduleData, Mask);
  1676. }
  1677. }
  1678. }
  1679. PWCHAR *
  1680. ConvertArgv(
  1681. DWORD argc,
  1682. PCHAR *argv
  1683. )
  1684. /*++
  1685. Routine Description:
  1686. Convert short char argv into wide char argv
  1687. Arguments:
  1688. argc - From main
  1689. argv - From main
  1690. Return Value:
  1691. Address of the new argv
  1692. --*/
  1693. {
  1694. PWCHAR *wideargv;
  1695. wideargv = new PWCHAR[argc + 1];
  1696. wideargv[argc] = NULL;
  1697. while (argc-- >= 1) {
  1698. wideargv[argc] = new WCHAR[strlen(argv[argc]) + 1];
  1699. wsprintf(wideargv[argc], L"%hs", argv[argc]);
  1700. }
  1701. return wideargv;
  1702. }
  1703. VOID
  1704. FreeArgv(
  1705. DWORD Argc,
  1706. PWCHAR *Argv
  1707. )
  1708. /*++
  1709. Routine Description:
  1710. Free the converted arguments.
  1711. Arguments:
  1712. Argc - No of arguments.
  1713. Argv - Converted arguments returned from ConvertArgv.
  1714. Return Value:
  1715. None.
  1716. --*/
  1717. {
  1718. while (Argc-- >= 1) {
  1719. FREE(Argv[Argc]);
  1720. }
  1721. FREE(Argv);
  1722. }
  1723. VOID
  1724. Usage(
  1725. PWCHAR *Argv
  1726. )
  1727. /*++
  1728. Routine Description:
  1729. Tell the user how to use the program.
  1730. Arguments:
  1731. Argv Argument array.
  1732. Return Value:
  1733. None
  1734. --*/
  1735. {
  1736. printf("%-60s\n", "This tool creates, adds, updates, dumps, and deletes connections between DCs.\n");
  1737. printf("%-60s%ws /?\n", "Help", Argv[0]);
  1738. printf("%-60s%ws /v\n", "Verbose mode.", Argv[0]);
  1739. printf("%-60s%ws /debug\n", "Debug mode. No Writes to the DC.", Argv[0]);
  1740. printf("%-60s%ws /dc\n", "Name of the DC to connect to.", Argv[0]);
  1741. printf("%-60s%ws /name\n", "Name of the connection to be created.", Argv[0]);
  1742. printf("%-60s%ws /[create update del dump]\n", "Operation to be performed.", Argv[0]);
  1743. printf("%-60s%ws /all\n", "Perform the operation on all the connections to the", Argv[0]);
  1744. printf("%-60s%ws \n", "toserver. /all only works with /dump and /del.", Argv[0]);
  1745. printf("%-60s%ws /enable\n", "Connection is enabled.", Argv[0]);
  1746. printf("%-60s%ws /disable\n", "Connection is disabled.", Argv[0]);
  1747. printf("%-60s%ws /options <integer>\n", "Sum of the following options for connection.", Argv[0]);
  1748. printf("%-60s\n", "IsGenerated = 1");
  1749. printf("%-60s\n", "TwoWaySync = 2");
  1750. printf("%-60s\n", "OverrideNotifyDefault = 4");
  1751. printf("%-60s\n\n", "UseNotify = 8");
  1752. printf("%-60s%ws /ignoreschedule\n", "Schedule is ignored during initial sync.", Argv[0]);
  1753. printf("%-60s%ws /priority <0-7>\n", "Sets the priority to be used during intial sync.", Argv[0]);
  1754. printf("%-60s%ws /schedule <Interval> <Stagger> <Offset>\n", "Schedule to create for the connection.", Argv[0]);
  1755. printf("%-60s%ws <Interval>\n", "The desired interval between each sync with one source.", Argv[0]);
  1756. printf("%-60s%ws <Stagger>\n", "Typically number of source DCs.", Argv[0]);
  1757. printf("%-60s%ws <Offset>\n", "Typically the number of the source DC.", Argv[0]);
  1758. printf("%-60s%ws /fromsite\n", "Name of the site the 'from' server is in.", Argv[0]);
  1759. printf("%-60s%ws /tosite\n", "Name of the site the 'to' server is in.", Argv[0]);
  1760. printf("%-60s%ws /fromserver\n", "Name of the 'from' server.", Argv[0]);
  1761. printf("%-60s%ws /toserver\n", "Name of the 'to' server.", Argv[0]);
  1762. printf("%-60s%ws /schedoverride\n", "File with 7x24 vector of schedule override data.", Argv[0]);
  1763. printf("%-60s%ws /schedmask\n", "File with 7x24 vector of schedule mask off data.", Argv[0]);
  1764. printf("%-60s\n\n", " SchedOverride and SchedMask data are formatted as 2 ascii hex digits for each schedule byte");
  1765. printf("%-60s%ws /customschedule\n", "A string of length 7x24 of schedule data. Override and Mask are not applied.", Argv[0]);
  1766. printf("%-60s%ws /replicasetname\n", "Name of the non-sysvol replica set.", Argv[0]);
  1767. printf("%-60s%ws /fromcomputer\n", "Name of the 'from' computer. /replicasetname needed.", Argv[0]);
  1768. printf("%-60s%ws /tocomputer\n", "Name of the 'to' computer. /replicasetname needed.", Argv[0]);
  1769. DPRINT0("\n");
  1770. DPRINT0("mkdsx.exe error return codes\n");
  1771. DPRINT0("0 = Success;\n");
  1772. DPRINT0("1 = Invalid Arguments.\n");
  1773. DPRINT0("2 = Could not bind to the DC.\n");
  1774. DPRINT0("3 = Could not find 'NTDS Settings' object for the to Server.\n");
  1775. DPRINT0("4 = Could not find 'NTDS Settings' object for the from Server.\n");
  1776. DPRINT0("5 = Error creating connection.\n");
  1777. DPRINT0("6 = Connection already exists.\n");
  1778. DPRINT0("7 = Error updating connection.\n");
  1779. DPRINT0("8 = Error updating connection; connection not found.\n");
  1780. DPRINT0("9 = Error updating connection; duplicate connections found.\n");
  1781. DPRINT0("10= Error deleting connection.\n");
  1782. DPRINT0("11= Error deleting connection; connection not found.\n");
  1783. DPRINT0("12= Deleting multiple connection.\n");
  1784. DPRINT0("13= Error dumping connection.\n");
  1785. DPRINT0("14= Error dumping; connection not found.\n");
  1786. DPRINT0("15= Dumping duplicate connections.\n");
  1787. DPRINT0("\n");
  1788. fflush(stdout);
  1789. }
  1790. DWORD __cdecl
  1791. main(
  1792. DWORD argc,
  1793. PCHAR *argv
  1794. )
  1795. /*++
  1796. Routine Description:
  1797. Arguments:
  1798. None.
  1799. Return Value:
  1800. Exits with 0 if everything went okay. Otherwise returns a error code.
  1801. MKDSXE_SUCCESS = Success;
  1802. MKDSXE_BAD_ARG = Invalid Arguments.
  1803. MKDSXE_CANT_BIND = Could not bind to the DC.
  1804. MKDSXE_NO_T0_NTDS_SETTINGS = Could not find "NTDS Settings" object for the to Server.
  1805. MKDSXE_NO_FROM_NTDS_SETTINGS = Could not find "NTDS Settings" object for the from Server.
  1806. MKDSXE_CXTION_OBJ_CRE_FAILED = Error creating connection.
  1807. MKDSXE_CXTION_EXISTS = Connection already exists. (unused)
  1808. MKDSXE_CXTION_OBJ_UPDATE_FAILED = Error updating connection.
  1809. MKDSXE_CXTION_NOT_FOUND_UPDATE = Error updating connection; connection not found.
  1810. MKDSXE_CXTION_DUPS_FOUND_UPDATE = Error updating connection; duplicate connections found.
  1811. MKDSXE_CXTION_DELETE_FAILED = Error deleting connection.
  1812. MKDSXE_CXTION_NOT_FOUND_DELETE = Error deleting connection; connection not found.
  1813. MKDSXE_MULTIPLE_CXTIONS_DELETED = Deleting multiple connection.
  1814. MKDSXE_CXTION_DUMP_FAILED = Error dumping connection.
  1815. MKDSXE_CXTION_NOT_FOUND_DUMP = Error dumping; connection not found.
  1816. MKDSXE_MULTIPLE_CXTIONS_DUMPED = Dumping duplicate connections.
  1817. --*/
  1818. {
  1819. PWCHAR *Argv;
  1820. ULONG i, j;
  1821. ULONG OptLen;
  1822. DWORD Status = ERROR_SUCCESS;
  1823. PWCHAR FromSite = NULL;
  1824. PWCHAR FromServer = NULL;
  1825. PWCHAR ToSite = NULL;
  1826. PWCHAR ToServer = NULL;
  1827. PWCHAR ReplicaSetName = NULL;
  1828. PWCHAR FromComputer = NULL;
  1829. PWCHAR ToComputer = NULL;
  1830. PWCHAR ToNTDSSettingsDn = NULL; // These parameters are settingsDn in sysvol replica sets
  1831. PWCHAR FromNTDSSettingsDn = NULL; // and memberDn in non sysvol replica sets.
  1832. PWCHAR CxtionName = NULL;
  1833. PBYTE pSchedule = NULL;
  1834. PBYTE CustSchedData = NULL;
  1835. DWORD Interval = 1;
  1836. DWORD Stagger = 1;
  1837. DWORD Offset = 0;
  1838. DWORD Options = 0xffffffff;
  1839. DWORD Priority = 0xffffffff;
  1840. BOOL bEnabled = TRUE;
  1841. BOOL bIgnoreSchedule = FALSE;
  1842. BOOL bCreateConnection = FALSE;
  1843. BOOL bUpdateConnection = FALSE;
  1844. BOOL bDelConnection = FALSE;
  1845. BOOL bDumpConnection = FALSE;
  1846. BOOL bSchedule = FALSE;
  1847. PWCHAR Switch = NULL;
  1848. PWCHAR CustomSchedule = NULL;
  1849. DWORD DataByte;
  1850. #define ArgMatch(_a, _b) (wcsstr(_a, _b) == (_a))
  1851. Argv = ConvertArgv(argc, argv);
  1852. if (argc <= 1) {
  1853. Usage(Argv);
  1854. FreeArgv(argc,Argv);
  1855. return MKDSXE_BAD_ARG;
  1856. }
  1857. for (i = 1; i < argc; ++i) {
  1858. OptLen = wcslen(Argv[i]);
  1859. _wcslwr(Argv[i]);
  1860. Switch = Argv[i];
  1861. if ((*Switch != L'/') && (*Switch != L'-')) {
  1862. Usage(Argv);
  1863. return MKDSXE_BAD_ARG;
  1864. }
  1865. ++Switch; // jump over -
  1866. if (OptLen == 2 && ArgMatch(Switch, L"v")) {
  1867. bVerboseMode=TRUE;
  1868. } else if (OptLen == 3 && ArgMatch(Switch, L"vs")) {
  1869. bVerboseModeSearch=TRUE;
  1870. } else if (OptLen == 2 && ArgMatch(Switch, L"?")) {
  1871. Usage(Argv);
  1872. FreeArgv(argc,Argv);
  1873. return MKDSXE_SUCCESS;
  1874. } else if (OptLen == 6 && ArgMatch(Switch, L"debug")) {
  1875. bDebugMode = TRUE;
  1876. bVerboseMode=TRUE;
  1877. } else if (OptLen == 3 && (i+1 < argc) && ArgMatch(Switch, L"dc")) {
  1878. i+=1;
  1879. DcName = new WCHAR[wcslen(Argv[i])+1];
  1880. wcscpy(DcName, Argv[i]);
  1881. } else if (OptLen == 9 && (i+1 < argc) && ArgMatch(Switch, L"fromsite")) {
  1882. i+=1;
  1883. FromSite = new WCHAR[wcslen(Argv[i])+1];
  1884. wcscpy(FromSite, Argv[i]);
  1885. } else if (OptLen == 11 && (i+1 < argc) && ArgMatch(Switch, L"fromserver")) {
  1886. i+=1;
  1887. FromServer = new WCHAR[wcslen(Argv[i])+1];
  1888. wcscpy(FromServer, Argv[i]);
  1889. } else if (OptLen == 7 && (i+1 < argc) && ArgMatch(Switch, L"tosite")) {
  1890. i+=1;
  1891. ToSite = new WCHAR[wcslen(Argv[i])+1];
  1892. wcscpy(ToSite, Argv[i]);
  1893. } else if (OptLen == 9 && (i+1 < argc) && ArgMatch(Switch, L"toserver")) {
  1894. i+=1;
  1895. ToServer = new WCHAR[wcslen(Argv[i])+1];
  1896. wcscpy(ToServer, Argv[i]);
  1897. } else if (OptLen == 15 && (i+1 < argc) && ArgMatch(Switch, L"replicasetname")) {
  1898. i+=1;
  1899. ReplicaSetName = new WCHAR[wcslen(Argv[i])+1];
  1900. wcscpy(ReplicaSetName, Argv[i]);
  1901. } else if (OptLen == 13 && (i+1 < argc) && ArgMatch(Switch, L"fromcomputer")) {
  1902. i+=1;
  1903. FromComputer = new WCHAR[wcslen(Argv[i])+1];
  1904. wcscpy(FromComputer, Argv[i]);
  1905. } else if (OptLen == 11 && (i+1 < argc) && ArgMatch(Switch, L"tocomputer")) {
  1906. i+=1;
  1907. ToComputer = new WCHAR[wcslen(Argv[i])+1];
  1908. wcscpy(ToComputer, Argv[i]);
  1909. } else if (OptLen == 5 && (i+1 < argc) && ArgMatch(Switch, L"name")) {
  1910. i+=1;
  1911. CxtionName = new WCHAR[wcslen(Argv[i])+1];
  1912. wcscpy(CxtionName, Argv[i]);
  1913. } else if (OptLen == 15 && (i+1 < argc) && ArgMatch(Switch, L"customschedule")) {
  1914. i+=1;
  1915. if (wcslen(Argv[i]) != FRST_SIZE_OF_SCHEDULE_GRID) {
  1916. printf("Invalid custom schedule - Expecting %d bytes, Actual was %d bytes\n",
  1917. FRST_SIZE_OF_SCHEDULE_GRID, wcslen(Argv[i]));
  1918. return MKDSXE_BAD_ARG;
  1919. }
  1920. bSchedule = TRUE;
  1921. CustomSchedule = new WCHAR[wcslen(Argv[i])+1];
  1922. wcscpy(CustomSchedule, Argv[i]);
  1923. //
  1924. // Convert to binary.
  1925. //
  1926. CustSchedData = new BYTE[FRST_SIZE_OF_SCHEDULE_GRID];
  1927. if (CustSchedData == NULL) {
  1928. printf("Error allocating memory.\n");
  1929. return MKDSXE_BAD_ARG;
  1930. }
  1931. for (j=0; j<FRST_SIZE_OF_SCHEDULE_GRID; j++) {
  1932. if (swscanf(&CustomSchedule[j], L"%1x", &DataByte) != 1) {
  1933. CustomSchedule[j+1] = L'\0';
  1934. printf("Invalid custom schedule - Invalid hex data (%ws) for schedule byte %d.\n",
  1935. &CustomSchedule[j], i);
  1936. return MKDSXE_BAD_ARG;
  1937. }
  1938. CustSchedData[j] = (BYTE) DataByte;
  1939. }
  1940. //PrintScheduleGrid(CustSchedData, 0x0F);
  1941. } else if (OptLen == 9 && (i+3 < argc) && ArgMatch(Switch, L"schedule")) {
  1942. bSchedule = TRUE;
  1943. Interval = _wtoi(Argv[i+1]);
  1944. Stagger = _wtoi(Argv[i+2]);
  1945. Offset = _wtoi(Argv[i+3]);
  1946. i+=3;
  1947. } else if ((OptLen == 10) && (i+1 < argc) && ArgMatch(Switch, L"schedmask")) {
  1948. SchedMask = ReadScheduleFile(Argv[i+1]);
  1949. if (SchedMask == NULL) {
  1950. FreeArgv(argc,Argv);
  1951. return MKDSXE_BAD_ARG;
  1952. } else {
  1953. if (bVerboseMode) {
  1954. printf(" schedmask:\n");
  1955. PrintScheduleGrid(SchedMask, 0x0F);
  1956. }
  1957. }
  1958. i+=1;
  1959. } else if ((OptLen == 14) && (i+1 < argc) && ArgMatch(Switch, L"schedoverride")) {
  1960. SchedOverride = ReadScheduleFile(Argv[i+1]);
  1961. if (SchedOverride == NULL) {
  1962. FreeArgv(argc,Argv);
  1963. return MKDSXE_BAD_ARG;
  1964. } else {
  1965. if (bVerboseMode) {
  1966. printf(" schedoverride:\n");
  1967. PrintScheduleGrid(SchedOverride, 0x0F);
  1968. }
  1969. }
  1970. bSchedule = TRUE;
  1971. i+=1;
  1972. } else if (OptLen == 8 && (i+1 < argc) && ArgMatch(Switch, L"options")) {
  1973. i+=1;
  1974. Options = _wtoi(Argv[i]);
  1975. //
  1976. // Mask off all the bits of option that are not supported.
  1977. //
  1978. Options = Options & MKDSXE_VALID_CONN_OPTIONS_MASK;
  1979. } else if (OptLen == 9 && (i+1 < argc) && ArgMatch(Switch, L"priority")) {
  1980. i+=1;
  1981. Priority = _wtoi(Argv[i]);
  1982. if (Priority > MKDSXE_MAX_CONN_PRIORITY) {
  1983. Usage(Argv);
  1984. FreeArgv(argc,Argv);
  1985. return MKDSXE_BAD_ARG;
  1986. }
  1987. } else if (OptLen == 15 && ArgMatch(Switch, L"ignoreschedule")) {
  1988. bIgnoreSchedule = TRUE;
  1989. } else if (OptLen == 7 && ArgMatch(Switch, L"enable")) {
  1990. bEnabled = TRUE;
  1991. } else if (OptLen == 8 && ArgMatch(Switch, L"disable")) {
  1992. bEnabled = FALSE;
  1993. } else if (OptLen == 7 && ArgMatch(Switch, L"create")) {
  1994. bCreateConnection = TRUE;
  1995. } else if (OptLen == 7 && ArgMatch(Switch, L"update")) {
  1996. bUpdateConnection = TRUE;
  1997. } else if (OptLen == 4 && ArgMatch(Switch, L"del")) {
  1998. bDelConnection = TRUE;
  1999. } else if (OptLen == 5 && ArgMatch(Switch, L"dump")) {
  2000. bDumpConnection = TRUE;
  2001. } else if (OptLen == 4 && ArgMatch(Switch, L"all")) {
  2002. bAffectAll = TRUE;
  2003. } else {
  2004. Usage(Argv);
  2005. FreeArgv(argc,Argv);
  2006. return MKDSXE_BAD_ARG;
  2007. }
  2008. }
  2009. FreeArgv(argc,Argv);
  2010. if (ReplicaSetName != NULL) {
  2011. if (bAffectAll) {
  2012. if ( //DcName == NULL ||
  2013. ToComputer == NULL) {
  2014. Usage(Argv);
  2015. return MKDSXE_BAD_ARG;
  2016. }
  2017. } else if ( //DcName == NULL ||
  2018. FromComputer == NULL ||
  2019. ToComputer == NULL) {
  2020. Usage(Argv);
  2021. return MKDSXE_BAD_ARG;
  2022. }
  2023. } else {
  2024. if (bAffectAll) {
  2025. if ( // DcName == NULL ||
  2026. ToSite == NULL || ToServer == NULL ) {
  2027. Usage(Argv);
  2028. return MKDSXE_BAD_ARG;
  2029. }
  2030. } else if ( // DcName == NULL ||
  2031. FromSite == NULL || FromServer == NULL ||
  2032. ToSite == NULL || ToServer == NULL ) {
  2033. Usage(Argv);
  2034. return MKDSXE_BAD_ARG;
  2035. }
  2036. }
  2037. Status = BindToDC(DcName, &pLdap);
  2038. if (Status != ERROR_SUCCESS) {
  2039. return MKDSXE_CANT_BIND;
  2040. }
  2041. if (ReplicaSetName != NULL) {
  2042. Status = FormMemberDn(ReplicaSetName, ToComputer, &ToNTDSSettingsDn);
  2043. } else {
  2044. Status = FormNTDSSettingsDn(ToSite, ToServer, &ToNTDSSettingsDn);
  2045. }
  2046. if (Status != ERROR_SUCCESS) {
  2047. return MKDSXE_NO_T0_NTDS_SETTINGS;
  2048. }
  2049. //
  2050. // We need the from server only if Affectall is not set.
  2051. //
  2052. if (!bAffectAll) {
  2053. if (ReplicaSetName != NULL) {
  2054. Status = FormMemberDn(ReplicaSetName, FromComputer, &FromNTDSSettingsDn);
  2055. } else {
  2056. Status = FormNTDSSettingsDn(FromSite, FromServer, &FromNTDSSettingsDn);
  2057. }
  2058. if (Status != ERROR_SUCCESS) {
  2059. return MKDSXE_NO_FROM_NTDS_SETTINGS;
  2060. }
  2061. }
  2062. //
  2063. // /all does not make sense with create.
  2064. //
  2065. if ((bAffectAll) && (bCreateConnection || bUpdateConnection) ) {
  2066. Usage(Argv);
  2067. DPRINT0("Error - /all can not be used with /create or /update.\n");
  2068. return MKDSXE_BAD_ARG;
  2069. }
  2070. //
  2071. // Merge the priority with options attribute if supplied.
  2072. //
  2073. if (Priority != 0xffffffff) {
  2074. if (Options == 0xffffffff) {
  2075. Options = (Priority << 28);
  2076. } else {
  2077. Options = Options | (Priority << 28);
  2078. }
  2079. }
  2080. //
  2081. // Merge the ignore schedule bit with options attribute if supplied.
  2082. //
  2083. if (bIgnoreSchedule == TRUE) {
  2084. if (Options == 0xffffffff) {
  2085. Options = NTDSCONN_OPT_IGNORE_SCHEDULE_MASK;
  2086. } else {
  2087. Options = Options | NTDSCONN_OPT_IGNORE_SCHEDULE_MASK;
  2088. }
  2089. }
  2090. if (bCreateConnection) {
  2091. if (CxtionName == NULL) {
  2092. DPRINT0("Need a cxtion name to create a new connection\n");
  2093. Status = MKDSXE_BAD_ARG;
  2094. } else {
  2095. if (bSchedule) {
  2096. BuildSchedule(&pSchedule, Interval, Stagger, Offset, CustSchedData);
  2097. Status = CreateNewConnection(CxtionName, FromNTDSSettingsDn,
  2098. ToNTDSSettingsDn, bEnabled, Options, pSchedule);
  2099. } else {
  2100. Status = CreateNewConnection(CxtionName, FromNTDSSettingsDn,
  2101. ToNTDSSettingsDn, bEnabled, Options, NULL);
  2102. }
  2103. }
  2104. } else if (bUpdateConnection) {
  2105. if (bSchedule) {
  2106. BuildSchedule(&pSchedule, Interval, Stagger, Offset, CustSchedData);
  2107. Status = UpdateConnection(FromNTDSSettingsDn, ToNTDSSettingsDn,
  2108. bEnabled, Options, pSchedule);
  2109. } else {
  2110. Status = UpdateConnection(FromNTDSSettingsDn, ToNTDSSettingsDn,
  2111. bEnabled, Options, NULL);
  2112. }
  2113. } else if (bDelConnection) {
  2114. Status = DelConnection(FromNTDSSettingsDn, ToNTDSSettingsDn);
  2115. } else if (bDumpConnection) {
  2116. Status = DumpConnection(FromNTDSSettingsDn, ToNTDSSettingsDn);
  2117. }
  2118. ldap_unbind(pLdap);
  2119. return Status;
  2120. }