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

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