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

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