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.

3419 lines
84 KiB

  1. /*++
  2. Module Name:
  3. LDAPUtils.h
  4. Abstract:
  5. This is the header file for the LDAP utility functions.
  6. */
  7. //--------------------------------------------------------------------
  8. #include <stdafx.h>
  9. #include "LDAPUtils.h"
  10. #include <dsgetdc.h>
  11. #include <stdio.h>
  12. #include <ntdsapi.h>
  13. #include <lm.h>
  14. #include <ntldap.h>
  15. #include <winber.h>
  16. #include "dfsenums.h"
  17. #include "netutils.h"
  18. //----------------------------------------------------------------------------------
  19. HRESULT FreeLDAPNamesList
  20. (
  21. IN PLDAPNAME i_pLDAPNames // pointer to list to be freed.
  22. )
  23. {
  24. /*++
  25. Routine Description:
  26. Helper funciton used to free the NETNAME linked list retrurned by
  27. LDAP helper functions.
  28. Arguments:
  29. i_pLDAPNames - Pointer to the first node in the list to be freed.
  30. Return value:
  31. S_OK, on Success.
  32. E_POINTER, Illegal pointer was passed.
  33. --*/
  34. PLDAPNAME pNodeToFree = NULL;
  35. try
  36. {
  37. while (NULL != i_pLDAPNames)
  38. {
  39. pNodeToFree = i_pLDAPNames;
  40. i_pLDAPNames = i_pLDAPNames->Next;
  41. delete pNodeToFree;
  42. }
  43. } // try
  44. catch (...)
  45. {
  46. return E_POINTER;
  47. }
  48. return S_OK;
  49. } // HRESULT FreeDomainList
  50. HRESULT FreeAttrValList
  51. (
  52. IN PLDAP_ATTR_VALUE i_pAttrVals
  53. )
  54. {
  55. /*++
  56. Routine Description:
  57. Helper funciton used to free the LDAP_ATTR_VALUE linked list retrurned by
  58. LDAP helper functions.
  59. Arguments:
  60. i_pLDAPNames - Pointer to the first node in the list to be freed.
  61. Return value:
  62. S_OK, on Success.
  63. E_POINTER, Illegal pointer was passed.
  64. --*/
  65. PLDAP_ATTR_VALUE pNodeToFree = NULL;
  66. try
  67. {
  68. while (NULL != i_pAttrVals)
  69. {
  70. pNodeToFree = i_pAttrVals;
  71. i_pAttrVals = i_pAttrVals->Next;
  72. if (NULL != pNodeToFree->vpValue)
  73. {
  74. free(pNodeToFree->vpValue);
  75. }
  76. delete pNodeToFree;
  77. }
  78. } // try
  79. catch (...)
  80. {
  81. return E_POINTER;
  82. }
  83. return S_OK;
  84. }
  85. //----------------------------------------------------------------------------------
  86. HRESULT ConnectToDS
  87. (
  88. IN PCTSTR i_lpszDomainName, // DNS or non DNS format.
  89. OUT PLDAP *o_ppldap,
  90. OUT BSTR* o_pbstrDC // = NULL
  91. )
  92. {
  93. /*++
  94. Routine Description:
  95. Opens an LDAP connection to a valid DC (DC re-fetched if down).
  96. Arguments:
  97. i_lpszDomainName - Name of the domain, DNS or Non Dns format.
  98. o_ppldap - Pointer to LDAP handle in returned here.
  99. NULL on failure.
  100. Return value:
  101. S_OK, on Success.
  102. E_INVALIDARG, Illegal pointer was passed.
  103. E_FAIL, if connection could not be established.
  104. Any Other error code returned by ldap or Net apis.
  105. --*/
  106. RETURN_INVALIDARG_IF_NULL(o_ppldap);
  107. *o_ppldap = NULL;
  108. //
  109. // open a ldap connection to a valid DC
  110. //
  111. HRESULT hr = S_OK;
  112. DWORD dwErr = 0;
  113. CComBSTR bstrDCName;
  114. PLDAP pldap = NULL;
  115. BOOL bRetry = FALSE;
  116. do {
  117. #ifdef DEBUG
  118. SYSTEMTIME time0 = {0};
  119. GetSystemTime(&time0);
  120. #endif // DEBUG
  121. //
  122. // pick a DC
  123. //
  124. PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
  125. if (bRetry)
  126. dwErr = DsGetDcName(NULL, i_lpszDomainName, NULL, NULL,
  127. DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME | DS_FORCE_REDISCOVERY, &pDCInfo);
  128. else
  129. dwErr = DsGetDcName(NULL, i_lpszDomainName, NULL, NULL,
  130. DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME, &pDCInfo);
  131. #ifdef DEBUG
  132. SYSTEMTIME time1 = {0};
  133. GetSystemTime(&time1);
  134. PrintTimeDelta(_T("ConnectToDS-DsGetDcName"), &time0, &time1);
  135. #endif // DEBUG
  136. if (ERROR_SUCCESS != dwErr)
  137. {
  138. hr = HRESULT_FROM_WIN32(dwErr);
  139. break;
  140. }
  141. if ( !mylstrncmpi(pDCInfo->DomainControllerName, _T("\\\\"), 2) )
  142. bstrDCName = pDCInfo->DomainControllerName + 2;
  143. else
  144. bstrDCName = pDCInfo->DomainControllerName;
  145. NetApiBufferFree(pDCInfo);
  146. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDCName, &hr);
  147. //
  148. // make ldap connection to this DC
  149. //
  150. pldap = ldap_init(bstrDCName, LDAP_PORT);
  151. if (!pldap)
  152. {
  153. hr = HRESULT_FROM_WIN32(GetLastError());
  154. break;
  155. }
  156. //
  157. // Making ldap_open/ldap_connect with a server name without first setting
  158. // LDAP_OPT_AREC_EXCLUSIVE (for ldap interfaces) or
  159. // ADS_SERVER_BIND (for ADSI interfaces) will result in bogus DNS queries
  160. // consuming bandwidth and potentially bringing up remote links that are
  161. // costly or demand dial.
  162. //
  163. // ignore the return of ldap_set_option
  164. ldap_set_option(pldap, LDAP_OPT_AREC_EXCLUSIVE, LDAP_OPT_ON);
  165. ULONG ulRet = ldap_connect(pldap, NULL); // NULL for the default timeout
  166. #ifdef DEBUG
  167. SYSTEMTIME time2 = {0};
  168. GetSystemTime(&time2);
  169. PrintTimeDelta(_T("ConnectToDS-ldap_connect"), &time1, &time2);
  170. #endif // DEBUG
  171. if (LDAP_SERVER_DOWN == ulRet && !bRetry)
  172. {
  173. ldap_unbind(pldap);
  174. bRetry = TRUE; // retry once to pick another DC
  175. } else
  176. {
  177. if (LDAP_SUCCESS != ulRet)
  178. {
  179. ldap_unbind(pldap);
  180. hr = HRESULT_FROM_WIN32(LdapMapErrorToWin32(ulRet));
  181. }
  182. break;
  183. }
  184. } while (1);
  185. RETURN_IF_FAILED(hr);
  186. //
  187. // bind to this ldap connection
  188. //
  189. dwErr = ldap_bind_s(pldap, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  190. if (LDAP_SUCCESS != dwErr)
  191. {
  192. dwErr = LdapMapErrorToWin32(dwErr);
  193. DebugOutLDAPError(pldap, dwErr, _T("ldap_bind_s"));
  194. ldap_unbind(pldap);
  195. hr = HRESULT_FROM_WIN32(dwErr);
  196. } else
  197. {
  198. *o_ppldap = pldap;
  199. if (o_pbstrDC)
  200. {
  201. *o_pbstrDC = bstrDCName.Copy();
  202. if (!*o_pbstrDC)
  203. {
  204. ldap_unbind(pldap);
  205. *o_ppldap = NULL;
  206. hr = E_OUTOFMEMORY;
  207. }
  208. }
  209. }
  210. return hr;
  211. }
  212. HRESULT CloseConnectionToDS
  213. (
  214. IN PLDAP i_pldap
  215. )
  216. {
  217. /*++
  218. Routine Description:
  219. Closes an open LDAP connection.
  220. Arguments:
  221. i_pldap - Open LDAP connection handle.
  222. Return value:
  223. S_OK, on Success.
  224. E_FAIL, if connection could not be established.
  225. Any Other error code returned by ldap or Net apis.
  226. --*/
  227. if (NULL == i_pldap)
  228. {
  229. return(E_INVALIDARG);
  230. }
  231. DWORD dwErr = ldap_unbind(i_pldap);
  232. if (LDAP_SUCCESS != dwErr)
  233. {
  234. dwErr = LdapMapErrorToWin32(dwErr);
  235. return(HRESULT_FROM_WIN32(dwErr));
  236. }
  237. else
  238. {
  239. return(S_OK);
  240. }
  241. }
  242. // Gets Values for an attribute from an LDAP Object.
  243. HRESULT GetValues
  244. (
  245. IN PLDAP i_pldap,
  246. IN PCTSTR i_lpszBase,
  247. IN PCTSTR i_lpszSearchFilter,
  248. IN ULONG i_ulScope,
  249. IN ULONG i_ulAttrCount,
  250. IN LDAP_ATTR_VALUE i_pAttributes[],
  251. OUT PLDAP_ATTR_VALUE o_ppValues[]
  252. )
  253. {
  254. /*++
  255. Routine Description:
  256. Gets Values for an attribute from an LDAP Object given Object class.
  257. Object Class can be "*" etc.
  258. Arguments:
  259. i_pldap - An open, bound ldap port.
  260. i_lpszBase - The base path of a DS object, can be "".
  261. i_lpszSearchFilter - LDAP Search Filter.
  262. i_ulScope - The search scope.
  263. i_ulAttrCount - Count of attributes passed in i_lpszAttributes.
  264. i_pAttributes - Attributes for which to get values. The bBerValue has to be set.
  265. o_ppValues - Array of pointers, size = i_ulAttrCount, each pointer to a list
  266. of values corresponding to the respective attribute in i_pAttributes.
  267. The bstrAttribute is not set for values. For BerVal types the
  268. bBerValue and ulLength are set.
  269. Return Value:
  270. S_OK, on Success.
  271. E_INVALIDARG, Illegal pointer was passed.
  272. E_OUTOFMEMORY on memory allocation failure.
  273. E_FAIL, if connection could not be established.
  274. Any Other error code returned by ldap or Net apis.
  275. --*/
  276. DWORD dwErr;
  277. LPTSTR lpszCurrentAttr = NULL;
  278. BerElement *BerElm = NULL;
  279. PLDAPMessage pMsg = NULL;
  280. PLDAPMessage pEntry = NULL;
  281. HRESULT hr = S_OK;
  282. if (!i_pldap ||
  283. !i_lpszBase ||
  284. !i_lpszSearchFilter ||
  285. (i_ulAttrCount < 1) ||
  286. !i_pAttributes ||
  287. !o_ppValues)
  288. {
  289. return(E_INVALIDARG);
  290. }
  291. // Prepare the list of attributes to be sent to ldap_search.
  292. LPTSTR *lpszAttributes = new LPTSTR[i_ulAttrCount + 1];
  293. if (!lpszAttributes)
  294. return E_OUTOFMEMORY;
  295. lpszAttributes[i_ulAttrCount] = NULL;
  296. for (ULONG i = 0; i < i_ulAttrCount; i++)
  297. lpszAttributes[i] = i_pAttributes[i].bstrAttribute;
  298. // Execute the search.
  299. dwErr = ldap_search_s (i_pldap,
  300. (PTSTR)i_lpszBase,
  301. i_ulScope,
  302. (PTSTR)i_lpszSearchFilter,
  303. lpszAttributes,
  304. 0,
  305. &pMsg
  306. );
  307. delete [] lpszAttributes;
  308. if (LDAP_SUCCESS != dwErr)
  309. {
  310. dwErr = LdapMapErrorToWin32(dwErr);
  311. DebugOutLDAPError(i_pldap, dwErr, _T("ldap_search_s"));
  312. hr = HRESULT_FROM_WIN32(dwErr);
  313. } else
  314. {
  315. LPTSTR lpszCurrentAttr = ldap_first_attribute(i_pldap, pMsg, &BerElm);
  316. if (!lpszCurrentAttr)
  317. {
  318. dfsDebugOut((_T("GetValues of %s returned NULL attributes.\n"), i_lpszBase));
  319. hr = HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED);
  320. } else
  321. {
  322. // For each attribute, build a list of values
  323. // by scanning each entry for the given attribute.
  324. for (i = 0; i < i_ulAttrCount && SUCCEEDED(hr); i++)
  325. {
  326. PLDAP_ATTR_VALUE *ppCurrent = &(o_ppValues[i]);
  327. // Scan each attribute of the entry for an exact match
  328. for(lpszCurrentAttr = ldap_first_attribute(i_pldap, pMsg, &BerElm);
  329. lpszCurrentAttr != NULL && SUCCEEDED(hr);
  330. lpszCurrentAttr = ldap_next_attribute(i_pldap, pMsg, BerElm))
  331. {
  332. // Is there a match?
  333. if (0 == lstrcmpi(i_pAttributes[i].bstrAttribute, lpszCurrentAttr))
  334. {
  335. // Add the value to the linked list for this attribute.
  336. LPTSTR *lpszCurrentValue = NULL, *templpszValue = NULL;
  337. LDAP_BERVAL **ppBerVal = NULL, **tempBerVal = NULL;
  338. if (i_pAttributes[i].bBerValue)
  339. {
  340. tempBerVal = ppBerVal = ldap_get_values_len(i_pldap, pMsg, lpszCurrentAttr);
  341. while(*ppBerVal && SUCCEEDED(hr))
  342. {
  343. *ppCurrent = new LDAP_ATTR_VALUE;
  344. if (!*ppCurrent)
  345. {
  346. hr = E_OUTOFMEMORY;
  347. } else
  348. {
  349. (*ppCurrent)->ulLength = (*ppBerVal)->bv_len;
  350. (*ppCurrent)->bBerValue = true;
  351. (*ppCurrent)->vpValue = malloc((*ppBerVal)->bv_len);
  352. if (!(*ppCurrent)->vpValue)
  353. {
  354. delete *ppCurrent;
  355. hr = E_OUTOFMEMORY;
  356. } else
  357. {
  358. memcpy(
  359. (*ppCurrent)->vpValue,
  360. (void *)(*ppBerVal)->bv_val,
  361. (*ppBerVal)->bv_len
  362. );
  363. (*ppCurrent)->Next = NULL;
  364. ppBerVal++;
  365. ppCurrent = &((*ppCurrent)->Next);
  366. }
  367. }
  368. } // while
  369. if (NULL != tempBerVal)
  370. ldap_value_free_len(tempBerVal);
  371. }
  372. else
  373. {
  374. templpszValue = lpszCurrentValue = ldap_get_values(i_pldap, pMsg, lpszCurrentAttr);
  375. while(*lpszCurrentValue && SUCCEEDED(hr))
  376. {
  377. *ppCurrent = new LDAP_ATTR_VALUE;
  378. if (NULL == *ppCurrent)
  379. {
  380. hr = E_OUTOFMEMORY;
  381. } else
  382. {
  383. (*ppCurrent)->bBerValue = false;
  384. (*ppCurrent)->vpValue = (void *)_tcsdup(*lpszCurrentValue);
  385. (*ppCurrent)->Next = NULL;
  386. if (NULL == (*ppCurrent)->vpValue)
  387. {
  388. delete *ppCurrent;
  389. hr = E_OUTOFMEMORY;
  390. } else
  391. {
  392. lpszCurrentValue++;
  393. ppCurrent = &((*ppCurrent)->Next);
  394. }
  395. }
  396. } // while
  397. if (NULL != templpszValue)
  398. ldap_value_free(templpszValue);
  399. }
  400. }
  401. }
  402. }
  403. }
  404. }
  405. // free pMsg because ldap_search_s always allocates pMsg
  406. if (pMsg)
  407. ldap_msgfree(pMsg);
  408. if (FAILED(hr))
  409. {
  410. for (i = 0; i < i_ulAttrCount; i++)
  411. FreeAttrValList(o_ppValues[i]);
  412. }
  413. return hr;
  414. }
  415. void FreeLListElem(LListElem* pElem)
  416. {
  417. LListElem* pCurElem = NULL;
  418. LListElem* pNextElem = pElem;
  419. while (pCurElem = pNextElem)
  420. {
  421. pNextElem = pCurElem->Next;
  422. delete pCurElem;
  423. }
  424. }
  425. HRESULT GetValuesEx
  426. (
  427. IN PLDAP i_pldap,
  428. IN PCTSTR i_pszBase,
  429. IN ULONG i_ulScope,
  430. IN PCTSTR i_pszSearchFilter,
  431. IN PCTSTR i_pszAttributes[],
  432. OUT LListElem** o_ppElem
  433. )
  434. {
  435. if (!i_pldap ||
  436. !i_pszBase ||
  437. !i_pszSearchFilter ||
  438. !i_pszAttributes ||
  439. !o_ppElem)
  440. {
  441. return(E_INVALIDARG);
  442. }
  443. *o_ppElem = NULL;
  444. //
  445. // count number of attributes
  446. //
  447. ULONG ulNumOfAttributes = 0;
  448. PTSTR* ppszAttr = (PTSTR *)i_pszAttributes;
  449. while (*ppszAttr++)
  450. ulNumOfAttributes++;
  451. if (!ulNumOfAttributes)
  452. return E_INVALIDARG;
  453. HRESULT hr = S_OK;
  454. PLDAPMessage pMsg = NULL;
  455. DWORD dwErr = ldap_search_s(i_pldap,
  456. (PTSTR)i_pszBase,
  457. i_ulScope,
  458. (PTSTR)i_pszSearchFilter,
  459. (PTSTR *)i_pszAttributes,
  460. 0,
  461. &pMsg
  462. );
  463. if (LDAP_SUCCESS != dwErr)
  464. {
  465. dwErr = LdapMapErrorToWin32(dwErr);
  466. DebugOutLDAPError(i_pldap, dwErr, _T("ldap_search_s"));
  467. hr = HRESULT_FROM_WIN32(dwErr);
  468. } else
  469. {
  470. PLDAPMessage pMsgEntry = NULL;
  471. BerElement* pBerElm = NULL;
  472. PTSTR pszCurrentAttr = NULL;
  473. LListElem* pHeadElem = NULL;
  474. LListElem* pCurElem = NULL;
  475. // Scan each entry to find the value set for the DN attribute.
  476. for(pMsgEntry = ldap_first_entry(i_pldap, pMsg); pMsgEntry; pMsgEntry = ldap_next_entry(i_pldap, pMsgEntry))
  477. {
  478. PTSTR** pppszValueArray = (PTSTR **)calloc(ulNumOfAttributes + 1, sizeof(PTSTR **));
  479. BREAK_OUTOFMEMORY_IF_NULL(pppszValueArray, &hr);
  480. // Read each attribute of the entry into the array
  481. for(pszCurrentAttr = ldap_first_attribute(i_pldap, pMsgEntry, &pBerElm); pszCurrentAttr; pszCurrentAttr = ldap_next_attribute(i_pldap, pMsgEntry, pBerElm))
  482. {
  483. PTSTR* ppszValues = ldap_get_values(i_pldap, pMsgEntry, pszCurrentAttr);
  484. for (ULONG i = 0; i < ulNumOfAttributes; i++)
  485. {
  486. if (!lstrcmpi(i_pszAttributes[i], pszCurrentAttr))
  487. {
  488. pppszValueArray[i] = ppszValues;
  489. break;
  490. }
  491. }
  492. } // end of attribute enumeration
  493. LListElem* pNewElem = new LListElem(pppszValueArray);
  494. if (!pNewElem)
  495. {
  496. free(pppszValueArray);
  497. hr = E_OUTOFMEMORY;
  498. break;
  499. }
  500. if (!pCurElem)
  501. {
  502. pHeadElem = pCurElem = pNewElem;
  503. } else
  504. {
  505. pCurElem->Next = pNewElem;
  506. pCurElem = pNewElem;
  507. }
  508. } // end of entry enumeration
  509. if (FAILED(hr))
  510. FreeLListElem(pHeadElem);
  511. else
  512. *o_ppElem = pHeadElem;
  513. }
  514. // free pMsg because ldap_search_s always allocates pMsg
  515. if (pMsg)
  516. ldap_msgfree(pMsg);
  517. return hr;
  518. }
  519. HRESULT GetLDAPRootPath
  520. (
  521. IN PLDAP i_pldap,
  522. OUT LPTSTR* o_ppszRootPath
  523. )
  524. /*++
  525. Routine Description:
  526. Return the DS pathname of the global configuration container.
  527. Arguments:
  528. pldap - An open and bound ldap handle.
  529. lpszRootPath - The LDAP path us returned here.
  530. Return Value:
  531. S_OK on success.
  532. E_FAIL on failure.
  533. E_OUTOFMEORY if memory allocation fails.
  534. E_INVALIDARG if null pointer arguments were passed.
  535. --*/
  536. {
  537. LPTSTR *ppszValues = NULL;
  538. PLDAP_ATTR_VALUE pDNName[1] = {0};
  539. PLDAP_ATTR_VALUE pCurrent = NULL;
  540. if (NULL == i_pldap || NULL == o_ppszRootPath)
  541. {
  542. return(E_INVALIDARG);
  543. }
  544. LPTSTR lpszCoDN = NULL;
  545. *o_ppszRootPath = NULL;
  546. LDAP_ATTR_VALUE pAttributes[1];
  547. pAttributes[0].bstrAttribute = _T("namingContexts");
  548. pAttributes[0].bBerValue = false;
  549. HRESULT hr = GetValues( i_pldap,
  550. _T(""), // LDAP Root.
  551. OBJCLASS_SF_ALL, // All Objects
  552. LDAP_SCOPE_BASE,
  553. 1, // Only 1 attribute
  554. pAttributes, // namingContexts Attribute.
  555. pDNName // List of all values at Root for namingContexts.
  556. );
  557. if (FAILED(hr))
  558. return(hr);
  559. // Find the naming context that begins with CN=Configuration
  560. pCurrent = pDNName[0];
  561. while (pCurrent)
  562. {
  563. if (0 == mylstrncmpi((LPTSTR)pCurrent->vpValue, _T("DC="), 3))
  564. {
  565. *o_ppszRootPath= _tcsdup((LPTSTR)pCurrent->vpValue);
  566. hr = (*o_ppszRootPath) ? S_OK : E_OUTOFMEMORY;
  567. break;
  568. }
  569. pCurrent = pCurrent->Next;
  570. }
  571. FreeAttrValList(pDNName[0]);
  572. return(hr);
  573. }
  574. HRESULT GetChildrenDN
  575. (
  576. IN PLDAP i_pldap,
  577. IN LPCTSTR i_lpszBase,
  578. IN ULONG i_ulScope,
  579. IN LPTSTR i_lpszChildObjectClassSF,
  580. OUT PLDAPNAME* o_ppDistNames
  581. )
  582. /*++
  583. Routine Description:
  584. Return the Distinguished Name of all children of a given objectClass
  585. as a linked list of LDAPNAME structures.
  586. Arguments:
  587. pldap - An open and bound ldap handle.
  588. i_lpszBase - The base path of a DS object, can be "".
  589. o_ppDistNames - The linked of child DNs is returned here.
  590. i_lpszChildObjectClassSF - The objectClass of the children to list.
  591. E.g fTDfs, User.
  592. Return Value:
  593. S_OK on success.
  594. E_FAIL on failure.
  595. E_OUTOFMEORY if memory allocation fails.
  596. E_INVALIDARG if null pointer arguments were passed.
  597. --*/
  598. {
  599. DWORD dwErr;
  600. LPTSTR lpszCurrentAttr = NULL;
  601. LPTSTR *plpszValues = NULL;
  602. BerElement *BerElm = NULL;
  603. PLDAPMessage pMsg = NULL;
  604. PLDAPMessage pEntry = NULL;
  605. PLDAPNAME *ppCurrent;
  606. HRESULT hr = S_OK;
  607. if (!i_pldap || !i_lpszBase || !o_ppDistNames ||
  608. !i_lpszChildObjectClassSF || !*i_lpszChildObjectClassSF)
  609. {
  610. return(E_INVALIDARG);
  611. }
  612. *o_ppDistNames = NULL;
  613. ppCurrent = o_ppDistNames;
  614. LPTSTR lpszAttributes[2] = {0,0};
  615. lpszAttributes[0] = _T("distinguishedName");
  616. // Execute the search.
  617. dwErr = ldap_search_s (i_pldap,
  618. (LPTSTR)i_lpszBase,
  619. i_ulScope,
  620. i_lpszChildObjectClassSF,
  621. lpszAttributes,
  622. 0,
  623. &pMsg
  624. );
  625. if (LDAP_SUCCESS != dwErr)
  626. {
  627. dwErr = LdapMapErrorToWin32(dwErr);
  628. DebugOutLDAPError(i_pldap, dwErr, _T("ldap_search_s"));
  629. hr = HRESULT_FROM_WIN32(dwErr);
  630. } else
  631. {
  632. // Scan each entry to find the value set for the DN attribute.
  633. for(pEntry = ldap_first_entry(i_pldap, pMsg);
  634. pEntry != NULL;
  635. pEntry = ldap_next_entry(i_pldap, pEntry))
  636. {
  637. CComBSTR bstrCN;
  638. // Scan each attribute of the entry for DN
  639. for(lpszCurrentAttr = ldap_first_attribute(i_pldap, pEntry, &BerElm);
  640. lpszCurrentAttr != NULL;
  641. lpszCurrentAttr = ldap_next_attribute(i_pldap, pEntry, BerElm))
  642. {
  643. plpszValues = ldap_get_values( i_pldap,
  644. pEntry,
  645. lpszCurrentAttr
  646. );
  647. // Is there a match for CN?
  648. if (0 == lstrcmpi(_T("distinguishedName"), lpszCurrentAttr))
  649. {
  650. bstrCN = plpszValues[0];
  651. }
  652. }
  653. // LDAP object does not have valid fields.
  654. if (!bstrCN)
  655. continue;
  656. // Add to list.
  657. *ppCurrent = new LDAPNAME;
  658. if (NULL == *ppCurrent)
  659. {
  660. hr = E_OUTOFMEMORY;
  661. break;
  662. }
  663. (*ppCurrent)->Next = NULL;
  664. (*ppCurrent)->bstrLDAPName = bstrCN.m_str;
  665. if (!(*ppCurrent)->bstrLDAPName)
  666. {
  667. delete *ppCurrent;
  668. *ppCurrent = NULL;
  669. hr = E_OUTOFMEMORY;
  670. break;
  671. }
  672. ppCurrent = &((*ppCurrent)->Next);
  673. }
  674. if (NULL == *o_ppDistNames)
  675. {
  676. hr = E_FAIL;
  677. }
  678. if (S_OK != hr)
  679. {
  680. FreeLDAPNamesList(*ppCurrent);
  681. *ppCurrent = NULL;
  682. hr = E_OUTOFMEMORY;
  683. }
  684. }
  685. // free pMsg because ldap_search_s always allocates pMsg
  686. if (pMsg)
  687. ldap_msgfree(pMsg);
  688. return(hr);
  689. }
  690. HRESULT GetConnectionDNs
  691. (
  692. IN PLDAP i_pldap,
  693. IN LPCTSTR i_lpszBase,
  694. IN LPTSTR i_lpszChildObjectClassSF,
  695. OUT PLDAPNAME* o_ppDistNames
  696. )
  697. {
  698. DWORD dwErr;
  699. LPTSTR lpszCurrentAttr = NULL;
  700. LPTSTR *plpszValues = NULL;
  701. BerElement *BerElm = NULL;
  702. PLDAPMessage pMsg = NULL;
  703. PLDAPMessage pEntry = NULL;
  704. PLDAPNAME *ppCurrent;
  705. HRESULT hr = S_OK;
  706. if (!i_pldap || !i_lpszBase || !o_ppDistNames ||
  707. !i_lpszChildObjectClassSF || !*i_lpszChildObjectClassSF)
  708. {
  709. return(E_INVALIDARG);
  710. }
  711. *o_ppDistNames = NULL;
  712. ppCurrent = o_ppDistNames;
  713. LPTSTR lpszAttributes[2] = {0,0};
  714. lpszAttributes[0] = _T("distinguishedName");
  715. // Execute the search.
  716. dwErr = ldap_search_s (i_pldap,
  717. (LPTSTR)i_lpszBase,
  718. LDAP_SCOPE_ONELEVEL,
  719. i_lpszChildObjectClassSF,
  720. lpszAttributes,
  721. 0,
  722. &pMsg
  723. );
  724. if (LDAP_SUCCESS != dwErr)
  725. {
  726. dwErr = LdapMapErrorToWin32(dwErr);
  727. DebugOutLDAPError(i_pldap, dwErr, _T("ldap_search_s"));
  728. hr = HRESULT_FROM_WIN32(dwErr);
  729. } else
  730. {
  731. // Scan each entry to find the value set for the DN attribute.
  732. for(pEntry = ldap_first_entry(i_pldap, pMsg);
  733. pEntry != NULL;
  734. pEntry = ldap_next_entry(i_pldap, pEntry))
  735. {
  736. CComBSTR bstrCN;
  737. // Scan each attribute of the entry for DN
  738. for(lpszCurrentAttr = ldap_first_attribute(i_pldap, pEntry, &BerElm);
  739. lpszCurrentAttr != NULL;
  740. lpszCurrentAttr = ldap_next_attribute(i_pldap, pEntry, BerElm))
  741. {
  742. plpszValues = ldap_get_values( i_pldap,
  743. pEntry,
  744. lpszCurrentAttr
  745. );
  746. // Is there a match for CN?
  747. if (0 == lstrcmpi(_T("distinguishedName"), lpszCurrentAttr))
  748. {
  749. bstrCN = plpszValues[0];
  750. }
  751. }
  752. // LDAP object does not have valid fields.
  753. if (!bstrCN)
  754. continue;
  755. // Add to list.
  756. *ppCurrent = new LDAPNAME;
  757. if (NULL == *ppCurrent)
  758. {
  759. hr = E_OUTOFMEMORY;
  760. break;
  761. }
  762. (*ppCurrent)->Next = NULL;
  763. (*ppCurrent)->bstrLDAPName = bstrCN.m_str;
  764. if (!(*ppCurrent)->bstrLDAPName)
  765. {
  766. delete *ppCurrent;
  767. *ppCurrent = NULL;
  768. hr = E_OUTOFMEMORY;
  769. break;
  770. }
  771. ppCurrent = &((*ppCurrent)->Next);
  772. }
  773. if (NULL == *o_ppDistNames)
  774. {
  775. hr = E_FAIL;
  776. }
  777. if (S_OK != hr)
  778. {
  779. FreeLDAPNamesList(*ppCurrent);
  780. *ppCurrent = NULL;
  781. hr = E_OUTOFMEMORY;
  782. }
  783. }
  784. // free pMsg because ldap_search_s always allocates pMsg
  785. if (pMsg)
  786. ldap_msgfree(pMsg);
  787. return(hr);
  788. }
  789. HRESULT PrepareLDAPMods
  790. (
  791. IN LDAP_ATTR_VALUE i_pAttrValue[],
  792. IN LDAP_ENTRY_ACTION i_AddModDel,
  793. IN ULONG i_ulCountOfVals,
  794. OUT LDAPMod* o_ppModVals[]
  795. )
  796. {
  797. /*++
  798. Routine Description:
  799. Fills up a LPDAMod pointer array given a array of attribute value pairs.
  800. The mod_op field of all LPDAMod structures returned depends on the value of i_AddModDel.
  801. Arguments:
  802. i_pAttrValue - An array of LDAP_ATTR_VALUE structures containing
  803. the attribute and name value pairs.
  804. i_AddModDel - One of LDAP_ENTRY_ACTION enum value.
  805. i_ulCountOfVals - The size of i_pAttrValue array (the number of values).
  806. o_ppModVals - Pointer to a pre-allocated (and NULL terminated) array of pointers to
  807. LPDAPMod structures. The LPDAMod structures and allocated and returned here.
  808. Size of this should be i_ulCountOfVals.
  809. Return value:
  810. S_OK, On success
  811. E_INVALIDARG, if an invalid (NULL) pointer was passed.
  812. E_OUTOEMEMORY, if memory allocation fails.
  813. Any other network (ldap) error.
  814. --*/
  815. if (NULL == i_pAttrValue || NULL == o_ppModVals)
  816. {
  817. return(E_INVALIDARG);
  818. }
  819. for (ULONG i = 0, k = 0; k < i_ulCountOfVals; i++, k++)
  820. {
  821. //
  822. // have to skip objectClass attribute in case of modify/delete,
  823. // otherwise, ldap_modify_xxx will return LDAP_UNWILLING_TO_PERFORM
  824. //
  825. if (ADD_VALUE != i_AddModDel &&
  826. !lstrcmpi(i_pAttrValue[k].bstrAttribute, ATTR_OBJCLASS))
  827. {
  828. k++;
  829. }
  830. o_ppModVals[i] = new LDAPMod;
  831. o_ppModVals[i]->mod_type = _tcsdup(i_pAttrValue[k].bstrAttribute);
  832. switch (i_AddModDel)
  833. {
  834. case ADD_VALUE:
  835. o_ppModVals[i]->mod_op = LDAP_MOD_ADD;
  836. break;
  837. case MODIFY_VALUE:
  838. o_ppModVals[i]->mod_op = LDAP_MOD_REPLACE;
  839. break;
  840. case DELETE_VALUE:
  841. o_ppModVals[i]->mod_op = LDAP_MOD_DELETE;
  842. break;
  843. }
  844. // Count the number of values for this attribute.
  845. PLDAP_ATTR_VALUE pAttrVal = &(i_pAttrValue[k]);
  846. ULONG ulCountOfVals = 0;
  847. while (pAttrVal)
  848. {
  849. ulCountOfVals++;
  850. pAttrVal = pAttrVal->Next;
  851. }
  852. pAttrVal = &(i_pAttrValue[k]);
  853. ULONG j = 0;
  854. if (i_pAttrValue[k].bBerValue)
  855. {
  856. PLDAP_BERVAL* ppBerValues = NULL;
  857. ppBerValues = new PLDAP_BERVAL[ulCountOfVals + 1];
  858. ppBerValues[ulCountOfVals] = NULL;
  859. while (pAttrVal)
  860. {
  861. ppBerValues[j] = new LDAP_BERVAL;
  862. if (!pAttrVal->vpValue)
  863. {
  864. ppBerValues[j]->bv_len = 0;
  865. ppBerValues[j]->bv_val = NULL;
  866. } else
  867. {
  868. ppBerValues[j]->bv_len = pAttrVal->ulLength;
  869. ppBerValues[j]->bv_val = new char[pAttrVal->ulLength];
  870. memcpy(
  871. (void *)ppBerValues[j]->bv_val,
  872. pAttrVal->vpValue,
  873. pAttrVal->ulLength
  874. );
  875. }
  876. pAttrVal = pAttrVal->Next;
  877. j++;
  878. }
  879. o_ppModVals[i]->mod_bvalues = ppBerValues;
  880. o_ppModVals[i]->mod_op |= LDAP_MOD_BVALUES;
  881. }
  882. else
  883. {
  884. LPTSTR* plpszValues = NULL;
  885. plpszValues = new LPTSTR[ulCountOfVals + 1];
  886. plpszValues[ulCountOfVals] = NULL;
  887. while (pAttrVal)
  888. {
  889. if (pAttrVal->vpValue)
  890. plpszValues[j] = _tcsdup((LPTSTR)(pAttrVal->vpValue));
  891. else
  892. plpszValues[j] = NULL;
  893. pAttrVal = pAttrVal->Next;
  894. j++;
  895. }
  896. o_ppModVals[i]->mod_values = plpszValues;
  897. }
  898. }
  899. return(S_OK);
  900. }
  901. HRESULT AddValues
  902. (
  903. IN PLDAP i_pldap,
  904. IN LPCTSTR i_DN,
  905. IN ULONG i_ulCountOfVals,
  906. IN LDAP_ATTR_VALUE i_pAttrValue[],
  907. IN BSTR i_bstrDC // = NULL
  908. )
  909. {
  910. /*++
  911. Routine Description:
  912. This method add an attribute value (and a new LDAP object if it does not exist)
  913. in the DS. The parent of the given DN must exist. This can be used to add a new object
  914. and also to add new values for attributes of an existing object in which case
  915. the DN must exist.
  916. Arguments:
  917. i_pldap - Open LDAP connection context.
  918. i_DN - Distinguished name of the (new) object.
  919. i_pAttrValue - Array of pointers to LDAP_ATTR_VALUE containing attribue and value.
  920. i_ulCountOfVals - The size of i_pAttrValue array (the number of values).
  921. Return value:
  922. S_OK, On success
  923. E_INVALIDARG, if an invalid (NULL) pointer was passed.
  924. E_OUTOEMEMORY, if memory allocation fails.
  925. Any other network (ldap) error.
  926. --*/
  927. if (NULL == i_pldap || NULL == i_DN || NULL == i_pAttrValue)
  928. {
  929. return(E_INVALIDARG);
  930. }
  931. LDAPMod** ppModVals = NULL;
  932. HRESULT hr = S_FALSE;
  933. ppModVals = new LDAPMod*[i_ulCountOfVals + 1];
  934. if (NULL == ppModVals)
  935. {
  936. return(E_OUTOFMEMORY);
  937. }
  938. for (ULONG i = 0; i <= i_ulCountOfVals; i++)
  939. {
  940. ppModVals[i] = NULL;
  941. }
  942. do
  943. {
  944. hr = PrepareLDAPMods(
  945. i_pAttrValue,
  946. ADD_VALUE,
  947. i_ulCountOfVals,
  948. ppModVals
  949. );
  950. if (FAILED(hr))
  951. {
  952. break;
  953. }
  954. DWORD dwStatus = LDAP_SUCCESS;
  955. if (!i_bstrDC)
  956. {
  957. dwStatus = ldap_add_s(
  958. i_pldap,
  959. (LPTSTR)i_DN,
  960. ppModVals
  961. );
  962. } else
  963. {
  964. //
  965. // prepare the server hint
  966. //
  967. LDAPControl simpleControl;
  968. PLDAPControl controlArray[2];
  969. INT rc;
  970. BERVAL* pBerVal = NULL;
  971. BerElement* pBer;
  972. pBer = ber_alloc_t(LBER_USE_DER);
  973. if (!pBer)
  974. {
  975. hr = E_OUTOFMEMORY;
  976. break;
  977. }
  978. rc = ber_printf(pBer,"{io}", 0, i_bstrDC, wcslen(i_bstrDC) * sizeof(WCHAR));
  979. if ( rc == -1 ) {
  980. hr = E_FAIL;
  981. break;
  982. }
  983. rc = ber_flatten(pBer, &pBerVal);
  984. if (rc == -1)
  985. {
  986. hr = E_FAIL;
  987. break;
  988. }
  989. ber_free(pBer,1);
  990. controlArray[0] = &simpleControl;
  991. controlArray[1] = NULL;
  992. simpleControl.ldctl_oid = LDAP_SERVER_VERIFY_NAME_OID_W;
  993. simpleControl.ldctl_iscritical = TRUE;
  994. simpleControl.ldctl_value = *pBerVal;
  995. dwStatus = ldap_add_ext_s(
  996. i_pldap,
  997. (LPTSTR)i_DN,
  998. ppModVals,
  999. (PLDAPControl *)&controlArray, //ServerControls,
  1000. NULL //ClientControls,
  1001. );
  1002. ber_bvfree(pBerVal);
  1003. }
  1004. if (LDAP_SUCCESS == dwStatus)
  1005. {
  1006. hr = S_OK;
  1007. } else if (LDAP_ALREADY_EXISTS == dwStatus)
  1008. {
  1009. hr = ModifyValues(i_pldap, i_DN, i_ulCountOfVals, i_pAttrValue);
  1010. }
  1011. else
  1012. {
  1013. dwStatus = LdapMapErrorToWin32(dwStatus);
  1014. DebugOutLDAPError(i_pldap, dwStatus, _T("ldap_add_ext_s"));
  1015. hr = HRESULT_FROM_WIN32(dwStatus);
  1016. }
  1017. } while (false);
  1018. FreeModVals(&ppModVals);
  1019. delete[] ppModVals;
  1020. return(hr);
  1021. }
  1022. // Modifies an existing record or values.
  1023. HRESULT ModifyValues
  1024. (
  1025. IN PLDAP i_pldap,
  1026. IN LPCTSTR i_DN,
  1027. IN ULONG i_ulCountOfVals,
  1028. IN LDAP_ATTR_VALUE i_pAttrValue[]
  1029. )
  1030. {
  1031. /*++
  1032. Routine Description:
  1033. This method modifies attribute values of a DS object given its DN.
  1034. The DN object must exist.
  1035. Arguments:
  1036. i_pldap - Open LDAP connection context.
  1037. i_DN - Distinguished name of the object.
  1038. i_pAttrValue - Array of pointers to LDAP_ATTR_VALUE containing attribue and value.
  1039. i_ulCountOfVals - The size of i_pAttrValue array (the number of values).
  1040. Return value:
  1041. S_OK, On success
  1042. E_INVALIDARG, if an invalid (NULL) pointer was passed.
  1043. E_OUTOEMEMORY, if memory allocation fails.
  1044. Any other network (ldap) error.
  1045. --*/
  1046. if (NULL == i_pldap || NULL == i_DN || NULL == i_pAttrValue)
  1047. {
  1048. return(E_INVALIDARG);
  1049. }
  1050. LDAPMod** ppModVals = NULL;
  1051. HRESULT hr = S_FALSE;
  1052. ppModVals = new LDAPMod*[i_ulCountOfVals + 1];
  1053. if (NULL == ppModVals)
  1054. {
  1055. return(E_OUTOFMEMORY);
  1056. }
  1057. for (ULONG i = 0; i <= i_ulCountOfVals; i++)
  1058. {
  1059. ppModVals[i] = NULL;
  1060. }
  1061. do
  1062. {
  1063. hr = PrepareLDAPMods(
  1064. i_pAttrValue,
  1065. MODIFY_VALUE,
  1066. i_ulCountOfVals,
  1067. ppModVals
  1068. );
  1069. if (FAILED(hr))
  1070. {
  1071. break;
  1072. }
  1073. //
  1074. // With this server side control, ldap_modify will return success
  1075. // if modifying an existing attribute with same value, or deleting
  1076. // an attribute with no value
  1077. //
  1078. BERVAL berVal = {0};
  1079. LDAPControl permissiveControl;
  1080. PLDAPControl controlArray[2];
  1081. controlArray[0] = &permissiveControl;
  1082. controlArray[1] = NULL;
  1083. permissiveControl.ldctl_oid = LDAP_SERVER_PERMISSIVE_MODIFY_OID_W;
  1084. permissiveControl.ldctl_iscritical = FALSE;
  1085. permissiveControl.ldctl_value = berVal;
  1086. DWORD dwStatus = ldap_modify_ext_s(
  1087. i_pldap,
  1088. (LPTSTR)i_DN,
  1089. ppModVals,
  1090. (PLDAPControl *)&controlArray, //ServerControls,
  1091. NULL //ClientControls,
  1092. );
  1093. if (LDAP_SUCCESS == dwStatus || LDAP_ATTRIBUTE_OR_VALUE_EXISTS == dwStatus)
  1094. {
  1095. hr = S_OK;
  1096. break;
  1097. }
  1098. else
  1099. {
  1100. dwStatus = LdapMapErrorToWin32(dwStatus);
  1101. DebugOutLDAPError(i_pldap, dwStatus, _T("ldap_modify_ext_s"));
  1102. hr = HRESULT_FROM_WIN32(dwStatus);
  1103. break;
  1104. }
  1105. }
  1106. while (false);
  1107. FreeModVals(&ppModVals);
  1108. delete[] ppModVals;
  1109. return(hr);
  1110. }
  1111. // Deletes values from an existing record or values.
  1112. HRESULT DeleteValues
  1113. (
  1114. IN PLDAP i_pldap,
  1115. IN LPCTSTR i_DN,
  1116. IN ULONG i_ulCountOfVals,
  1117. IN LDAP_ATTR_VALUE i_pAttrValue[]
  1118. )
  1119. {
  1120. /*++
  1121. Routine Description:
  1122. This method deletes attribute values of a DS object given its DN.
  1123. The DN object must exist.
  1124. Arguments:
  1125. i_pldap - Open LDAP connection context.
  1126. i_DN - Distinguished name of the object.
  1127. i_pAttrValue - Array of pointers to LDAP_ATTR_VALUE containing attribue and value.
  1128. i_ulCountOfVals - The size of i_pAttrValue array (the number of values).
  1129. Return value:
  1130. S_OK, On success
  1131. E_INVALIDARG, if an invalid (NULL) pointer was passed.
  1132. E_OUTOEMEMORY, if memory allocation fails.
  1133. Any other network (ldap) error.
  1134. --*/
  1135. if (NULL == i_pldap || NULL == i_DN || NULL == i_pAttrValue)
  1136. {
  1137. return(E_INVALIDARG);
  1138. }
  1139. LDAPMod** ppModVals = NULL;
  1140. HRESULT hr = S_FALSE;
  1141. ppModVals = new LDAPMod*[i_ulCountOfVals + 1];
  1142. if (NULL == ppModVals)
  1143. {
  1144. return(E_OUTOFMEMORY);
  1145. }
  1146. for (ULONG i = 0; i <= i_ulCountOfVals; i++)
  1147. {
  1148. ppModVals[i] = NULL;
  1149. }
  1150. do
  1151. {
  1152. hr = PrepareLDAPMods(
  1153. i_pAttrValue,
  1154. DELETE_VALUE,
  1155. i_ulCountOfVals,
  1156. ppModVals
  1157. );
  1158. if (FAILED(hr))
  1159. {
  1160. break;
  1161. }
  1162. //
  1163. // With this server side control, ldap_modify will return success
  1164. // if modifying an existing attribute with same value, or deleting
  1165. // an attribute with no value
  1166. //
  1167. BERVAL berVal = {0};
  1168. LDAPControl permissiveControl;
  1169. PLDAPControl controlArray[2];
  1170. controlArray[0] = &permissiveControl;
  1171. controlArray[1] = NULL;
  1172. permissiveControl.ldctl_oid = LDAP_SERVER_PERMISSIVE_MODIFY_OID_W;
  1173. permissiveControl.ldctl_iscritical = FALSE;
  1174. permissiveControl.ldctl_value = berVal;
  1175. DWORD dwStatus = ldap_modify_ext_s(
  1176. i_pldap,
  1177. (LPTSTR)i_DN,
  1178. ppModVals,
  1179. (PLDAPControl *)&controlArray, //ServerControls,
  1180. NULL //ClientControls,
  1181. );
  1182. if (LDAP_SUCCESS == dwStatus || LDAP_NO_SUCH_ATTRIBUTE == dwStatus)
  1183. {
  1184. hr = S_OK;
  1185. break;
  1186. }
  1187. else
  1188. {
  1189. dwStatus = LdapMapErrorToWin32(dwStatus);
  1190. DebugOutLDAPError(i_pldap, dwStatus, _T("ldap_modify_ext_s"));
  1191. hr = HRESULT_FROM_WIN32(dwStatus);
  1192. break;
  1193. }
  1194. }
  1195. while (false);
  1196. FreeModVals(&ppModVals);
  1197. delete[] ppModVals;
  1198. return(hr);
  1199. }
  1200. // Deletes an object, recursive or non-recursive.
  1201. HRESULT DeleteDSObject
  1202. (
  1203. IN PLDAP i_pldap,
  1204. IN LPCTSTR i_DN,
  1205. IN bool i_bDeleteRecursively //= true
  1206. )
  1207. {
  1208. if (i_bDeleteRecursively)
  1209. {
  1210. PLDAPNAME pDNs = NULL;
  1211. PLDAPNAME pTemp = NULL;
  1212. HRESULT hr = GetChildrenDN(
  1213. i_pldap,
  1214. i_DN,
  1215. LDAP_SCOPE_ONELEVEL,
  1216. OBJCLASS_SF_ALL,
  1217. &pDNs
  1218. );
  1219. if (S_OK == hr)
  1220. {
  1221. pTemp = pDNs;
  1222. while (pTemp)
  1223. {
  1224. DeleteDSObject(i_pldap, pTemp->bstrLDAPName);
  1225. pTemp = pTemp->Next;
  1226. }
  1227. FreeLDAPNamesList(pDNs);
  1228. }
  1229. }
  1230. DWORD dwStatus = ldap_delete_s(
  1231. i_pldap,
  1232. (LPTSTR)i_DN
  1233. );
  1234. if ( LDAP_NO_SUCH_OBJECT == dwStatus ||
  1235. (!i_bDeleteRecursively && LDAP_NOT_ALLOWED_ON_NONLEAF == dwStatus) )
  1236. return S_FALSE;
  1237. if ( LDAP_SUCCESS != dwStatus)
  1238. {
  1239. dwStatus = LdapMapErrorToWin32(dwStatus);
  1240. DebugOutLDAPError(i_pldap, dwStatus, _T("ldap_delete_s"));
  1241. }
  1242. return HRESULT_FROM_WIN32(dwStatus);
  1243. }
  1244. HRESULT FreeModVals
  1245. (
  1246. IN OUT LDAPMod ***pppMod
  1247. )
  1248. /*++
  1249. Routine Description:
  1250. Free the LPDAMod structures. Frees all LDAPMod values and pointers.
  1251. Arguments:
  1252. pppMod - Address of a null-terminated array of LPDAMod.
  1253. Return Value:
  1254. S_OK, On success
  1255. E_INVALIDARG, if an invalid (NULL) pointer was passed.
  1256. --*/
  1257. {
  1258. if (NULL == pppMod)
  1259. {
  1260. return(E_INVALIDARG);
  1261. }
  1262. DWORD i, j;
  1263. LDAPMod **ppMod;
  1264. if (NULL == *pppMod)
  1265. {
  1266. // Nothing to do.
  1267. return(S_OK);
  1268. }
  1269. ppMod = *pppMod;
  1270. // For each attribute entry, free all its values.
  1271. for (i = 0; ppMod[i] != NULL; i++)
  1272. {
  1273. for (j = 0; (ppMod[i])->mod_values[j] != NULL; j++)
  1274. {
  1275. if (ppMod[i]->mod_op & LDAP_MOD_BVALUES)
  1276. {
  1277. delete (ppMod[i]->mod_bvalues[j]->bv_val);
  1278. }
  1279. delete ((ppMod[i])->mod_values[j]);
  1280. }
  1281. delete ((ppMod[i])->mod_values); // Free the array of pointers to values
  1282. delete ((ppMod[i])->mod_type); // Free the string identifying the attribute
  1283. delete (ppMod[i]); // Free the attribute
  1284. }
  1285. return(S_OK);
  1286. }
  1287. LPTSTR ErrorString
  1288. (
  1289. DWORD i_ldapErrCode
  1290. )
  1291. {
  1292. /*++
  1293. Routine Description:
  1294. Gets a string corresponding to the ldap error code.
  1295. Arguments:
  1296. i_ldapErrCode - The ldap error code to map to an error string.
  1297. Return Value:
  1298. The pointer to the error string.
  1299. --*/
  1300. return(ldap_err2string(i_ldapErrCode));
  1301. }
  1302. HRESULT IsValidObject
  1303. (
  1304. IN PLDAP i_pldap,
  1305. IN BSTR i_bstrObjectDN
  1306. )
  1307. {
  1308. /*++
  1309. Routine Description:
  1310. Checks if an object with given DN exists.
  1311. Arguments:
  1312. i_bstrObjectDN - The DN of the object.
  1313. Return value:
  1314. S_OK, Object exist
  1315. S_FALSE, no such object
  1316. Others, error occurred
  1317. --*/
  1318. if (NULL == i_bstrObjectDN)
  1319. {
  1320. return(E_INVALIDARG);
  1321. }
  1322. PLDAP_ATTR_VALUE pValues[2] = {0,0}, pCurrent = NULL;
  1323. LDAP_ATTR_VALUE pAttributes[1];
  1324. pAttributes[0].bstrAttribute = _T("Name");
  1325. pAttributes[0].bBerValue = false;
  1326. HRESULT hr = GetValues(
  1327. i_pldap,
  1328. i_bstrObjectDN,
  1329. OBJCLASS_SF_ALL,
  1330. LDAP_SCOPE_BASE,
  1331. 1,
  1332. pAttributes,
  1333. pValues
  1334. );
  1335. if (SUCCEEDED(hr))
  1336. FreeAttrValList(pValues[0]);
  1337. else
  1338. hr = S_FALSE;
  1339. return(hr);
  1340. }
  1341. HRESULT CrackName(
  1342. IN HANDLE i_hDS,
  1343. IN LPTSTR i_lpszOldTypeName,
  1344. IN DS_NAME_FORMAT i_formatIn,
  1345. IN DS_NAME_FORMAT i_formatdesired,
  1346. OUT BSTR* o_pbstrResult
  1347. )
  1348. {
  1349. if (!i_hDS || !i_lpszOldTypeName || !*i_lpszOldTypeName || !o_pbstrResult)
  1350. return E_INVALIDARG;
  1351. *o_pbstrResult = NULL;
  1352. HRESULT hr = S_OK;
  1353. DS_NAME_RESULT* pDsNameResult = NULL;
  1354. DWORD dwErr = DsCrackNames(
  1355. i_hDS,
  1356. DS_NAME_NO_FLAGS,
  1357. i_formatIn,
  1358. i_formatdesired,
  1359. 1,
  1360. &i_lpszOldTypeName,
  1361. &pDsNameResult
  1362. );
  1363. if (ERROR_SUCCESS != dwErr)
  1364. hr = HRESULT_FROM_WIN32(dwErr);
  1365. else
  1366. {
  1367. if (DS_NAME_NO_ERROR != pDsNameResult->rItems->status)
  1368. hr = HRESULT_FROM_WIN32(pDsNameResult->rItems->status);
  1369. else
  1370. {
  1371. *o_pbstrResult = SysAllocString(pDsNameResult->rItems->pName);
  1372. if (!*o_pbstrResult)
  1373. hr = E_OUTOFMEMORY;
  1374. }
  1375. DsFreeNameResult(pDsNameResult);
  1376. }
  1377. return hr;
  1378. }
  1379. void RemoveBracesOnGuid(IN OUT BSTR bstrGuid)
  1380. {
  1381. if (!bstrGuid || !*bstrGuid)
  1382. return;
  1383. TCHAR *p = bstrGuid + lstrlen(bstrGuid) - 1;
  1384. if (_T('}') == *p)
  1385. *p = _T('\0');
  1386. p = bstrGuid;
  1387. if (_T('{') == *p)
  1388. {
  1389. while (*++p)
  1390. *(p-1) = *p;
  1391. *(p-1) = _T('\0');
  1392. }
  1393. }
  1394. //+-------------------------------------------------------------------------
  1395. //
  1396. // Function: GetDomainInfo
  1397. //
  1398. // Synopsis: return DC Dns name, DomainDN, and/or LDAP://<DC>/<DomainDN>
  1399. //
  1400. //--------------------------------------------------------------------------
  1401. HRESULT GetDomainInfo(
  1402. IN LPCTSTR i_bstrDomain,
  1403. OUT BSTR* o_pbstrDC, // return DC's Dns name
  1404. OUT BSTR* o_pbstrDomainDnsName, // return Domain's Dns name
  1405. OUT BSTR* o_pbstrDomainDN, // return DC=nttest,DC=microsoft,DC=com
  1406. OUT BSTR* o_pbstrLDAPDomainPath,// return LDAP://<DC>/<DomainDN>
  1407. OUT BSTR* o_pbstrDomainGuid // return Domain's guid in string without {}
  1408. )
  1409. {
  1410. if (o_pbstrDC) *o_pbstrDC = NULL;
  1411. if (o_pbstrDomainDnsName) *o_pbstrDomainDnsName = NULL;
  1412. if (o_pbstrDomainDN) *o_pbstrDomainDN = NULL;
  1413. if (o_pbstrLDAPDomainPath) *o_pbstrLDAPDomainPath = NULL;
  1414. if (o_pbstrDomainGuid) *o_pbstrDomainGuid = NULL;
  1415. HRESULT hr = S_OK;
  1416. BOOL bRetry = FALSE;
  1417. BOOL b50Domain = FALSE;
  1418. CComBSTR bstrDCName;
  1419. CComBSTR bstrDomainDnsName;
  1420. CComBSTR bstrDomainDN;
  1421. CComBSTR bstrLDAPDomainPath;
  1422. CComBSTR bstrDomainGuid;
  1423. HANDLE hDS = NULL;
  1424. DWORD dwErr = ERROR_SUCCESS;
  1425. do {
  1426. #ifdef DEBUG
  1427. SYSTEMTIME time0 = {0};
  1428. GetSystemTime(&time0);
  1429. #endif // DEBUG
  1430. PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
  1431. if (bRetry)
  1432. dwErr = DsGetDcName(NULL, i_bstrDomain, NULL, NULL,
  1433. DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME | DS_FORCE_REDISCOVERY, &pDCInfo);
  1434. else
  1435. dwErr = DsGetDcName(NULL, i_bstrDomain, NULL, NULL,
  1436. DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME, &pDCInfo);
  1437. #ifdef DEBUG
  1438. SYSTEMTIME time1 = {0};
  1439. GetSystemTime(&time1);
  1440. PrintTimeDelta(_T("GetDomainInfo-DsGetDcName"), &time0, &time1);
  1441. #endif // DEBUG
  1442. if (ERROR_SUCCESS != dwErr)
  1443. return HRESULT_FROM_WIN32(dwErr);
  1444. b50Domain = pDCInfo->Flags & DS_DS_FLAG;
  1445. if ( !mylstrncmpi(pDCInfo->DomainControllerName, _T("\\\\"), 2) )
  1446. bstrDCName = pDCInfo->DomainControllerName + 2;
  1447. else
  1448. bstrDCName = pDCInfo->DomainControllerName;
  1449. // remove the ending dot
  1450. int len = _tcslen(pDCInfo->DomainName);
  1451. if ( _T('.') == *(pDCInfo->DomainName + len - 1) )
  1452. *(pDCInfo->DomainName + len - 1) = _T('\0');
  1453. bstrDomainDnsName = pDCInfo->DomainName;
  1454. NetApiBufferFree(pDCInfo);
  1455. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDCName, &hr);
  1456. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDomainDnsName, &hr);
  1457. hr = b50Domain ? S_OK : S_FALSE;
  1458. if (!b50Domain ||
  1459. !o_pbstrDC &&
  1460. !o_pbstrDomainDnsName &&
  1461. !o_pbstrDomainDN &&
  1462. !o_pbstrLDAPDomainPath &&
  1463. !o_pbstrDomainGuid)
  1464. return hr;
  1465. if (!o_pbstrDomainDN && !o_pbstrLDAPDomainPath && !o_pbstrDomainGuid)
  1466. break;
  1467. dwErr = DsBind(bstrDCName, bstrDomainDnsName, &hDS);
  1468. hr = HRESULT_FROM_WIN32(dwErr);
  1469. #ifdef DEBUG
  1470. SYSTEMTIME time2 = {0};
  1471. GetSystemTime(&time2);
  1472. PrintTimeDelta(_T("GetDomainInfo-DsBind"), &time1, &time2);
  1473. #endif // DEBUG
  1474. if ((RPC_S_SERVER_UNAVAILABLE == dwErr || RPC_S_CALL_FAILED == dwErr) && !bRetry)
  1475. bRetry = TRUE; // only retry once
  1476. else
  1477. break;
  1478. } while (1);
  1479. if (FAILED(hr))
  1480. return hr;
  1481. if (hDS)
  1482. {
  1483. do {
  1484. CComBSTR bstrDomainTrailing = bstrDomainDnsName;
  1485. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDomainTrailing, &hr);
  1486. bstrDomainTrailing += _T("/"); // add the trailing slash
  1487. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDomainTrailing, &hr);
  1488. hr = CrackName(
  1489. hDS,
  1490. bstrDomainTrailing,
  1491. DS_CANONICAL_NAME,
  1492. DS_FQDN_1779_NAME,
  1493. &bstrDomainDN
  1494. );
  1495. BREAK_IF_FAILED(hr);
  1496. if (o_pbstrLDAPDomainPath)
  1497. {
  1498. bstrLDAPDomainPath = _T("LDAP://");
  1499. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrLDAPDomainPath, &hr);
  1500. bstrLDAPDomainPath += bstrDCName;
  1501. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrLDAPDomainPath, &hr);
  1502. bstrLDAPDomainPath += _T("/");
  1503. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrLDAPDomainPath, &hr);
  1504. bstrLDAPDomainPath += bstrDomainDN;
  1505. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrLDAPDomainPath, &hr);
  1506. }
  1507. if (o_pbstrDomainGuid)
  1508. {
  1509. hr = CrackName(
  1510. hDS,
  1511. bstrDomainTrailing,
  1512. DS_CANONICAL_NAME,
  1513. DS_UNIQUE_ID_NAME,
  1514. &bstrDomainGuid
  1515. );
  1516. BREAK_IF_FAILED(hr);
  1517. RemoveBracesOnGuid(bstrDomainGuid);
  1518. }
  1519. } while (0);
  1520. DsUnBind(&hDS);
  1521. } while (0);
  1522. if (SUCCEEDED(hr))
  1523. {
  1524. if (o_pbstrDC)
  1525. *o_pbstrDC = bstrDCName.Detach();
  1526. if (o_pbstrDomainDnsName)
  1527. *o_pbstrDomainDnsName = bstrDomainDnsName.Detach();
  1528. if (o_pbstrDomainDN)
  1529. *o_pbstrDomainDN = bstrDomainDN.Detach();
  1530. if (o_pbstrLDAPDomainPath)
  1531. *o_pbstrLDAPDomainPath = bstrLDAPDomainPath.Detach();
  1532. if (o_pbstrDomainGuid)
  1533. *o_pbstrDomainGuid = bstrDomainGuid.Detach();
  1534. }
  1535. return hr;
  1536. }
  1537. HRESULT GetRootDomainName(
  1538. IN LPCTSTR i_bstrDomainName,
  1539. OUT BSTR* o_pbstrRootDomainName
  1540. )
  1541. {
  1542. #ifdef DEBUG
  1543. SYSTEMTIME time0 = {0};
  1544. GetSystemTime(&time0);
  1545. #endif // DEBUG
  1546. PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
  1547. DWORD dwErr = DsGetDcName(NULL, i_bstrDomainName, NULL, NULL,
  1548. DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME, &pDCInfo);
  1549. #ifdef DEBUG
  1550. SYSTEMTIME time1 = {0};
  1551. GetSystemTime(&time1);
  1552. PrintTimeDelta(_T("GetRootDomainName-DsGetDcName"), &time0, &time1);
  1553. #endif // DEBUG
  1554. if (ERROR_SUCCESS != dwErr)
  1555. return HRESULT_FROM_WIN32(dwErr);
  1556. // remove the ending dot
  1557. int len = _tcslen(pDCInfo->DnsForestName);
  1558. if ( _T('.') == *(pDCInfo->DnsForestName + len - 1) )
  1559. *(pDCInfo->DnsForestName + len - 1) = _T('\0');
  1560. *o_pbstrRootDomainName = SysAllocString(pDCInfo->DnsForestName);
  1561. NetApiBufferFree(pDCInfo);
  1562. if (!*o_pbstrRootDomainName)
  1563. return E_OUTOFMEMORY;
  1564. return S_OK;
  1565. }
  1566. void
  1567. DebugOutLDAPError(
  1568. IN PLDAP i_pldap,
  1569. IN ULONG i_ulError,
  1570. IN PCTSTR i_pszLDAPFunctionName
  1571. )
  1572. {
  1573. #ifdef DEBUG
  1574. if (i_pldap && LDAP_SUCCESS != i_ulError)
  1575. {
  1576. TCHAR *pszExtendedError = NULL;
  1577. DWORD dwErrorEx = ldap_get_optionW(
  1578. i_pldap,
  1579. LDAP_OPT_SERVER_ERROR,
  1580. (void *) &pszExtendedError);
  1581. if (LDAP_SUCCESS == dwErrorEx)
  1582. {
  1583. dfsDebugOut((_T("%s returns error: %x, extended error: %s\n"),
  1584. i_pszLDAPFunctionName, i_ulError, pszExtendedError));
  1585. ldap_memfree(pszExtendedError);
  1586. } else
  1587. {
  1588. dfsDebugOut((_T("%s returns error: %x\n"),
  1589. i_pszLDAPFunctionName, i_ulError));
  1590. }
  1591. }
  1592. #endif // DEBUG
  1593. }
  1594. int
  1595. MyCompareStringN(
  1596. IN LPCTSTR lpString1,
  1597. IN LPCTSTR lpString2,
  1598. IN UINT cchCount,
  1599. IN DWORD dwCmpFlags
  1600. )
  1601. {
  1602. UINT nLen1 = (lpString1 ? lstrlen(lpString1) : 0);
  1603. UINT nLen2 = (lpString2 ? lstrlen(lpString2) : 0);
  1604. int nRet = CompareString(
  1605. LOCALE_USER_DEFAULT,
  1606. dwCmpFlags,
  1607. lpString1,
  1608. min(cchCount, nLen1),
  1609. lpString2,
  1610. min(cchCount, nLen2)
  1611. );
  1612. return (nRet - CSTR_EQUAL);
  1613. }
  1614. int
  1615. mylstrncmp(
  1616. IN LPCTSTR lpString1,
  1617. IN LPCTSTR lpString2,
  1618. IN UINT cchCount
  1619. )
  1620. {
  1621. return MyCompareStringN(lpString1, lpString2, cchCount, 0);
  1622. }
  1623. int
  1624. mylstrncmpi(
  1625. IN LPCTSTR lpString1,
  1626. IN LPCTSTR lpString2,
  1627. IN UINT cchCount
  1628. )
  1629. {
  1630. return MyCompareStringN(lpString1, lpString2, cchCount, NORM_IGNORECASE);
  1631. }
  1632. HRESULT ExtendDN
  1633. (
  1634. IN LPTSTR i_lpszCN,
  1635. IN LPTSTR i_lpszDN,
  1636. OUT BSTR *o_pbstrNewDN
  1637. )
  1638. {
  1639. RETURN_INVALIDARG_IF_NULL(o_pbstrNewDN);
  1640. RETURN_INVALIDARG_IF_TRUE(!i_lpszCN || !*i_lpszCN);
  1641. CComBSTR bstrNewDN = _T("CN=");
  1642. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrNewDN);
  1643. bstrNewDN += i_lpszCN;
  1644. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrNewDN);
  1645. if (i_lpszDN && *i_lpszDN)
  1646. {
  1647. bstrNewDN += _T(",");
  1648. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrNewDN);
  1649. bstrNewDN += i_lpszDN;
  1650. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrNewDN);
  1651. }
  1652. *o_pbstrNewDN = bstrNewDN.Detach();
  1653. return S_OK;
  1654. }
  1655. HRESULT ExtendDNIfLongJunctionName(
  1656. IN LPTSTR i_lpszJunctionName,
  1657. IN LPCTSTR i_lpszBaseDN,
  1658. OUT BSTR *o_pbstrNewDN
  1659. )
  1660. {
  1661. RETURN_INVALIDARG_IF_NULL(o_pbstrNewDN);
  1662. RETURN_INVALIDARG_IF_TRUE(!i_lpszJunctionName || !*i_lpszJunctionName);
  1663. HRESULT hr = S_OK;
  1664. if (_tcslen(i_lpszJunctionName) > MAX_RDN_KEY_SIZE)
  1665. {
  1666. // junction name is too long to be fit into one CN= name,
  1667. // we need to break it down into several CN= names
  1668. LPTSTR *paStrings = NULL;
  1669. DWORD dwEntries = 0;
  1670. hr = GetJunctionPathPartitions((PVOID *)&paStrings, &dwEntries, i_lpszJunctionName);
  1671. if (SUCCEEDED(hr))
  1672. {
  1673. CComBSTR bstrIn = i_lpszBaseDN;
  1674. CComBSTR bstrOut;
  1675. for (DWORD i=0; i<dwEntries; i++)
  1676. {
  1677. hr = ExtendDN(paStrings[i], bstrIn, &bstrOut);
  1678. if (FAILED(hr)) break;
  1679. bstrIn = bstrOut;
  1680. bstrOut.Empty();
  1681. }
  1682. free(paStrings);
  1683. if (SUCCEEDED(hr))
  1684. *o_pbstrNewDN = bstrIn.Detach();
  1685. }
  1686. } else {
  1687. // junction name can fit into one CN= name
  1688. ReplaceChar(i_lpszJunctionName, _T('\\'), _T('|'));
  1689. hr = ExtendDN(i_lpszJunctionName, (PTSTR)i_lpszBaseDN, o_pbstrNewDN);
  1690. }
  1691. return hr;
  1692. }
  1693. HRESULT ReplaceChar
  1694. (
  1695. IN OUT BSTR io_bstrString,
  1696. TCHAR i_cOldChar,
  1697. TCHAR i_cNewChar
  1698. )
  1699. /*++
  1700. Routine Description:
  1701. Replace all occurences of a char ("\") with another char ("_") in
  1702. the given string.
  1703. Arguments:
  1704. io_bstrString - The string which needs to be converted.
  1705. i_cOldChar - The original character.
  1706. i_cNewChar - The character to replace the old one with.
  1707. --*/
  1708. {
  1709. RETURN_INVALIDARG_IF_NULL(io_bstrString);
  1710. // Replace i_cOldChar by i_cNewChar
  1711. // allowed in DN.
  1712. LPTSTR lpszTempPtr = _tcschr(io_bstrString, i_cOldChar);
  1713. while (lpszTempPtr)
  1714. {
  1715. *lpszTempPtr = i_cNewChar;
  1716. lpszTempPtr = _tcschr(lpszTempPtr +1,i_cOldChar);
  1717. }
  1718. return S_OK;
  1719. }
  1720. HRESULT GetJunctionPathPartitions(
  1721. OUT PVOID *o_ppBuffer,
  1722. OUT DWORD *o_pdwEntries,
  1723. IN LPCTSTR i_pszJunctionPath
  1724. )
  1725. {
  1726. _ASSERT(o_ppBuffer && o_pdwEntries && i_pszJunctionPath && *i_pszJunctionPath);
  1727. if (!o_ppBuffer || !o_pdwEntries || !i_pszJunctionPath || !(*i_pszJunctionPath))
  1728. return(E_INVALIDARG);
  1729. HRESULT hr = S_OK;
  1730. int nLength = _tcslen(i_pszJunctionPath);
  1731. DWORD dwCount = nLength / MAX_RDN_KEY_SIZE + ((nLength % MAX_RDN_KEY_SIZE) ? 1 : 0);
  1732. PBYTE pBuffer = NULL;
  1733. pBuffer = (PBYTE)calloc(dwCount, sizeof(LPTSTR *) + (MAX_RDN_KEY_SIZE + 1) * sizeof(TCHAR));
  1734. if (!pBuffer)
  1735. {
  1736. hr = E_OUTOFMEMORY;
  1737. } else
  1738. {
  1739. DWORD i = 0;
  1740. LPTSTR *ppsz = NULL;
  1741. LPTSTR pString = NULL;
  1742. for (i=0; i<dwCount; i++)
  1743. {
  1744. ppsz = (LPTSTR *)(pBuffer + i * sizeof(LPTSTR *));
  1745. pString = (LPTSTR)(pBuffer + dwCount * sizeof(LPTSTR *) + i * (MAX_RDN_KEY_SIZE + 1) * sizeof(TCHAR));
  1746. _tcsncpy(pString, i_pszJunctionPath, MAX_RDN_KEY_SIZE);
  1747. ReplaceChar(pString, _T('\\'), _T('|'));
  1748. *ppsz = pString;
  1749. i_pszJunctionPath += MAX_RDN_KEY_SIZE;
  1750. }
  1751. *o_ppBuffer = pBuffer;
  1752. *o_pdwEntries = dwCount;
  1753. }
  1754. return hr;
  1755. }
  1756. HRESULT CreateExtraNodesIfLongJunctionName(
  1757. IN PLDAP i_pldap,
  1758. IN LPCTSTR i_lpszJunctionName,
  1759. IN LPCTSTR i_lpszBaseDN,
  1760. IN LPCTSTR i_lpszObjClass
  1761. )
  1762. {
  1763. _ASSERT(i_pldap &&
  1764. i_lpszJunctionName && *i_lpszJunctionName &&
  1765. i_lpszBaseDN && *i_lpszBaseDN &&
  1766. i_lpszObjClass && *i_lpszObjClass);
  1767. HRESULT hr = S_OK;
  1768. if (_tcslen(i_lpszJunctionName) > MAX_RDN_KEY_SIZE)
  1769. {
  1770. // junction name is too long to be fit into one CN= name,
  1771. // we need to break it down into several CN= names
  1772. LPTSTR *paStrings = NULL;
  1773. DWORD dwEntries = 0;
  1774. hr = GetJunctionPathPartitions((PVOID *)&paStrings, &dwEntries, i_lpszJunctionName);
  1775. if (SUCCEEDED(hr))
  1776. {
  1777. DWORD i = 0;
  1778. CComBSTR bstrIn = i_lpszBaseDN;
  1779. CComBSTR bstrOut;
  1780. for (i=0; i<(dwEntries-1); i++)
  1781. {
  1782. hr = ExtendDN(paStrings[i], bstrIn, &bstrOut);
  1783. if (SUCCEEDED(hr))
  1784. hr = CreateObjectSimple(i_pldap, bstrOut, i_lpszObjClass);
  1785. if (FAILED(hr)) break;
  1786. bstrIn = bstrOut;
  1787. bstrOut.Empty();
  1788. }
  1789. free(paStrings);
  1790. }
  1791. } // > MAX_RDN_KEY_SIZE
  1792. return hr;
  1793. }
  1794. HRESULT CreateObjectSimple(
  1795. IN PLDAP i_pldap,
  1796. IN LPCTSTR i_lpszDN,
  1797. IN LPCTSTR i_lpszObjClass
  1798. )
  1799. {
  1800. RETURN_INVALIDARG_IF_NULL(i_pldap);
  1801. RETURN_INVALIDARG_IF_NULL(i_lpszDN);
  1802. RETURN_INVALIDARG_IF_NULL(i_lpszObjClass);
  1803. LDAP_ATTR_VALUE pAttrVals[1];
  1804. pAttrVals[0].bstrAttribute = OBJCLASS_ATTRIBUTENAME;
  1805. pAttrVals[0].vpValue = (void *)i_lpszObjClass;
  1806. pAttrVals[0].bBerValue = false;
  1807. return AddValues(
  1808. i_pldap,
  1809. i_lpszDN,
  1810. 1,
  1811. pAttrVals
  1812. );
  1813. }
  1814. HRESULT DeleteExtraNodesIfLongJunctionName(
  1815. IN PLDAP i_pldap,
  1816. IN LPCTSTR i_lpszJunctionName,
  1817. IN LPCTSTR i_lpszDN
  1818. )
  1819. {
  1820. _ASSERT(i_pldap &&
  1821. i_lpszJunctionName && *i_lpszJunctionName &&
  1822. i_lpszDN && *i_lpszDN);
  1823. DWORD nLength = _tcslen(i_lpszJunctionName);
  1824. if (nLength > MAX_RDN_KEY_SIZE)
  1825. {
  1826. DWORD dwEntries = nLength / MAX_RDN_KEY_SIZE + ((nLength % MAX_RDN_KEY_SIZE) ? 1 : 0);
  1827. (void) DeleteAncestorNodesIfEmpty(i_pldap, i_lpszDN+3, dwEntries-1);
  1828. }
  1829. return S_OK;
  1830. }
  1831. HRESULT
  1832. CreateObjectsRecursively(
  1833. IN PLDAP i_pldap,
  1834. IN BSTR i_bstrDN,
  1835. IN UINT i_nLenPrefix,
  1836. IN LPCTSTR i_lpszObjClass)
  1837. {
  1838. RETURN_INVALIDARG_IF_NULL(i_pldap);
  1839. RETURN_INVALIDARG_IF_NULL(i_bstrDN);
  1840. RETURN_INVALIDARG_IF_NULL(i_lpszObjClass);
  1841. if (0 == i_nLenPrefix)
  1842. return S_OK;
  1843. HRESULT hr = IsValidObject(i_pldap, i_bstrDN);
  1844. if (S_OK == hr)
  1845. return S_OK;
  1846. CComBSTR bstrPrefix = CComBSTR(i_nLenPrefix, i_bstrDN);
  1847. PTSTR pszNextPrefix = _tcsstr(bstrPrefix + 3, _T("CN="));
  1848. UINT nLengthNext = (pszNextPrefix ? _tcslen(pszNextPrefix) : 0);
  1849. UINT nLengthThis = (pszNextPrefix ? (pszNextPrefix - bstrPrefix) : _tcslen(bstrPrefix));
  1850. hr = CreateObjectsRecursively(
  1851. i_pldap,
  1852. i_bstrDN + nLengthThis,
  1853. nLengthNext,
  1854. i_lpszObjClass);
  1855. if (SUCCEEDED(hr))
  1856. hr = CreateObjectSimple(
  1857. i_pldap,
  1858. i_bstrDN,
  1859. i_lpszObjClass);
  1860. return hr;
  1861. }
  1862. HRESULT DeleteAncestorNodesIfEmpty(
  1863. IN PLDAP i_pldap,
  1864. IN LPCTSTR i_lpszDN,
  1865. IN DWORD i_dwCount
  1866. )
  1867. {
  1868. _ASSERT(i_pldap &&
  1869. i_lpszDN && *i_lpszDN &&
  1870. i_dwCount > 0);
  1871. DWORD i = 0;
  1872. LPTSTR p = NULL;
  1873. for (i=0; i<i_dwCount; i++)
  1874. {
  1875. p = _tcsstr(i_lpszDN, _T("CN="));
  1876. if (p)
  1877. {
  1878. (void) DeleteDSObject(i_pldap, p, false);
  1879. i_lpszDN = p+3;
  1880. }
  1881. }
  1882. return S_OK;
  1883. }
  1884. HRESULT GetDfsLinkNameFromDN(
  1885. IN BSTR i_bstrReplicaSetDN,
  1886. OUT BSTR* o_pbstrDfsLinkName)
  1887. {
  1888. if (!i_bstrReplicaSetDN || !*i_bstrReplicaSetDN || !o_pbstrDfsLinkName)
  1889. return E_INVALIDARG;
  1890. HRESULT hr = S_OK;
  1891. PTSTR pszReplicaSetDN = NULL;
  1892. do {
  1893. //
  1894. // make a copy of the string
  1895. //
  1896. pszReplicaSetDN = _tcsdup(i_bstrReplicaSetDN);
  1897. BREAK_OUTOFMEMORY_IF_NULL(pszReplicaSetDN, &hr);
  1898. //
  1899. // change the string to all upper cases
  1900. //
  1901. _tcsupr(pszReplicaSetDN);
  1902. //
  1903. // get rid of suffix: Dfs Volumes\File Replication Service\system\.....
  1904. //
  1905. TCHAR* p = _tcsstr(pszReplicaSetDN, _T(",CN=DFS VOLUMES"));
  1906. if (!p)
  1907. {
  1908. hr = E_INVALIDARG;
  1909. break;
  1910. }
  1911. *p = _T('\0');
  1912. //
  1913. // reverse the string
  1914. //
  1915. _tcsrev(pszReplicaSetDN);
  1916. //
  1917. // get rid of the CN= clause about the DfsRoot container
  1918. //
  1919. PTSTR pszCN = _tcsstr(pszReplicaSetDN, _T("=NC,"));
  1920. if (!pszCN)
  1921. {
  1922. hr = E_INVALIDARG;
  1923. break;
  1924. }
  1925. pszCN += 4; // after this tep, pszCN points at the delta
  1926. //
  1927. // Now, the left over CN= clauses are all related to Dfs Link name
  1928. //
  1929. p = _tcsstr(pszCN, _T("=NC"));
  1930. if (!p)
  1931. {
  1932. hr = E_INVALIDARG; // there must be at least one CN= clause
  1933. break;
  1934. }
  1935. CComBSTR bstrLinkName;
  1936. do {
  1937. *p = _T('\0');
  1938. _tcsrev(pszCN);
  1939. bstrLinkName += pszCN;
  1940. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrLinkName, &hr);
  1941. pszCN = p + 3; // points to the next CN= clause
  1942. if (*pszCN && *pszCN == _T(','))
  1943. pszCN++;
  1944. if (!*pszCN)
  1945. break; // no more CN= clauses
  1946. p = _tcsstr(pszCN, _T("=NC"));
  1947. } while (p);
  1948. if (SUCCEEDED(hr))
  1949. {
  1950. ReplaceChar(bstrLinkName, _T('|'), _T('\\'));
  1951. *o_pbstrDfsLinkName = bstrLinkName.Detach();
  1952. }
  1953. } while (0);
  1954. if (pszReplicaSetDN)
  1955. free(pszReplicaSetDN);
  1956. return hr;
  1957. }
  1958. HRESULT GetReplicaSetContainer(
  1959. PLDAP i_pldap,
  1960. BSTR i_bstrDfsName,
  1961. BSTR* o_pbstrContainerDN)
  1962. {
  1963. RETURN_INVALIDARG_IF_NULL(i_pldap);
  1964. RETURN_INVALIDARG_IF_NULL(i_bstrDfsName);
  1965. RETURN_INVALIDARG_IF_NULL(o_pbstrContainerDN);
  1966. CComBSTR bstrDfsRootDN = _T("CN=");
  1967. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrDfsRootDN);
  1968. bstrDfsRootDN += i_bstrDfsName;
  1969. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrDfsRootDN);
  1970. bstrDfsRootDN += CN_DFSVOLUMES_PREFIX_COMMA;
  1971. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrDfsRootDN);
  1972. LPTSTR lpszDomainDN = NULL;
  1973. HRESULT hr = GetLDAPRootPath(i_pldap, &lpszDomainDN);
  1974. RETURN_IF_FAILED(hr);
  1975. bstrDfsRootDN += lpszDomainDN;
  1976. free(lpszDomainDN);
  1977. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrDfsRootDN);
  1978. *o_pbstrContainerDN = bstrDfsRootDN.Detach();
  1979. return S_OK;
  1980. }
  1981. HRESULT GetSubscriberDN(
  1982. IN BSTR i_bstrReplicaSetDN,
  1983. IN BSTR i_bstrDomainGuid,
  1984. IN BSTR i_bstrComputerDN,
  1985. OUT BSTR* o_pbstrSubscriberDN
  1986. )
  1987. {
  1988. RETURN_INVALIDARG_IF_NULL(i_bstrReplicaSetDN);
  1989. RETURN_INVALIDARG_IF_NULL(i_bstrDomainGuid);
  1990. RETURN_INVALIDARG_IF_NULL(i_bstrComputerDN);
  1991. RETURN_INVALIDARG_IF_NULL(o_pbstrSubscriberDN);
  1992. HRESULT hr = S_OK;
  1993. CComBSTR bstrSubscriberDN;
  1994. PTSTR pszReplicaSetDN = _tcsdup(i_bstrReplicaSetDN);
  1995. RETURN_OUTOFMEMORY_IF_NULL(pszReplicaSetDN);
  1996. _tcsupr(pszReplicaSetDN); // change to all upper case
  1997. do {
  1998. TCHAR* p = _tcsstr(pszReplicaSetDN, _T(",CN=DFS VOLUMES"));
  1999. if (!p)
  2000. {
  2001. hr = E_INVALIDARG;
  2002. break;
  2003. }
  2004. bstrSubscriberDN = CComBSTR((int)(p - pszReplicaSetDN) + 4, i_bstrReplicaSetDN);
  2005. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrSubscriberDN, &hr);
  2006. bstrSubscriberDN += i_bstrDomainGuid;
  2007. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrSubscriberDN, &hr);
  2008. bstrSubscriberDN += _T(",CN=DFS Volumes,CN=NTFRS Subscriptions,");
  2009. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrSubscriberDN, &hr);
  2010. bstrSubscriberDN += i_bstrComputerDN;
  2011. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrSubscriberDN, &hr);
  2012. } while (0);
  2013. free(pszReplicaSetDN);
  2014. if (SUCCEEDED(hr))
  2015. *o_pbstrSubscriberDN = bstrSubscriberDN.Detach();
  2016. return hr;
  2017. }
  2018. HRESULT CreateNtfrsMemberObject(
  2019. IN PLDAP i_pldap,
  2020. IN BSTR i_bstrMemberDN,
  2021. IN BSTR i_bstrComputerDN,
  2022. IN BSTR i_bstrDCofComputerObj
  2023. )
  2024. {
  2025. RETURN_INVALIDARG_IF_NULL(i_pldap);
  2026. RETURN_INVALIDARG_IF_NULL(i_bstrMemberDN);
  2027. RETURN_INVALIDARG_IF_NULL(i_bstrComputerDN);
  2028. HRESULT hr = S_OK;
  2029. LDAP_ATTR_VALUE pAttrVals[2];
  2030. pAttrVals[0].bstrAttribute = OBJCLASS_ATTRIBUTENAME;
  2031. pAttrVals[0].vpValue = (void *)OBJCLASS_NTFRSMEMBER;
  2032. pAttrVals[0].bBerValue = false;
  2033. pAttrVals[1].bstrAttribute = ATTR_FRS_MEMBER_COMPUTERREF;
  2034. pAttrVals[1].vpValue = (void *)i_bstrComputerDN;
  2035. pAttrVals[1].bBerValue = false;
  2036. hr = AddValues(
  2037. i_pldap,
  2038. i_bstrMemberDN,
  2039. 2,
  2040. pAttrVals,
  2041. i_bstrDCofComputerObj
  2042. );
  2043. return hr;
  2044. }
  2045. HRESULT CreateNtfrsSubscriberObject(
  2046. IN PLDAP i_pldap,
  2047. IN BSTR i_bstrSubscriberDN,
  2048. IN BSTR i_bstrMemberDN,
  2049. IN BSTR i_bstrRootPath,
  2050. IN BSTR i_bstrStagingPath,
  2051. IN BSTR i_bstrDC // validate MemberDN against this DC
  2052. )
  2053. {
  2054. RETURN_INVALIDARG_IF_NULL(i_pldap);
  2055. RETURN_INVALIDARG_IF_NULL(i_bstrSubscriberDN);
  2056. RETURN_INVALIDARG_IF_NULL(i_bstrMemberDN);
  2057. RETURN_INVALIDARG_IF_NULL(i_bstrRootPath);
  2058. RETURN_INVALIDARG_IF_NULL(i_bstrStagingPath);
  2059. RETURN_INVALIDARG_IF_NULL(i_bstrDC);
  2060. HRESULT hr = S_OK;
  2061. LDAP_ATTR_VALUE pAttrVals[4];
  2062. pAttrVals[0].bstrAttribute = OBJCLASS_ATTRIBUTENAME;
  2063. pAttrVals[0].vpValue = (void *)OBJCLASS_NTFRSSUBSCRIBER;
  2064. pAttrVals[0].bBerValue = false;
  2065. pAttrVals[1].bstrAttribute = ATTR_FRS_SUBSCRIBER_MEMBERREF;
  2066. pAttrVals[1].vpValue = (void *)i_bstrMemberDN;
  2067. pAttrVals[1].bBerValue = false;
  2068. pAttrVals[2].bstrAttribute = ATTR_FRS_SUBSCRIBER_ROOTPATH;
  2069. pAttrVals[2].vpValue = (void *)i_bstrRootPath;
  2070. pAttrVals[2].bBerValue = false;
  2071. pAttrVals[3].bstrAttribute = ATTR_FRS_SUBSCRIBER_STAGINGPATH;
  2072. pAttrVals[3].vpValue = (void *)i_bstrStagingPath;
  2073. pAttrVals[3].bBerValue = false;
  2074. hr = AddValues(
  2075. i_pldap,
  2076. i_bstrSubscriberDN,
  2077. 4,
  2078. pAttrVals,
  2079. i_bstrDC
  2080. );
  2081. return hr;
  2082. }
  2083. HRESULT CreateNtdsConnectionObject(
  2084. IN PLDAP i_pldap,
  2085. IN BSTR i_bstrConnectionDN,
  2086. IN BSTR i_bstrFromMemberDN,
  2087. IN BOOL i_bEnable
  2088. )
  2089. {
  2090. RETURN_INVALIDARG_IF_NULL(i_pldap);
  2091. RETURN_INVALIDARG_IF_NULL(i_bstrConnectionDN);
  2092. RETURN_INVALIDARG_IF_NULL(i_bstrFromMemberDN);
  2093. HRESULT hr = S_OK;
  2094. LDAP_ATTR_VALUE pAttrVals[4];
  2095. pAttrVals[0].bstrAttribute = OBJCLASS_ATTRIBUTENAME;
  2096. pAttrVals[0].vpValue = (void *)OBJCLASS_NTDSCONNECTION;
  2097. pAttrVals[0].bBerValue = false;
  2098. pAttrVals[1].bstrAttribute = ATTR_NTDS_CONNECTION_FROMSERVER;
  2099. pAttrVals[1].vpValue = (void *)i_bstrFromMemberDN;
  2100. pAttrVals[1].bBerValue = false;
  2101. pAttrVals[2].bstrAttribute = ATTR_NTDS_CONNECTION_ENABLEDCONNECTION;
  2102. pAttrVals[2].vpValue = (void *)(i_bEnable ? CONNECTION_ENABLED_TRUE : CONNECTION_ENABLED_FALSE);
  2103. pAttrVals[2].bBerValue = false;
  2104. pAttrVals[3].bstrAttribute = ATTR_NTDS_CONNECTION_OPTIONS;
  2105. pAttrVals[3].vpValue = (void *)DEFAULT_CONNECTION_OPTIONS;
  2106. pAttrVals[3].bBerValue = false;
  2107. hr = AddValues(
  2108. i_pldap,
  2109. i_bstrConnectionDN,
  2110. 4,
  2111. pAttrVals
  2112. );
  2113. return hr;
  2114. }
  2115. #define CN_SEARCH_UPR_DFSVOL_FRS_SYS _T(",CN=DFS VOLUMES,CN=FILE REPLICATION SERVICE,CN=SYSTEM")
  2116. #define CN_SEARCH_UPR_SYS _T(",CN=SYSTEM")
  2117. #define CN_SEARCH_UPR_FRS_SYS _T(",CN=FILE REPLICATION SERVICE,CN=SYSTEM")
  2118. HRESULT CreateNtfrsSettingsObjects(
  2119. IN PLDAP i_pldap,
  2120. IN BSTR i_bstrReplicaSetDN
  2121. )
  2122. {
  2123. RETURN_INVALIDARG_IF_NULL(i_pldap);
  2124. RETURN_INVALIDARG_IF_NULL(i_bstrReplicaSetDN);
  2125. HRESULT hr = S_OK;
  2126. //
  2127. // The first CN= clause is a nTFRSReplicaSet object.
  2128. // The clauses from the 2nd to the CN=System clause should be created
  2129. // as nTFRSSettings objects
  2130. //
  2131. PTSTR pszReplicaSetDN = _tcsdup(i_bstrReplicaSetDN);
  2132. RETURN_OUTOFMEMORY_IF_NULL(pszReplicaSetDN);
  2133. _tcsupr(pszReplicaSetDN);
  2134. TCHAR *pszNtfrsSettingsDN = NULL;
  2135. int lenPrefix = 0;
  2136. do {
  2137. // have pStart point at the 2nd CN=
  2138. TCHAR *pStart = _tcsstr(pszReplicaSetDN, _T(",CN="));
  2139. if (!pStart)
  2140. {
  2141. hr = E_INVALIDARG;
  2142. break;
  2143. }
  2144. pStart++;
  2145. // have pEnd points at the CN=SYSTEM
  2146. TCHAR *pEnd = _tcsstr(pszReplicaSetDN, CN_SEARCH_UPR_DFSVOL_FRS_SYS);
  2147. if (!pEnd)
  2148. {
  2149. hr = E_INVALIDARG;
  2150. break;
  2151. }
  2152. pEnd += lstrlen(CN_SEARCH_UPR_DFSVOL_FRS_SYS) - lstrlen(CN_SEARCH_UPR_SYS) + 1;
  2153. //
  2154. // calculate
  2155. //
  2156. pszNtfrsSettingsDN = i_bstrReplicaSetDN + ((BYTE*)pStart - (BYTE*)pszReplicaSetDN) / sizeof(TCHAR);
  2157. lenPrefix = (int)((BYTE*)pEnd - (BYTE*)pStart) / sizeof(TCHAR);
  2158. } while (0);
  2159. free(pszReplicaSetDN);
  2160. RETURN_IF_FAILED(hr);
  2161. hr = CreateObjectsRecursively(
  2162. i_pldap,
  2163. pszNtfrsSettingsDN,
  2164. lenPrefix,
  2165. OBJCLASS_NTFRSSETTINGS
  2166. );
  2167. return hr;
  2168. }
  2169. HRESULT CreateNtfrsSubscriptionsObjects(
  2170. IN PLDAP i_pldap,
  2171. IN BSTR i_bstrSubscriberDN,
  2172. IN BSTR i_bstrComputerDN
  2173. )
  2174. {
  2175. RETURN_INVALIDARG_IF_NULL(i_pldap);
  2176. RETURN_INVALIDARG_IF_NULL(i_bstrSubscriberDN);
  2177. RETURN_INVALIDARG_IF_NULL(i_bstrComputerDN);
  2178. //
  2179. // The first CN= clause is a nTFRSSubscriber object.
  2180. // The clauses from the 2nd to the CN=<computer> clause should be created
  2181. // as nTFRSSubscriptions objects
  2182. //
  2183. // have pStart point at the 2nd CN=
  2184. TCHAR *pStart = _tcsstr(i_bstrSubscriberDN, _T(",CN="));
  2185. RETURN_INVALIDARG_IF_NULL(pStart);
  2186. pStart++;
  2187. //
  2188. // calculate
  2189. //
  2190. TCHAR *pszNtfrsSubscriptionsDN = pStart;
  2191. int lenPrefix = lstrlen(pszNtfrsSubscriptionsDN) - lstrlen(i_bstrComputerDN);
  2192. HRESULT hr = CreateObjectsRecursively(
  2193. i_pldap,
  2194. pszNtfrsSubscriptionsDN,
  2195. lenPrefix,
  2196. OBJCLASS_NTFRSSUBSCRIPTIONS
  2197. );
  2198. return hr;
  2199. }
  2200. HRESULT DeleteNtfrsReplicaSetObjectAndContainers(
  2201. IN PLDAP i_pldap,
  2202. IN BSTR i_bstrReplicaSetDN
  2203. )
  2204. {
  2205. RETURN_INVALIDARG_IF_NULL(i_pldap);
  2206. RETURN_INVALIDARG_IF_NULL(i_bstrReplicaSetDN);
  2207. HRESULT hr = S_OK;
  2208. //
  2209. // The first CN= clause is a nTFRSReplicaSet object.
  2210. // The clauses from the 2nd to the CN=File Replication Service clause should
  2211. // be deleted if empty
  2212. //
  2213. PTSTR pszReplicaSetDN = _tcsdup(i_bstrReplicaSetDN);
  2214. RETURN_OUTOFMEMORY_IF_NULL(pszReplicaSetDN);
  2215. _tcsupr(pszReplicaSetDN);
  2216. int lenPrefix = 0;
  2217. TCHAR *pStart = NULL;
  2218. do {
  2219. // have pStart point at the 2nd CN=
  2220. pStart = _tcsstr(pszReplicaSetDN, _T(",CN="));
  2221. if (!pStart)
  2222. {
  2223. hr = E_INVALIDARG;
  2224. break;
  2225. }
  2226. pStart++;
  2227. // have pEnd points at the CN=FILE REPLICATION SERVICE
  2228. TCHAR *pEnd = _tcsstr(pszReplicaSetDN, CN_SEARCH_UPR_DFSVOL_FRS_SYS);
  2229. if (!pEnd)
  2230. {
  2231. hr = E_INVALIDARG;
  2232. break;
  2233. }
  2234. pEnd += lstrlen(CN_SEARCH_UPR_DFSVOL_FRS_SYS) - lstrlen(CN_SEARCH_UPR_FRS_SYS) + 1;
  2235. //
  2236. // calculate
  2237. //
  2238. lenPrefix = (int)((BYTE*)pEnd - (BYTE*)pStart) / sizeof(TCHAR);
  2239. } while (0);
  2240. if (SUCCEEDED(hr))
  2241. {
  2242. // forcibly blow away the replicaset object
  2243. hr = DeleteDSObject(i_pldap, i_bstrReplicaSetDN, true);
  2244. if (SUCCEEDED(hr))
  2245. {
  2246. // delete replicasettings objects if empty
  2247. hr = DeleteDSObjectsIfEmpty(
  2248. i_pldap,
  2249. pStart,
  2250. lenPrefix
  2251. );
  2252. }
  2253. }
  2254. free(pszReplicaSetDN);
  2255. return hr;
  2256. }
  2257. HRESULT DeleteNtfrsSubscriberObjectAndContainers(
  2258. IN PLDAP i_pldap,
  2259. IN BSTR i_bstrSubscriberDN,
  2260. IN BSTR i_bstrComputerDN
  2261. )
  2262. {
  2263. RETURN_INVALIDARG_IF_NULL(i_pldap);
  2264. RETURN_INVALIDARG_IF_NULL(i_bstrSubscriberDN);
  2265. RETURN_INVALIDARG_IF_NULL(i_bstrComputerDN);
  2266. //
  2267. // The first CN= clause is a nTFRSSubscriber object.
  2268. // The clauses from the 1st to the CN=<computer> clause should
  2269. // be deleted if empty
  2270. //
  2271. //
  2272. // calculate
  2273. //
  2274. int lenPrefix = lstrlen(i_bstrSubscriberDN) - lstrlen(i_bstrComputerDN);
  2275. HRESULT hr = DeleteDSObjectsIfEmpty(
  2276. i_pldap,
  2277. i_bstrSubscriberDN,
  2278. lenPrefix
  2279. );
  2280. return hr;
  2281. }
  2282. HRESULT DeleteDSObjectsIfEmpty(
  2283. IN PLDAP i_pldap,
  2284. IN LPCTSTR i_lpszDN,
  2285. IN int i_nPrefixLength
  2286. )
  2287. {
  2288. RETURN_INVALIDARG_IF_NULL(i_pldap);
  2289. RETURN_INVALIDARG_IF_NULL(i_lpszDN);
  2290. RETURN_INVALIDARG_IF_NULL(i_nPrefixLength);
  2291. HRESULT hr = S_OK;
  2292. TCHAR *p = (PTSTR)i_lpszDN;
  2293. while (p < i_lpszDN + i_nPrefixLength)
  2294. {
  2295. hr = DeleteDSObject(i_pldap, p, false);
  2296. BREAK_IF_FAILED(hr);
  2297. p = _tcsstr(p, _T(",CN="));
  2298. if (!p)
  2299. break;
  2300. p++;
  2301. }
  2302. return hr;
  2303. }
  2304. HRESULT SetConnectionSchedule(
  2305. IN PLDAP i_pldap,
  2306. IN BSTR i_bstrConnectionDN,
  2307. IN SCHEDULE* i_pSchedule)
  2308. {
  2309. RETURN_INVALIDARG_IF_NULL(i_pldap);
  2310. RETURN_INVALIDARG_IF_NULL(i_bstrConnectionDN);
  2311. RETURN_INVALIDARG_IF_NULL(i_pSchedule);
  2312. //
  2313. // set attribute schedule of this nTDSConnection object
  2314. //
  2315. LDAP_ATTR_VALUE pAttrVals[1];
  2316. pAttrVals[0].bstrAttribute = ATTR_NTDS_CONNECTION_SCHEDULE;
  2317. pAttrVals[0].vpValue = (void *)i_pSchedule;
  2318. pAttrVals[0].ulLength = i_pSchedule->Size;
  2319. pAttrVals[0].bBerValue = true;
  2320. return ::ModifyValues(i_pldap, i_bstrConnectionDN, 1, pAttrVals);
  2321. }
  2322. HRESULT UuidToStructuredString(
  2323. UUID* i_pUuid,
  2324. BSTR* o_pbstr
  2325. )
  2326. {
  2327. if (!i_pUuid || !o_pbstr)
  2328. return E_INVALIDARG;
  2329. TCHAR szString[40];
  2330. _stprintf( szString,
  2331. _T("{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}"),
  2332. i_pUuid->Data1,
  2333. i_pUuid->Data2,
  2334. i_pUuid->Data3,
  2335. i_pUuid->Data4[0],
  2336. i_pUuid->Data4[1],
  2337. i_pUuid->Data4[2],
  2338. i_pUuid->Data4[3],
  2339. i_pUuid->Data4[4],
  2340. i_pUuid->Data4[5],
  2341. i_pUuid->Data4[6],
  2342. i_pUuid->Data4[7] );
  2343. *o_pbstr = SysAllocString(szString);
  2344. if (!*o_pbstr)
  2345. return E_OUTOFMEMORY;
  2346. return S_OK;
  2347. }
  2348. HRESULT ScheduleToVariant(
  2349. IN SCHEDULE* i_pSchedule,
  2350. OUT VARIANT* o_pVar)
  2351. {
  2352. RETURN_INVALIDARG_IF_NULL(i_pSchedule);
  2353. RETURN_INVALIDARG_IF_NULL(o_pVar);
  2354. VariantInit(o_pVar);
  2355. o_pVar->vt = VT_ARRAY | VT_VARIANT;
  2356. o_pVar->parray = NULL;
  2357. int nItems = i_pSchedule->Size;
  2358. SAFEARRAYBOUND bounds = {nItems, 0};
  2359. SAFEARRAY* psa = SafeArrayCreate(VT_VARIANT, 1, &bounds);
  2360. RETURN_OUTOFMEMORY_IF_NULL(psa);
  2361. VARIANT* varArray;
  2362. SafeArrayAccessData(psa, (void**)&varArray);
  2363. for (int i = 0; i < nItems; i++)
  2364. {
  2365. varArray[i].vt = VT_UI1;
  2366. varArray[i].cVal = *((BYTE *)i_pSchedule + i);
  2367. }
  2368. SafeArrayUnaccessData(psa);
  2369. o_pVar->parray = psa;
  2370. return S_OK;
  2371. }
  2372. HRESULT VariantToSchedule(
  2373. IN VARIANT* i_pVar,
  2374. OUT PSCHEDULE* o_ppSchedule // freed by caller
  2375. )
  2376. {
  2377. RETURN_INVALIDARG_IF_NULL(i_pVar);
  2378. RETURN_INVALIDARG_IF_NULL(o_ppSchedule);
  2379. HRESULT hr = S_OK;
  2380. if (V_VT(i_pVar) != (VT_ARRAY | VT_VARIANT))
  2381. return E_INVALIDARG;
  2382. SAFEARRAY *psa = V_ARRAY(i_pVar);
  2383. long lLowerBound = 0;
  2384. long lUpperBound = 0;
  2385. long lCount = 0;
  2386. SafeArrayGetLBound(psa, 1, &lLowerBound );
  2387. SafeArrayGetUBound(psa, 1, &lUpperBound );
  2388. lCount = lUpperBound - lLowerBound + 1;
  2389. BYTE *pSchedule = (BYTE *)calloc(lCount, 1);
  2390. RETURN_OUTOFMEMORY_IF_NULL(pSchedule);
  2391. VARIANT HUGEP *pArray;
  2392. SafeArrayAccessData(psa, (void HUGEP **) &pArray);
  2393. for (int i = 0; i < lCount; i++)
  2394. {
  2395. if (VT_UI1 != pArray[i].vt)
  2396. {
  2397. hr = E_INVALIDARG;
  2398. break;
  2399. }
  2400. pSchedule[i] = pArray[i].cVal;
  2401. }
  2402. SafeArrayUnaccessData(psa);
  2403. if (FAILED(hr))
  2404. free(pSchedule);
  2405. else
  2406. *o_ppSchedule = (SCHEDULE *)pSchedule;
  2407. return hr;
  2408. }
  2409. HRESULT CompareSchedules(
  2410. IN SCHEDULE* i_pSchedule1,
  2411. IN SCHEDULE* i_pSchedule2
  2412. )
  2413. {
  2414. if (!i_pSchedule1 && !i_pSchedule2)
  2415. return S_OK;
  2416. else if (!i_pSchedule1 || !i_pSchedule2)
  2417. return S_FALSE;
  2418. else if (i_pSchedule1->Size != i_pSchedule2->Size)
  2419. return S_FALSE;
  2420. HRESULT hr = S_OK;
  2421. for (ULONG i = 0; i < i_pSchedule1->Size; i++)
  2422. {
  2423. if (*((BYTE *)i_pSchedule1 + i) != *((BYTE *)i_pSchedule2 + i))
  2424. {
  2425. hr = S_FALSE;
  2426. break;
  2427. }
  2428. }
  2429. return hr;
  2430. }
  2431. HRESULT CopySchedule(
  2432. IN SCHEDULE* i_pSrcSchedule,
  2433. OUT PSCHEDULE* o_ppDstSchedule
  2434. )
  2435. {
  2436. RETURN_INVALIDARG_IF_NULL(i_pSrcSchedule);
  2437. RETURN_INVALIDARG_IF_NULL(o_ppDstSchedule);
  2438. *o_ppDstSchedule = (SCHEDULE *)calloc(i_pSrcSchedule->Size, 1);
  2439. RETURN_OUTOFMEMORY_IF_NULL(*o_ppDstSchedule);
  2440. memcpy(*o_ppDstSchedule, i_pSrcSchedule, i_pSrcSchedule->Size);
  2441. return S_OK;
  2442. }
  2443. HRESULT GetDefaultSchedule(
  2444. OUT PSCHEDULE* o_ppSchedule
  2445. )
  2446. {
  2447. RETURN_INVALIDARG_IF_NULL(o_ppSchedule);
  2448. SCHEDULE* pSchedule = (SCHEDULE *)calloc(20 + SCHEDULE_DATA_ENTRIES, 1);
  2449. RETURN_OUTOFMEMORY_IF_NULL(pSchedule);
  2450. pSchedule->Size = 20 + SCHEDULE_DATA_ENTRIES;
  2451. pSchedule->Bandwidth = 0; // not used
  2452. pSchedule->NumberOfSchedules = 1;
  2453. pSchedule->Schedules->Type = SCHEDULE_INTERVAL;
  2454. pSchedule->Schedules->Offset = 20;
  2455. memset((BYTE *)pSchedule + 20, 1, SCHEDULE_DATA_ENTRIES);
  2456. *o_ppSchedule = pSchedule;
  2457. return S_OK;
  2458. }
  2459. //
  2460. // S_OK: Whistler version
  2461. // S_FALSE: Windows2000 version
  2462. // others: error occurred
  2463. //
  2464. HRESULT GetSchemaVersion(IN PLDAP i_pldap)
  2465. {
  2466. RETURN_INVALIDARG_IF_NULL(i_pldap);
  2467. LDAP_ATTR_VALUE pAttributes[1];
  2468. pAttributes[0].bstrAttribute = ATTR_SCHEMANAMINGCONTEXT;
  2469. pAttributes[0].bBerValue = false;
  2470. PLDAP_ATTR_VALUE pDNName[1] = {0};
  2471. HRESULT hr = GetValues( i_pldap,
  2472. _T(""), // LDAP Root.
  2473. OBJCLASS_SF_ALL, // All Objects
  2474. LDAP_SCOPE_BASE,
  2475. 1, // Only 1 attribute
  2476. pAttributes, // schemaNamingContext Attribute.
  2477. pDNName // List of all values at Root for schemaNamingContext.
  2478. );
  2479. if (FAILED(hr))
  2480. return(hr);
  2481. if (!(pDNName[0]))
  2482. return S_FALSE;
  2483. if (!(pDNName[0]->vpValue) || !*((LPTSTR)pDNName[0]->vpValue))
  2484. {
  2485. FreeAttrValList(pDNName[0]);
  2486. return S_FALSE;
  2487. }
  2488. CComBSTR bstrSchemaNamingContext = (LPTSTR)pDNName[0]->vpValue;
  2489. FreeAttrValList(pDNName[0]);
  2490. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrSchemaNamingContext);
  2491. CComBSTR bstrReplicaSetSchemaDN = DN_PREFIX_SCHEMA_REPLICASET;
  2492. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrReplicaSetSchemaDN);
  2493. bstrReplicaSetSchemaDN += bstrSchemaNamingContext;
  2494. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrReplicaSetSchemaDN);
  2495. BOOL bFound = FALSE;
  2496. PCTSTR ppszAttributes[] = {ATTR_SYSTEMMAYCONTAIN, 0};
  2497. LListElem* pElem = NULL;
  2498. hr = GetValuesEx(
  2499. i_pldap,
  2500. bstrReplicaSetSchemaDN,
  2501. LDAP_SCOPE_BASE,
  2502. OBJCLASS_SF_CLASSSCHEMA,
  2503. ppszAttributes,
  2504. &pElem);
  2505. if (SUCCEEDED(hr) && pElem && pElem->pppszAttrValues)
  2506. {
  2507. PTSTR* ppszValues = *(pElem->pppszAttrValues);
  2508. if (ppszValues)
  2509. {
  2510. while (*ppszValues)
  2511. {
  2512. if (!lstrcmpi(ATTR_FRS_REPSET_TOPOLOGYPREF, *ppszValues))
  2513. {
  2514. bFound = TRUE;
  2515. break;
  2516. }
  2517. ppszValues++;
  2518. }
  2519. }
  2520. FreeLListElem(pElem);
  2521. }
  2522. RETURN_IF_FAILED(hr);
  2523. return (bFound ? S_OK : S_FALSE);
  2524. }
  2525. //
  2526. // S_OK: Whistler version
  2527. // S_FALSE: Windows2000 version
  2528. // others: error occurred
  2529. //
  2530. HRESULT GetSchemaVersionEx(
  2531. IN BSTR i_bstrName,
  2532. IN BOOL i_bServer // =TRUE if i_bstrName is a server, FALSE if i_bstrName is a domain
  2533. )
  2534. {
  2535. HRESULT hr = S_OK;
  2536. PTSTR pszDomain = NULL;
  2537. do {
  2538. CComBSTR bstrDomain;
  2539. if (i_bServer)
  2540. {
  2541. hr = GetServerInfo(i_bstrName, &bstrDomain);
  2542. if (S_OK != hr)
  2543. break;
  2544. pszDomain = bstrDomain;
  2545. } else
  2546. {
  2547. pszDomain = i_bstrName;
  2548. }
  2549. PLDAP pldap = NULL;
  2550. hr = ConnectToDS(pszDomain, &pldap, NULL);
  2551. if (SUCCEEDED(hr))
  2552. {
  2553. hr = GetSchemaVersion(pldap);
  2554. CloseConnectionToDS(pldap);
  2555. }
  2556. } while (0);
  2557. return hr;
  2558. }
  2559. //
  2560. // This function doesn't refetch DC in case of LDAP_SERVER_DOWN
  2561. //
  2562. HRESULT LdapConnectToDC(IN LPCTSTR i_pszDC, OUT PLDAP* o_ppldap)
  2563. {
  2564. if (!i_pszDC || !*i_pszDC || !o_ppldap)
  2565. return E_INVALIDARG;
  2566. *o_ppldap = NULL;
  2567. PLDAP pldap = ldap_init((LPTSTR)i_pszDC, LDAP_PORT);
  2568. if (!pldap)
  2569. return HRESULT_FROM_WIN32(GetLastError());
  2570. //
  2571. // Making ldap_open/ldap_connect with a server name without first setting
  2572. // LDAP_OPT_AREC_EXCLUSIVE (for ldap interfaces) or
  2573. // ADS_SERVER_BIND (for ADSI interfaces) will result in bogus DNS queries
  2574. // consuming bandwidth and potentially bringing up remote links that are
  2575. // costly or demand dial.
  2576. //
  2577. // ignore the return of ldap_set_option
  2578. ldap_set_option(pldap, LDAP_OPT_AREC_EXCLUSIVE, LDAP_OPT_ON);
  2579. ULONG ulRet = ldap_connect(pldap, NULL); // NULL for the default timeout
  2580. if (LDAP_SUCCESS != ulRet)
  2581. {
  2582. ldap_unbind(pldap);
  2583. return HRESULT_FROM_WIN32(LdapMapErrorToWin32(ulRet));
  2584. }
  2585. *o_ppldap = pldap;
  2586. return S_OK;
  2587. }
  2588. HRESULT
  2589. GetErrorMessage(
  2590. IN DWORD i_dwError,
  2591. OUT BSTR* o_pbstrErrorMsg
  2592. )
  2593. {
  2594. if (0 == i_dwError || !o_pbstrErrorMsg)
  2595. return E_INVALIDARG;
  2596. HRESULT hr = S_OK;
  2597. LPTSTR lpBuffer = NULL;
  2598. DWORD dwRet = ::FormatMessage(
  2599. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  2600. NULL, i_dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  2601. (LPTSTR)&lpBuffer, 0, NULL);
  2602. if (0 == dwRet)
  2603. {
  2604. // if no message is found, GetLastError will return ERROR_MR_MID_NOT_FOUND
  2605. hr = HRESULT_FROM_WIN32(GetLastError());
  2606. if (HRESULT_FROM_WIN32(ERROR_MR_MID_NOT_FOUND) == hr ||
  2607. 0x80070000 == (i_dwError & 0xffff0000) ||
  2608. 0 == (i_dwError & 0xffff0000) )
  2609. { // Try locating the message from NetMsg.dll.
  2610. hr = S_OK;
  2611. DWORD dwNetError = i_dwError & 0x0000ffff;
  2612. HINSTANCE hLib = LoadLibrary(_T("netmsg.dll"));
  2613. if (!hLib)
  2614. hr = HRESULT_FROM_WIN32(GetLastError());
  2615. else
  2616. {
  2617. dwRet = ::FormatMessage(
  2618. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
  2619. hLib, dwNetError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  2620. (LPTSTR)&lpBuffer, 0, NULL);
  2621. if (0 == dwRet)
  2622. hr = HRESULT_FROM_WIN32(GetLastError());
  2623. FreeLibrary(hLib);
  2624. }
  2625. }
  2626. }
  2627. if (SUCCEEDED(hr))
  2628. {
  2629. *o_pbstrErrorMsg = SysAllocString(lpBuffer);
  2630. LocalFree(lpBuffer);
  2631. }
  2632. else
  2633. {
  2634. // we failed to retrieve the error message from system/netmsg.dll,
  2635. // report the error code directly to user
  2636. hr = S_OK;
  2637. TCHAR szString[32];
  2638. _stprintf(szString, _T("0x%x"), i_dwError);
  2639. *o_pbstrErrorMsg = SysAllocString(szString);
  2640. }
  2641. if (!*o_pbstrErrorMsg)
  2642. hr = E_OUTOFMEMORY;
  2643. return hr;
  2644. }
  2645. HRESULT
  2646. FormatMessageString(
  2647. OUT BSTR *o_pbstrMsg,
  2648. IN DWORD dwErr,
  2649. IN UINT iStringId, // OPTIONAL: String resource Id
  2650. ...) // Optional arguments
  2651. {
  2652. _ASSERT(dwErr != 0 || iStringId != 0); // One of the parameter must be non-zero
  2653. HRESULT hr = S_OK;
  2654. CComBSTR bstrErrorMsg, bstrMsg;
  2655. if (dwErr)
  2656. hr = GetErrorMessage(dwErr, &bstrErrorMsg);
  2657. if (SUCCEEDED(hr))
  2658. {
  2659. if (iStringId == 0)
  2660. {
  2661. bstrMsg = bstrErrorMsg;
  2662. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrMsg);
  2663. }
  2664. else
  2665. {
  2666. TCHAR szString[1024];
  2667. ::LoadString(_Module.GetModuleInstance(), iStringId,
  2668. szString, sizeof(szString)/sizeof(TCHAR));
  2669. va_list arglist;
  2670. va_start(arglist, iStringId);
  2671. LPTSTR lpBuffer = NULL;
  2672. DWORD dwRet = ::FormatMessage(
  2673. FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
  2674. szString,
  2675. 0, // dwMessageId
  2676. 0, // dwLanguageId, ignored
  2677. (LPTSTR)&lpBuffer,
  2678. 0, // nSize
  2679. &arglist);
  2680. va_end(arglist);
  2681. if (dwRet == 0)
  2682. {
  2683. hr = HRESULT_FROM_WIN32(GetLastError());
  2684. }
  2685. else
  2686. {
  2687. bstrMsg = lpBuffer;
  2688. LocalFree(lpBuffer);
  2689. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrMsg);
  2690. if (dwErr)
  2691. {
  2692. bstrMsg += bstrErrorMsg;
  2693. RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrMsg);
  2694. }
  2695. }
  2696. }
  2697. }
  2698. if (SUCCEEDED(hr))
  2699. *o_pbstrMsg = bstrMsg.Detach();
  2700. return hr;
  2701. }
  2702. //
  2703. // This function will DsBind to a valid DC (DC is re-fetched if down)
  2704. //
  2705. HRESULT DsBindToDS(BSTR i_bstrDomain, BSTR *o_pbstrDC, HANDLE *o_phDS)
  2706. {
  2707. RETURN_INVALIDARG_IF_NULL(o_pbstrDC);
  2708. RETURN_INVALIDARG_IF_NULL(o_phDS);
  2709. HRESULT hr = S_OK;
  2710. BOOL bRetry = FALSE;
  2711. HANDLE hDS = NULL;
  2712. DWORD dwErr = ERROR_SUCCESS;
  2713. CComBSTR bstrDCName;
  2714. CComBSTR bstrDomainDnsName;
  2715. do {
  2716. #ifdef DEBUG
  2717. SYSTEMTIME time0 = {0};
  2718. GetSystemTime(&time0);
  2719. #endif // DEBUG
  2720. PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
  2721. if (bRetry)
  2722. dwErr = DsGetDcName(NULL, i_bstrDomain, NULL, NULL,
  2723. DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME | DS_FORCE_REDISCOVERY, &pDCInfo);
  2724. else
  2725. dwErr = DsGetDcName(NULL, i_bstrDomain, NULL, NULL,
  2726. DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME, &pDCInfo);
  2727. #ifdef DEBUG
  2728. SYSTEMTIME time1 = {0};
  2729. GetSystemTime(&time1);
  2730. PrintTimeDelta(_T("DsBindToDS-DsGetDcName"), &time0, &time1);
  2731. #endif // DEBUG
  2732. if (ERROR_SUCCESS != dwErr)
  2733. return HRESULT_FROM_WIN32(dwErr);
  2734. if ( !mylstrncmpi(pDCInfo->DomainControllerName, _T("\\\\"), 2) )
  2735. bstrDCName = pDCInfo->DomainControllerName + 2;
  2736. else
  2737. bstrDCName = pDCInfo->DomainControllerName;
  2738. // remove the ending dot
  2739. int len = _tcslen(pDCInfo->DomainName);
  2740. if ( _T('.') == *(pDCInfo->DomainName + len - 1) )
  2741. *(pDCInfo->DomainName + len - 1) = _T('\0');
  2742. bstrDomainDnsName = pDCInfo->DomainName;
  2743. NetApiBufferFree(pDCInfo);
  2744. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDCName, &hr);
  2745. BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDomainDnsName, &hr);
  2746. dwErr = DsBind(bstrDCName, bstrDomainDnsName, &hDS);
  2747. hr = HRESULT_FROM_WIN32(dwErr);
  2748. #ifdef DEBUG
  2749. SYSTEMTIME time2 = {0};
  2750. GetSystemTime(&time2);
  2751. PrintTimeDelta(_T("DsBindToDS-DsBind"), &time1, &time2);
  2752. #endif // DEBUG
  2753. if ((RPC_S_SERVER_UNAVAILABLE == dwErr || RPC_S_CALL_FAILED == dwErr) && !bRetry)
  2754. {
  2755. bRetry = TRUE; // only retry once
  2756. } else
  2757. {
  2758. if (SUCCEEDED(hr))
  2759. {
  2760. *o_phDS = hDS;
  2761. *o_pbstrDC = bstrDCName.Copy();
  2762. if (!*o_pbstrDC)
  2763. {
  2764. hr = E_OUTOFMEMORY;
  2765. DsUnBind(&hDS);
  2766. *o_phDS = NULL;
  2767. }
  2768. }
  2769. break;
  2770. }
  2771. } while (1);
  2772. return hr;
  2773. }
  2774. #ifdef DEBUG
  2775. void PrintTimeDelta(LPCTSTR pszMsg, SYSTEMTIME* pt0, SYSTEMTIME* pt1)
  2776. {
  2777. if (!pt0 || !pt1)
  2778. return;
  2779. dfsDebugOut((_T("%s took %d milliseconds.\n"), (pszMsg ? pszMsg : _T("")),
  2780. ((pt1->wMinute - pt0->wMinute) * 60 +
  2781. (pt1->wSecond - pt0->wSecond)) * 1000 +
  2782. (pt1->wMilliseconds - pt0->wMilliseconds)
  2783. ));
  2784. }
  2785. #endif // DEBUG