|
|
/*++
Module Name:
LDAPUtils.h
Abstract: This is the header file for the LDAP utility functions.
*/ //--------------------------------------------------------------------
#include <stdafx.h>
#include "LDAPUtils.h"
#include <dsgetdc.h>
#include <stdio.h>
#include <ntdsapi.h>
#include <lm.h>
#include <ntldap.h>
#include <winber.h>
#include "dfsenums.h"
#include "netutils.h"
//----------------------------------------------------------------------------------
HRESULT FreeLDAPNamesList ( IN PLDAPNAME i_pLDAPNames // pointer to list to be freed.
) { /*++
Routine Description:
Helper funciton used to free the NETNAME linked list retrurned by LDAP helper functions.
Arguments:
i_pLDAPNames - Pointer to the first node in the list to be freed.
Return value:
S_OK, on Success. E_POINTER, Illegal pointer was passed.
--*/
PLDAPNAME pNodeToFree = NULL; try { while (NULL != i_pLDAPNames) { pNodeToFree = i_pLDAPNames; i_pLDAPNames = i_pLDAPNames->Next; delete pNodeToFree; } } // try
catch (...) { return E_POINTER; } return S_OK; } // HRESULT FreeDomainList
HRESULT FreeAttrValList ( IN PLDAP_ATTR_VALUE i_pAttrVals ) { /*++
Routine Description:
Helper funciton used to free the LDAP_ATTR_VALUE linked list retrurned by LDAP helper functions.
Arguments:
i_pLDAPNames - Pointer to the first node in the list to be freed.
Return value:
S_OK, on Success. E_POINTER, Illegal pointer was passed.
--*/
PLDAP_ATTR_VALUE pNodeToFree = NULL; try { while (NULL != i_pAttrVals) { pNodeToFree = i_pAttrVals; i_pAttrVals = i_pAttrVals->Next; if (NULL != pNodeToFree->vpValue) { free(pNodeToFree->vpValue); } delete pNodeToFree; } } // try
catch (...) { return E_POINTER; } return S_OK; }
//----------------------------------------------------------------------------------
HRESULT ConnectToDS ( IN PCTSTR i_lpszDomainName, // DNS or non DNS format.
OUT PLDAP *o_ppldap, OUT BSTR* o_pbstrDC // = NULL
) { /*++
Routine Description:
Opens an LDAP connection to a valid DC (DC re-fetched if down).
Arguments:
i_lpszDomainName - Name of the domain, DNS or Non Dns format. o_ppldap - Pointer to LDAP handle in returned here. NULL on failure.
Return value:
S_OK, on Success. E_INVALIDARG, Illegal pointer was passed. E_FAIL, if connection could not be established. Any Other error code returned by ldap or Net apis.
--*/
RETURN_INVALIDARG_IF_NULL(o_ppldap);
*o_ppldap = NULL;
//
// open a ldap connection to a valid DC
//
HRESULT hr = S_OK; DWORD dwErr = 0; CComBSTR bstrDCName; PLDAP pldap = NULL; BOOL bRetry = FALSE; do { #ifdef DEBUG
SYSTEMTIME time0 = {0}; GetSystemTime(&time0); #endif // DEBUG
//
// pick a DC
//
PDOMAIN_CONTROLLER_INFO pDCInfo = NULL; if (bRetry) dwErr = DsGetDcName(NULL, i_lpszDomainName, NULL, NULL, DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME | DS_FORCE_REDISCOVERY, &pDCInfo); else dwErr = DsGetDcName(NULL, i_lpszDomainName, NULL, NULL, DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME, &pDCInfo);
#ifdef DEBUG
SYSTEMTIME time1 = {0}; GetSystemTime(&time1); PrintTimeDelta(_T("ConnectToDS-DsGetDcName"), &time0, &time1); #endif // DEBUG
if (ERROR_SUCCESS != dwErr) { hr = HRESULT_FROM_WIN32(dwErr); break; }
if ( !mylstrncmpi(pDCInfo->DomainControllerName, _T("\\\\"), 2) ) bstrDCName = pDCInfo->DomainControllerName + 2; else bstrDCName = pDCInfo->DomainControllerName; NetApiBufferFree(pDCInfo);
BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDCName, &hr);
//
// make ldap connection to this DC
//
pldap = ldap_init(bstrDCName, LDAP_PORT); if (!pldap) { hr = HRESULT_FROM_WIN32(GetLastError()); break; }
//
// Making ldap_open/ldap_connect with a server name without first setting
// LDAP_OPT_AREC_EXCLUSIVE (for ldap interfaces) or
// ADS_SERVER_BIND (for ADSI interfaces) will result in bogus DNS queries
// consuming bandwidth and potentially bringing up remote links that are
// costly or demand dial.
//
// ignore the return of ldap_set_option
ldap_set_option(pldap, LDAP_OPT_AREC_EXCLUSIVE, LDAP_OPT_ON);
ULONG ulRet = ldap_connect(pldap, NULL); // NULL for the default timeout
#ifdef DEBUG
SYSTEMTIME time2 = {0}; GetSystemTime(&time2); PrintTimeDelta(_T("ConnectToDS-ldap_connect"), &time1, &time2); #endif // DEBUG
if (LDAP_SERVER_DOWN == ulRet && !bRetry) { ldap_unbind(pldap); bRetry = TRUE; // retry once to pick another DC
} else { if (LDAP_SUCCESS != ulRet) { ldap_unbind(pldap); hr = HRESULT_FROM_WIN32(LdapMapErrorToWin32(ulRet)); }
break; } } while (1);
RETURN_IF_FAILED(hr);
//
// bind to this ldap connection
//
dwErr = ldap_bind_s(pldap, NULL, NULL, LDAP_AUTH_NEGOTIATE); if (LDAP_SUCCESS != dwErr) { dwErr = LdapMapErrorToWin32(dwErr); DebugOutLDAPError(pldap, dwErr, _T("ldap_bind_s")); ldap_unbind(pldap); hr = HRESULT_FROM_WIN32(dwErr); } else { *o_ppldap = pldap;
if (o_pbstrDC) { *o_pbstrDC = bstrDCName.Copy(); if (!*o_pbstrDC) { ldap_unbind(pldap); *o_ppldap = NULL; hr = E_OUTOFMEMORY; } } }
return hr; }
HRESULT CloseConnectionToDS ( IN PLDAP i_pldap ) { /*++
Routine Description:
Closes an open LDAP connection.
Arguments:
i_pldap - Open LDAP connection handle.
Return value:
S_OK, on Success. E_FAIL, if connection could not be established. Any Other error code returned by ldap or Net apis.
--*/
if (NULL == i_pldap) { return(E_INVALIDARG); }
DWORD dwErr = ldap_unbind(i_pldap); if (LDAP_SUCCESS != dwErr) { dwErr = LdapMapErrorToWin32(dwErr); return(HRESULT_FROM_WIN32(dwErr)); } else { return(S_OK); } }
// Gets Values for an attribute from an LDAP Object.
HRESULT GetValues ( IN PLDAP i_pldap, IN PCTSTR i_lpszBase, IN PCTSTR i_lpszSearchFilter, IN ULONG i_ulScope, IN ULONG i_ulAttrCount, IN LDAP_ATTR_VALUE i_pAttributes[], OUT PLDAP_ATTR_VALUE o_ppValues[] ) {
/*++
Routine Description:
Gets Values for an attribute from an LDAP Object given Object class. Object Class can be "*" etc.
Arguments:
i_pldap - An open, bound ldap port. i_lpszBase - The base path of a DS object, can be "". i_lpszSearchFilter - LDAP Search Filter. i_ulScope - The search scope. i_ulAttrCount - Count of attributes passed in i_lpszAttributes. i_pAttributes - Attributes for which to get values. The bBerValue has to be set. o_ppValues - Array of pointers, size = i_ulAttrCount, each pointer to a list of values corresponding to the respective attribute in i_pAttributes. The bstrAttribute is not set for values. For BerVal types the bBerValue and ulLength are set.
Return Value:
S_OK, on Success. E_INVALIDARG, Illegal pointer was passed. E_OUTOFMEMORY on memory allocation failure. E_FAIL, if connection could not be established. Any Other error code returned by ldap or Net apis. --*/ DWORD dwErr; LPTSTR lpszCurrentAttr = NULL; BerElement *BerElm = NULL; PLDAPMessage pMsg = NULL; PLDAPMessage pEntry = NULL; HRESULT hr = S_OK;
if (!i_pldap || !i_lpszBase || !i_lpszSearchFilter || (i_ulAttrCount < 1) || !i_pAttributes || !o_ppValues) { return(E_INVALIDARG); }
// Prepare the list of attributes to be sent to ldap_search.
LPTSTR *lpszAttributes = new LPTSTR[i_ulAttrCount + 1]; if (!lpszAttributes) return E_OUTOFMEMORY;
lpszAttributes[i_ulAttrCount] = NULL; for (ULONG i = 0; i < i_ulAttrCount; i++) lpszAttributes[i] = i_pAttributes[i].bstrAttribute;
// Execute the search.
dwErr = ldap_search_s (i_pldap, (PTSTR)i_lpszBase, i_ulScope, (PTSTR)i_lpszSearchFilter, lpszAttributes, 0, &pMsg ); delete [] lpszAttributes;
if (LDAP_SUCCESS != dwErr) { dwErr = LdapMapErrorToWin32(dwErr); DebugOutLDAPError(i_pldap, dwErr, _T("ldap_search_s")); hr = HRESULT_FROM_WIN32(dwErr); } else { LPTSTR lpszCurrentAttr = ldap_first_attribute(i_pldap, pMsg, &BerElm); if (!lpszCurrentAttr) { dfsDebugOut((_T("GetValues of %s returned NULL attributes.\n"), i_lpszBase)); hr = HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED); } else { // For each attribute, build a list of values
// by scanning each entry for the given attribute.
for (i = 0; i < i_ulAttrCount && SUCCEEDED(hr); i++) { PLDAP_ATTR_VALUE *ppCurrent = &(o_ppValues[i]);
// Scan each attribute of the entry for an exact match
for(lpszCurrentAttr = ldap_first_attribute(i_pldap, pMsg, &BerElm); lpszCurrentAttr != NULL && SUCCEEDED(hr); lpszCurrentAttr = ldap_next_attribute(i_pldap, pMsg, BerElm)) { // Is there a match?
if (0 == lstrcmpi(i_pAttributes[i].bstrAttribute, lpszCurrentAttr)) { // Add the value to the linked list for this attribute.
LPTSTR *lpszCurrentValue = NULL, *templpszValue = NULL; LDAP_BERVAL **ppBerVal = NULL, **tempBerVal = NULL;
if (i_pAttributes[i].bBerValue) { tempBerVal = ppBerVal = ldap_get_values_len(i_pldap, pMsg, lpszCurrentAttr); while(*ppBerVal && SUCCEEDED(hr)) { *ppCurrent = new LDAP_ATTR_VALUE; if (!*ppCurrent) { hr = E_OUTOFMEMORY; } else { (*ppCurrent)->ulLength = (*ppBerVal)->bv_len; (*ppCurrent)->bBerValue = true; (*ppCurrent)->vpValue = malloc((*ppBerVal)->bv_len); if (!(*ppCurrent)->vpValue) { delete *ppCurrent; hr = E_OUTOFMEMORY; } else { memcpy( (*ppCurrent)->vpValue, (void *)(*ppBerVal)->bv_val, (*ppBerVal)->bv_len );
(*ppCurrent)->Next = NULL;
ppBerVal++; ppCurrent = &((*ppCurrent)->Next); } } } // while
if (NULL != tempBerVal) ldap_value_free_len(tempBerVal); } else { templpszValue = lpszCurrentValue = ldap_get_values(i_pldap, pMsg, lpszCurrentAttr); while(*lpszCurrentValue && SUCCEEDED(hr)) { *ppCurrent = new LDAP_ATTR_VALUE; if (NULL == *ppCurrent) { hr = E_OUTOFMEMORY; } else { (*ppCurrent)->bBerValue = false; (*ppCurrent)->vpValue = (void *)_tcsdup(*lpszCurrentValue); (*ppCurrent)->Next = NULL;
if (NULL == (*ppCurrent)->vpValue) { delete *ppCurrent; hr = E_OUTOFMEMORY; } else { lpszCurrentValue++; ppCurrent = &((*ppCurrent)->Next); } } } // while
if (NULL != templpszValue) ldap_value_free(templpszValue); } } } } } }
// free pMsg because ldap_search_s always allocates pMsg
if (pMsg) ldap_msgfree(pMsg);
if (FAILED(hr)) { for (i = 0; i < i_ulAttrCount; i++) FreeAttrValList(o_ppValues[i]); }
return hr; }
void FreeLListElem(LListElem* pElem) { LListElem* pCurElem = NULL; LListElem* pNextElem = pElem;
while (pCurElem = pNextElem) { pNextElem = pCurElem->Next; delete pCurElem; } }
HRESULT GetValuesEx ( IN PLDAP i_pldap, IN PCTSTR i_pszBase, IN ULONG i_ulScope, IN PCTSTR i_pszSearchFilter, IN PCTSTR i_pszAttributes[], OUT LListElem** o_ppElem ) { if (!i_pldap || !i_pszBase || !i_pszSearchFilter || !i_pszAttributes || !o_ppElem) { return(E_INVALIDARG); }
*o_ppElem = NULL;
//
// count number of attributes
//
ULONG ulNumOfAttributes = 0; PTSTR* ppszAttr = (PTSTR *)i_pszAttributes; while (*ppszAttr++) ulNumOfAttributes++; if (!ulNumOfAttributes) return E_INVALIDARG;
HRESULT hr = S_OK; PLDAPMessage pMsg = NULL; DWORD dwErr = ldap_search_s(i_pldap, (PTSTR)i_pszBase, i_ulScope, (PTSTR)i_pszSearchFilter, (PTSTR *)i_pszAttributes, 0, &pMsg );
if (LDAP_SUCCESS != dwErr) { dwErr = LdapMapErrorToWin32(dwErr); DebugOutLDAPError(i_pldap, dwErr, _T("ldap_search_s")); hr = HRESULT_FROM_WIN32(dwErr); } else { PLDAPMessage pMsgEntry = NULL; BerElement* pBerElm = NULL; PTSTR pszCurrentAttr = NULL; LListElem* pHeadElem = NULL; LListElem* pCurElem = NULL;
// Scan each entry to find the value set for the DN attribute.
for(pMsgEntry = ldap_first_entry(i_pldap, pMsg); pMsgEntry; pMsgEntry = ldap_next_entry(i_pldap, pMsgEntry)) { PTSTR** pppszValueArray = (PTSTR **)calloc(ulNumOfAttributes + 1, sizeof(PTSTR **)); BREAK_OUTOFMEMORY_IF_NULL(pppszValueArray, &hr);
// Read each attribute of the entry into the array
for(pszCurrentAttr = ldap_first_attribute(i_pldap, pMsgEntry, &pBerElm); pszCurrentAttr; pszCurrentAttr = ldap_next_attribute(i_pldap, pMsgEntry, pBerElm)) { PTSTR* ppszValues = ldap_get_values(i_pldap, pMsgEntry, pszCurrentAttr);
for (ULONG i = 0; i < ulNumOfAttributes; i++) { if (!lstrcmpi(i_pszAttributes[i], pszCurrentAttr)) { pppszValueArray[i] = ppszValues; break; } } } // end of attribute enumeration
LListElem* pNewElem = new LListElem(pppszValueArray); if (!pNewElem) { free(pppszValueArray); hr = E_OUTOFMEMORY; break; }
if (!pCurElem) { pHeadElem = pCurElem = pNewElem; } else { pCurElem->Next = pNewElem; pCurElem = pNewElem; } } // end of entry enumeration
if (FAILED(hr)) FreeLListElem(pHeadElem); else *o_ppElem = pHeadElem; }
// free pMsg because ldap_search_s always allocates pMsg
if (pMsg) ldap_msgfree(pMsg);
return hr; }
HRESULT GetLDAPRootPath ( IN PLDAP i_pldap, OUT LPTSTR* o_ppszRootPath ) /*++
Routine Description:
Return the DS pathname of the global configuration container. Arguments:
pldap - An open and bound ldap handle. lpszRootPath - The LDAP path us returned here.
Return Value: S_OK on success. E_FAIL on failure. E_OUTOFMEORY if memory allocation fails. E_INVALIDARG if null pointer arguments were passed.
--*/ { LPTSTR *ppszValues = NULL; PLDAP_ATTR_VALUE pDNName[1] = {0}; PLDAP_ATTR_VALUE pCurrent = NULL; if (NULL == i_pldap || NULL == o_ppszRootPath) { return(E_INVALIDARG); }
LPTSTR lpszCoDN = NULL;
*o_ppszRootPath = NULL;
LDAP_ATTR_VALUE pAttributes[1];
pAttributes[0].bstrAttribute = _T("namingContexts"); pAttributes[0].bBerValue = false; HRESULT hr = GetValues( i_pldap, _T(""), // LDAP Root.
OBJCLASS_SF_ALL, // All Objects
LDAP_SCOPE_BASE, 1, // Only 1 attribute
pAttributes, // namingContexts Attribute.
pDNName // List of all values at Root for namingContexts.
);
if (FAILED(hr)) return(hr);
// Find the naming context that begins with CN=Configuration
pCurrent = pDNName[0]; while (pCurrent) { if (0 == mylstrncmpi((LPTSTR)pCurrent->vpValue, _T("DC="), 3)) { *o_ppszRootPath= _tcsdup((LPTSTR)pCurrent->vpValue); hr = (*o_ppszRootPath) ? S_OK : E_OUTOFMEMORY;
break; } pCurrent = pCurrent->Next; } FreeAttrValList(pDNName[0]);
return(hr); }
HRESULT GetChildrenDN ( IN PLDAP i_pldap, IN LPCTSTR i_lpszBase, IN ULONG i_ulScope, IN LPTSTR i_lpszChildObjectClassSF, OUT PLDAPNAME* o_ppDistNames ) /*++
Routine Description: Return the Distinguished Name of all children of a given objectClass as a linked list of LDAPNAME structures. Arguments: pldap - An open and bound ldap handle.
i_lpszBase - The base path of a DS object, can be "".
o_ppDistNames - The linked of child DNs is returned here.
i_lpszChildObjectClassSF - The objectClass of the children to list. E.g fTDfs, User.
Return Value: S_OK on success. E_FAIL on failure. E_OUTOFMEORY if memory allocation fails. E_INVALIDARG if null pointer arguments were passed.
--*/ {
DWORD dwErr; LPTSTR lpszCurrentAttr = NULL; LPTSTR *plpszValues = NULL; BerElement *BerElm = NULL; PLDAPMessage pMsg = NULL; PLDAPMessage pEntry = NULL; PLDAPNAME *ppCurrent; HRESULT hr = S_OK;
if (!i_pldap || !i_lpszBase || !o_ppDistNames || !i_lpszChildObjectClassSF || !*i_lpszChildObjectClassSF) { return(E_INVALIDARG); }
*o_ppDistNames = NULL; ppCurrent = o_ppDistNames;
LPTSTR lpszAttributes[2] = {0,0}; lpszAttributes[0] = _T("distinguishedName");
// Execute the search.
dwErr = ldap_search_s (i_pldap, (LPTSTR)i_lpszBase, i_ulScope, i_lpszChildObjectClassSF, lpszAttributes, 0, &pMsg ); if (LDAP_SUCCESS != dwErr) { dwErr = LdapMapErrorToWin32(dwErr); DebugOutLDAPError(i_pldap, dwErr, _T("ldap_search_s")); hr = HRESULT_FROM_WIN32(dwErr); } else { // Scan each entry to find the value set for the DN attribute.
for(pEntry = ldap_first_entry(i_pldap, pMsg); pEntry != NULL; pEntry = ldap_next_entry(i_pldap, pEntry)) { CComBSTR bstrCN;
// Scan each attribute of the entry for DN
for(lpszCurrentAttr = ldap_first_attribute(i_pldap, pEntry, &BerElm); lpszCurrentAttr != NULL; lpszCurrentAttr = ldap_next_attribute(i_pldap, pEntry, BerElm)) {
plpszValues = ldap_get_values( i_pldap, pEntry, lpszCurrentAttr ); // Is there a match for CN?
if (0 == lstrcmpi(_T("distinguishedName"), lpszCurrentAttr)) { bstrCN = plpszValues[0]; } }
// LDAP object does not have valid fields.
if (!bstrCN) continue;
// Add to list.
*ppCurrent = new LDAPNAME; if (NULL == *ppCurrent) { hr = E_OUTOFMEMORY; break; }
(*ppCurrent)->Next = NULL; (*ppCurrent)->bstrLDAPName = bstrCN.m_str;
if (!(*ppCurrent)->bstrLDAPName) { delete *ppCurrent; *ppCurrent = NULL; hr = E_OUTOFMEMORY; break; }
ppCurrent = &((*ppCurrent)->Next); }
if (NULL == *o_ppDistNames) { hr = E_FAIL; } if (S_OK != hr) { FreeLDAPNamesList(*ppCurrent); *ppCurrent = NULL; hr = E_OUTOFMEMORY; } }
// free pMsg because ldap_search_s always allocates pMsg
if (pMsg) ldap_msgfree(pMsg);
return(hr); }
HRESULT GetConnectionDNs ( IN PLDAP i_pldap, IN LPCTSTR i_lpszBase, IN LPTSTR i_lpszChildObjectClassSF, OUT PLDAPNAME* o_ppDistNames ) {
DWORD dwErr; LPTSTR lpszCurrentAttr = NULL; LPTSTR *plpszValues = NULL; BerElement *BerElm = NULL; PLDAPMessage pMsg = NULL; PLDAPMessage pEntry = NULL; PLDAPNAME *ppCurrent; HRESULT hr = S_OK;
if (!i_pldap || !i_lpszBase || !o_ppDistNames || !i_lpszChildObjectClassSF || !*i_lpszChildObjectClassSF) { return(E_INVALIDARG); }
*o_ppDistNames = NULL; ppCurrent = o_ppDistNames;
LPTSTR lpszAttributes[2] = {0,0}; lpszAttributes[0] = _T("distinguishedName");
// Execute the search.
dwErr = ldap_search_s (i_pldap, (LPTSTR)i_lpszBase, LDAP_SCOPE_ONELEVEL, i_lpszChildObjectClassSF, lpszAttributes, 0, &pMsg ); if (LDAP_SUCCESS != dwErr) { dwErr = LdapMapErrorToWin32(dwErr); DebugOutLDAPError(i_pldap, dwErr, _T("ldap_search_s")); hr = HRESULT_FROM_WIN32(dwErr); } else { // Scan each entry to find the value set for the DN attribute.
for(pEntry = ldap_first_entry(i_pldap, pMsg); pEntry != NULL; pEntry = ldap_next_entry(i_pldap, pEntry)) { CComBSTR bstrCN;
// Scan each attribute of the entry for DN
for(lpszCurrentAttr = ldap_first_attribute(i_pldap, pEntry, &BerElm); lpszCurrentAttr != NULL; lpszCurrentAttr = ldap_next_attribute(i_pldap, pEntry, BerElm)) {
plpszValues = ldap_get_values( i_pldap, pEntry, lpszCurrentAttr ); // Is there a match for CN?
if (0 == lstrcmpi(_T("distinguishedName"), lpszCurrentAttr)) { bstrCN = plpszValues[0]; } }
// LDAP object does not have valid fields.
if (!bstrCN) continue;
// Add to list.
*ppCurrent = new LDAPNAME; if (NULL == *ppCurrent) { hr = E_OUTOFMEMORY; break; }
(*ppCurrent)->Next = NULL; (*ppCurrent)->bstrLDAPName = bstrCN.m_str;
if (!(*ppCurrent)->bstrLDAPName) { delete *ppCurrent; *ppCurrent = NULL; hr = E_OUTOFMEMORY; break; }
ppCurrent = &((*ppCurrent)->Next); }
if (NULL == *o_ppDistNames) { hr = E_FAIL; } if (S_OK != hr) { FreeLDAPNamesList(*ppCurrent); *ppCurrent = NULL; hr = E_OUTOFMEMORY; } }
// free pMsg because ldap_search_s always allocates pMsg
if (pMsg) ldap_msgfree(pMsg);
return(hr); }
HRESULT PrepareLDAPMods ( IN LDAP_ATTR_VALUE i_pAttrValue[], IN LDAP_ENTRY_ACTION i_AddModDel, IN ULONG i_ulCountOfVals, OUT LDAPMod* o_ppModVals[] ) { /*++
Routine Description:
Fills up a LPDAMod pointer array given a array of attribute value pairs. The mod_op field of all LPDAMod structures returned depends on the value of i_AddModDel.
Arguments:
i_pAttrValue - An array of LDAP_ATTR_VALUE structures containing the attribute and name value pairs.
i_AddModDel - One of LDAP_ENTRY_ACTION enum value.
i_ulCountOfVals - The size of i_pAttrValue array (the number of values).
o_ppModVals - Pointer to a pre-allocated (and NULL terminated) array of pointers to LPDAPMod structures. The LPDAMod structures and allocated and returned here. Size of this should be i_ulCountOfVals.
Return value: S_OK, On success E_INVALIDARG, if an invalid (NULL) pointer was passed. E_OUTOEMEMORY, if memory allocation fails. Any other network (ldap) error.
--*/
if (NULL == i_pAttrValue || NULL == o_ppModVals) { return(E_INVALIDARG); }
for (ULONG i = 0, k = 0; k < i_ulCountOfVals; i++, k++) {
//
// have to skip objectClass attribute in case of modify/delete,
// otherwise, ldap_modify_xxx will return LDAP_UNWILLING_TO_PERFORM
//
if (ADD_VALUE != i_AddModDel && !lstrcmpi(i_pAttrValue[k].bstrAttribute, ATTR_OBJCLASS)) { k++; }
o_ppModVals[i] = new LDAPMod; o_ppModVals[i]->mod_type = _tcsdup(i_pAttrValue[k].bstrAttribute); switch (i_AddModDel) { case ADD_VALUE: o_ppModVals[i]->mod_op = LDAP_MOD_ADD; break; case MODIFY_VALUE: o_ppModVals[i]->mod_op = LDAP_MOD_REPLACE; break; case DELETE_VALUE: o_ppModVals[i]->mod_op = LDAP_MOD_DELETE; break; }
// Count the number of values for this attribute.
PLDAP_ATTR_VALUE pAttrVal = &(i_pAttrValue[k]); ULONG ulCountOfVals = 0; while (pAttrVal) { ulCountOfVals++; pAttrVal = pAttrVal->Next; }
pAttrVal = &(i_pAttrValue[k]); ULONG j = 0;
if (i_pAttrValue[k].bBerValue) { PLDAP_BERVAL* ppBerValues = NULL; ppBerValues = new PLDAP_BERVAL[ulCountOfVals + 1]; ppBerValues[ulCountOfVals] = NULL;
while (pAttrVal) { ppBerValues[j] = new LDAP_BERVAL;
if (!pAttrVal->vpValue) { ppBerValues[j]->bv_len = 0; ppBerValues[j]->bv_val = NULL; } else { ppBerValues[j]->bv_len = pAttrVal->ulLength; ppBerValues[j]->bv_val = new char[pAttrVal->ulLength]; memcpy( (void *)ppBerValues[j]->bv_val, pAttrVal->vpValue, pAttrVal->ulLength ); } pAttrVal = pAttrVal->Next; j++; } o_ppModVals[i]->mod_bvalues = ppBerValues; o_ppModVals[i]->mod_op |= LDAP_MOD_BVALUES; } else { LPTSTR* plpszValues = NULL; plpszValues = new LPTSTR[ulCountOfVals + 1]; plpszValues[ulCountOfVals] = NULL;
while (pAttrVal) { if (pAttrVal->vpValue) plpszValues[j] = _tcsdup((LPTSTR)(pAttrVal->vpValue)); else plpszValues[j] = NULL;
pAttrVal = pAttrVal->Next; j++; }
o_ppModVals[i]->mod_values = plpszValues;
} }
return(S_OK); }
HRESULT AddValues ( IN PLDAP i_pldap, IN LPCTSTR i_DN, IN ULONG i_ulCountOfVals, IN LDAP_ATTR_VALUE i_pAttrValue[], IN BSTR i_bstrDC // = NULL
) { /*++
Routine Description: This method add an attribute value (and a new LDAP object if it does not exist) in the DS. The parent of the given DN must exist. This can be used to add a new object and also to add new values for attributes of an existing object in which case the DN must exist.
Arguments: i_pldap - Open LDAP connection context.
i_DN - Distinguished name of the (new) object.
i_pAttrValue - Array of pointers to LDAP_ATTR_VALUE containing attribue and value.
i_ulCountOfVals - The size of i_pAttrValue array (the number of values).
Return value:
S_OK, On success E_INVALIDARG, if an invalid (NULL) pointer was passed. E_OUTOEMEMORY, if memory allocation fails. Any other network (ldap) error. --*/
if (NULL == i_pldap || NULL == i_DN || NULL == i_pAttrValue) { return(E_INVALIDARG); } LDAPMod** ppModVals = NULL; HRESULT hr = S_FALSE;
ppModVals = new LDAPMod*[i_ulCountOfVals + 1]; if (NULL == ppModVals) { return(E_OUTOFMEMORY); }
for (ULONG i = 0; i <= i_ulCountOfVals; i++) { ppModVals[i] = NULL; }
do { hr = PrepareLDAPMods( i_pAttrValue, ADD_VALUE, i_ulCountOfVals, ppModVals );
if (FAILED(hr)) { break; }
DWORD dwStatus = LDAP_SUCCESS;
if (!i_bstrDC) { dwStatus = ldap_add_s( i_pldap, (LPTSTR)i_DN, ppModVals ); } else { //
// prepare the server hint
//
LDAPControl simpleControl; PLDAPControl controlArray[2]; INT rc; BERVAL* pBerVal = NULL; BerElement* pBer;
pBer = ber_alloc_t(LBER_USE_DER); if (!pBer) { hr = E_OUTOFMEMORY; break; } rc = ber_printf(pBer,"{io}", 0, i_bstrDC, wcslen(i_bstrDC) * sizeof(WCHAR)); if ( rc == -1 ) { hr = E_FAIL; break; } rc = ber_flatten(pBer, &pBerVal); if (rc == -1) { hr = E_FAIL; break; } ber_free(pBer,1);
controlArray[0] = &simpleControl; controlArray[1] = NULL;
simpleControl.ldctl_oid = LDAP_SERVER_VERIFY_NAME_OID_W; simpleControl.ldctl_iscritical = TRUE; simpleControl.ldctl_value = *pBerVal;
dwStatus = ldap_add_ext_s( i_pldap, (LPTSTR)i_DN, ppModVals, (PLDAPControl *)&controlArray, //ServerControls,
NULL //ClientControls,
);
ber_bvfree(pBerVal);
}
if (LDAP_SUCCESS == dwStatus) { hr = S_OK; } else if (LDAP_ALREADY_EXISTS == dwStatus) { hr = ModifyValues(i_pldap, i_DN, i_ulCountOfVals, i_pAttrValue); } else { dwStatus = LdapMapErrorToWin32(dwStatus); DebugOutLDAPError(i_pldap, dwStatus, _T("ldap_add_ext_s")); hr = HRESULT_FROM_WIN32(dwStatus); }
} while (false);
FreeModVals(&ppModVals); delete[] ppModVals;
return(hr); }
// Modifies an existing record or values.
HRESULT ModifyValues ( IN PLDAP i_pldap, IN LPCTSTR i_DN, IN ULONG i_ulCountOfVals, IN LDAP_ATTR_VALUE i_pAttrValue[] ) { /*++
Routine Description: This method modifies attribute values of a DS object given its DN. The DN object must exist.
Arguments: i_pldap - Open LDAP connection context.
i_DN - Distinguished name of the object.
i_pAttrValue - Array of pointers to LDAP_ATTR_VALUE containing attribue and value.
i_ulCountOfVals - The size of i_pAttrValue array (the number of values).
Return value:
S_OK, On success E_INVALIDARG, if an invalid (NULL) pointer was passed. E_OUTOEMEMORY, if memory allocation fails. Any other network (ldap) error. --*/ if (NULL == i_pldap || NULL == i_DN || NULL == i_pAttrValue) { return(E_INVALIDARG); }
LDAPMod** ppModVals = NULL; HRESULT hr = S_FALSE; ppModVals = new LDAPMod*[i_ulCountOfVals + 1]; if (NULL == ppModVals) { return(E_OUTOFMEMORY); }
for (ULONG i = 0; i <= i_ulCountOfVals; i++) { ppModVals[i] = NULL; }
do { hr = PrepareLDAPMods( i_pAttrValue, MODIFY_VALUE, i_ulCountOfVals, ppModVals ); if (FAILED(hr)) { break; }
//
// With this server side control, ldap_modify will return success
// if modifying an existing attribute with same value, or deleting
// an attribute with no value
//
BERVAL berVal = {0}; LDAPControl permissiveControl; PLDAPControl controlArray[2];
controlArray[0] = &permissiveControl; controlArray[1] = NULL;
permissiveControl.ldctl_oid = LDAP_SERVER_PERMISSIVE_MODIFY_OID_W; permissiveControl.ldctl_iscritical = FALSE; permissiveControl.ldctl_value = berVal;
DWORD dwStatus = ldap_modify_ext_s( i_pldap, (LPTSTR)i_DN, ppModVals, (PLDAPControl *)&controlArray, //ServerControls,
NULL //ClientControls,
);
if (LDAP_SUCCESS == dwStatus || LDAP_ATTRIBUTE_OR_VALUE_EXISTS == dwStatus) { hr = S_OK; break; } else { dwStatus = LdapMapErrorToWin32(dwStatus); DebugOutLDAPError(i_pldap, dwStatus, _T("ldap_modify_ext_s")); hr = HRESULT_FROM_WIN32(dwStatus); break; } } while (false);
FreeModVals(&ppModVals); delete[] ppModVals;
return(hr); }
// Deletes values from an existing record or values.
HRESULT DeleteValues ( IN PLDAP i_pldap, IN LPCTSTR i_DN, IN ULONG i_ulCountOfVals, IN LDAP_ATTR_VALUE i_pAttrValue[] ) { /*++
Routine Description: This method deletes attribute values of a DS object given its DN. The DN object must exist.
Arguments: i_pldap - Open LDAP connection context.
i_DN - Distinguished name of the object.
i_pAttrValue - Array of pointers to LDAP_ATTR_VALUE containing attribue and value.
i_ulCountOfVals - The size of i_pAttrValue array (the number of values).
Return value:
S_OK, On success E_INVALIDARG, if an invalid (NULL) pointer was passed. E_OUTOEMEMORY, if memory allocation fails. Any other network (ldap) error. --*/
if (NULL == i_pldap || NULL == i_DN || NULL == i_pAttrValue) { return(E_INVALIDARG); }
LDAPMod** ppModVals = NULL; HRESULT hr = S_FALSE; ppModVals = new LDAPMod*[i_ulCountOfVals + 1]; if (NULL == ppModVals) { return(E_OUTOFMEMORY); }
for (ULONG i = 0; i <= i_ulCountOfVals; i++) { ppModVals[i] = NULL; }
do { hr = PrepareLDAPMods( i_pAttrValue, DELETE_VALUE, i_ulCountOfVals, ppModVals ); if (FAILED(hr)) { break; }
//
// With this server side control, ldap_modify will return success
// if modifying an existing attribute with same value, or deleting
// an attribute with no value
//
BERVAL berVal = {0}; LDAPControl permissiveControl; PLDAPControl controlArray[2];
controlArray[0] = &permissiveControl; controlArray[1] = NULL;
permissiveControl.ldctl_oid = LDAP_SERVER_PERMISSIVE_MODIFY_OID_W; permissiveControl.ldctl_iscritical = FALSE; permissiveControl.ldctl_value = berVal;
DWORD dwStatus = ldap_modify_ext_s( i_pldap, (LPTSTR)i_DN, ppModVals, (PLDAPControl *)&controlArray, //ServerControls,
NULL //ClientControls,
);
if (LDAP_SUCCESS == dwStatus || LDAP_NO_SUCH_ATTRIBUTE == dwStatus) { hr = S_OK; break; } else { dwStatus = LdapMapErrorToWin32(dwStatus); DebugOutLDAPError(i_pldap, dwStatus, _T("ldap_modify_ext_s")); hr = HRESULT_FROM_WIN32(dwStatus); break; } } while (false);
FreeModVals(&ppModVals); delete[] ppModVals;
return(hr); }
// Deletes an object, recursive or non-recursive.
HRESULT DeleteDSObject ( IN PLDAP i_pldap, IN LPCTSTR i_DN, IN bool i_bDeleteRecursively //= true
) { if (i_bDeleteRecursively) { PLDAPNAME pDNs = NULL; PLDAPNAME pTemp = NULL;
HRESULT hr = GetChildrenDN( i_pldap, i_DN, LDAP_SCOPE_ONELEVEL, OBJCLASS_SF_ALL, &pDNs );
if (S_OK == hr) { pTemp = pDNs; while (pTemp) { DeleteDSObject(i_pldap, pTemp->bstrLDAPName); pTemp = pTemp->Next; }
FreeLDAPNamesList(pDNs); } }
DWORD dwStatus = ldap_delete_s( i_pldap, (LPTSTR)i_DN );
if ( LDAP_NO_SUCH_OBJECT == dwStatus || (!i_bDeleteRecursively && LDAP_NOT_ALLOWED_ON_NONLEAF == dwStatus) ) return S_FALSE;
if ( LDAP_SUCCESS != dwStatus) { dwStatus = LdapMapErrorToWin32(dwStatus); DebugOutLDAPError(i_pldap, dwStatus, _T("ldap_delete_s")); }
return HRESULT_FROM_WIN32(dwStatus); }
HRESULT FreeModVals ( IN OUT LDAPMod ***pppMod ) /*++
Routine Description: Free the LPDAMod structures. Frees all LDAPMod values and pointers.
Arguments: pppMod - Address of a null-terminated array of LPDAMod.
Return Value:
S_OK, On success E_INVALIDARG, if an invalid (NULL) pointer was passed.
--*/ { if (NULL == pppMod) { return(E_INVALIDARG); }
DWORD i, j; LDAPMod **ppMod;
if (NULL == *pppMod) { // Nothing to do.
return(S_OK); }
ppMod = *pppMod;
// For each attribute entry, free all its values.
for (i = 0; ppMod[i] != NULL; i++) { for (j = 0; (ppMod[i])->mod_values[j] != NULL; j++) { if (ppMod[i]->mod_op & LDAP_MOD_BVALUES) { delete (ppMod[i]->mod_bvalues[j]->bv_val); } delete ((ppMod[i])->mod_values[j]); } delete ((ppMod[i])->mod_values); // Free the array of pointers to values
delete ((ppMod[i])->mod_type); // Free the string identifying the attribute
delete (ppMod[i]); // Free the attribute
} return(S_OK); }
LPTSTR ErrorString ( DWORD i_ldapErrCode ) { /*++
Routine Description: Gets a string corresponding to the ldap error code.
Arguments: i_ldapErrCode - The ldap error code to map to an error string.
Return Value:
The pointer to the error string.
--*/ return(ldap_err2string(i_ldapErrCode)); }
HRESULT IsValidObject ( IN PLDAP i_pldap, IN BSTR i_bstrObjectDN ) { /*++
Routine Description:
Checks if an object with given DN exists.
Arguments:
i_bstrObjectDN - The DN of the object.
Return value:
S_OK, Object exist S_FALSE, no such object Others, error occurred --*/
if (NULL == i_bstrObjectDN) { return(E_INVALIDARG); }
PLDAP_ATTR_VALUE pValues[2] = {0,0}, pCurrent = NULL;
LDAP_ATTR_VALUE pAttributes[1];
pAttributes[0].bstrAttribute = _T("Name"); pAttributes[0].bBerValue = false; HRESULT hr = GetValues( i_pldap, i_bstrObjectDN, OBJCLASS_SF_ALL, LDAP_SCOPE_BASE, 1, pAttributes, pValues );
if (SUCCEEDED(hr)) FreeAttrValList(pValues[0]); else hr = S_FALSE;
return(hr);
}
HRESULT CrackName( IN HANDLE i_hDS, IN LPTSTR i_lpszOldTypeName, IN DS_NAME_FORMAT i_formatIn, IN DS_NAME_FORMAT i_formatdesired, OUT BSTR* o_pbstrResult ) { if (!i_hDS || !i_lpszOldTypeName || !*i_lpszOldTypeName || !o_pbstrResult) return E_INVALIDARG;
*o_pbstrResult = NULL;
HRESULT hr = S_OK; DS_NAME_RESULT* pDsNameResult = NULL; DWORD dwErr = DsCrackNames( i_hDS, DS_NAME_NO_FLAGS, i_formatIn, i_formatdesired, 1, &i_lpszOldTypeName, &pDsNameResult ); if (ERROR_SUCCESS != dwErr) hr = HRESULT_FROM_WIN32(dwErr); else { if (DS_NAME_NO_ERROR != pDsNameResult->rItems->status) hr = HRESULT_FROM_WIN32(pDsNameResult->rItems->status); else { *o_pbstrResult = SysAllocString(pDsNameResult->rItems->pName); if (!*o_pbstrResult) hr = E_OUTOFMEMORY; }
DsFreeNameResult(pDsNameResult); }
return hr; }
void RemoveBracesOnGuid(IN OUT BSTR bstrGuid) { if (!bstrGuid || !*bstrGuid) return;
TCHAR *p = bstrGuid + lstrlen(bstrGuid) - 1; if (_T('}') == *p) *p = _T('\0');
p = bstrGuid; if (_T('{') == *p) { while (*++p) *(p-1) = *p;
*(p-1) = _T('\0'); } }
//+-------------------------------------------------------------------------
//
// Function: GetDomainInfo
//
// Synopsis: return DC Dns name, DomainDN, and/or LDAP://<DC>/<DomainDN>
//
//--------------------------------------------------------------------------
HRESULT GetDomainInfo( IN LPCTSTR i_bstrDomain, OUT BSTR* o_pbstrDC, // return DC's Dns name
OUT BSTR* o_pbstrDomainDnsName, // return Domain's Dns name
OUT BSTR* o_pbstrDomainDN, // return DC=nttest,DC=microsoft,DC=com
OUT BSTR* o_pbstrLDAPDomainPath,// return LDAP://<DC>/<DomainDN>
OUT BSTR* o_pbstrDomainGuid // return Domain's guid in string without {}
) { if (o_pbstrDC) *o_pbstrDC = NULL; if (o_pbstrDomainDnsName) *o_pbstrDomainDnsName = NULL; if (o_pbstrDomainDN) *o_pbstrDomainDN = NULL; if (o_pbstrLDAPDomainPath) *o_pbstrLDAPDomainPath = NULL; if (o_pbstrDomainGuid) *o_pbstrDomainGuid = NULL;
HRESULT hr = S_OK; BOOL bRetry = FALSE; BOOL b50Domain = FALSE; CComBSTR bstrDCName; CComBSTR bstrDomainDnsName; CComBSTR bstrDomainDN; CComBSTR bstrLDAPDomainPath; CComBSTR bstrDomainGuid;
HANDLE hDS = NULL; DWORD dwErr = ERROR_SUCCESS; do { #ifdef DEBUG
SYSTEMTIME time0 = {0}; GetSystemTime(&time0); #endif // DEBUG
PDOMAIN_CONTROLLER_INFO pDCInfo = NULL; if (bRetry) dwErr = DsGetDcName(NULL, i_bstrDomain, NULL, NULL, DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME | DS_FORCE_REDISCOVERY, &pDCInfo); else dwErr = DsGetDcName(NULL, i_bstrDomain, NULL, NULL, DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME, &pDCInfo);
#ifdef DEBUG
SYSTEMTIME time1 = {0}; GetSystemTime(&time1); PrintTimeDelta(_T("GetDomainInfo-DsGetDcName"), &time0, &time1); #endif // DEBUG
if (ERROR_SUCCESS != dwErr) return HRESULT_FROM_WIN32(dwErr);
b50Domain = pDCInfo->Flags & DS_DS_FLAG;
if ( !mylstrncmpi(pDCInfo->DomainControllerName, _T("\\\\"), 2) ) bstrDCName = pDCInfo->DomainControllerName + 2; else bstrDCName = pDCInfo->DomainControllerName;
// remove the ending dot
int len = _tcslen(pDCInfo->DomainName); if ( _T('.') == *(pDCInfo->DomainName + len - 1) ) *(pDCInfo->DomainName + len - 1) = _T('\0'); bstrDomainDnsName = pDCInfo->DomainName;
NetApiBufferFree(pDCInfo);
BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDCName, &hr); BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDomainDnsName, &hr);
hr = b50Domain ? S_OK : S_FALSE;
if (!b50Domain || !o_pbstrDC && !o_pbstrDomainDnsName && !o_pbstrDomainDN && !o_pbstrLDAPDomainPath && !o_pbstrDomainGuid) return hr;
if (!o_pbstrDomainDN && !o_pbstrLDAPDomainPath && !o_pbstrDomainGuid) break;
dwErr = DsBind(bstrDCName, bstrDomainDnsName, &hDS); hr = HRESULT_FROM_WIN32(dwErr);
#ifdef DEBUG
SYSTEMTIME time2 = {0}; GetSystemTime(&time2); PrintTimeDelta(_T("GetDomainInfo-DsBind"), &time1, &time2); #endif // DEBUG
if ((RPC_S_SERVER_UNAVAILABLE == dwErr || RPC_S_CALL_FAILED == dwErr) && !bRetry) bRetry = TRUE; // only retry once
else break;
} while (1);
if (FAILED(hr)) return hr;
if (hDS) { do { CComBSTR bstrDomainTrailing = bstrDomainDnsName; BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDomainTrailing, &hr); bstrDomainTrailing += _T("/"); // add the trailing slash
BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDomainTrailing, &hr);
hr = CrackName( hDS, bstrDomainTrailing, DS_CANONICAL_NAME, DS_FQDN_1779_NAME, &bstrDomainDN ); BREAK_IF_FAILED(hr);
if (o_pbstrLDAPDomainPath) { bstrLDAPDomainPath = _T("LDAP://"); BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrLDAPDomainPath, &hr); bstrLDAPDomainPath += bstrDCName; BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrLDAPDomainPath, &hr); bstrLDAPDomainPath += _T("/"); BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrLDAPDomainPath, &hr); bstrLDAPDomainPath += bstrDomainDN; BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrLDAPDomainPath, &hr); }
if (o_pbstrDomainGuid) { hr = CrackName( hDS, bstrDomainTrailing, DS_CANONICAL_NAME, DS_UNIQUE_ID_NAME, &bstrDomainGuid ); BREAK_IF_FAILED(hr); RemoveBracesOnGuid(bstrDomainGuid); } } while (0);
DsUnBind(&hDS);
} while (0);
if (SUCCEEDED(hr)) { if (o_pbstrDC) *o_pbstrDC = bstrDCName.Detach();
if (o_pbstrDomainDnsName) *o_pbstrDomainDnsName = bstrDomainDnsName.Detach();
if (o_pbstrDomainDN) *o_pbstrDomainDN = bstrDomainDN.Detach();
if (o_pbstrLDAPDomainPath) *o_pbstrLDAPDomainPath = bstrLDAPDomainPath.Detach();
if (o_pbstrDomainGuid) *o_pbstrDomainGuid = bstrDomainGuid.Detach(); }
return hr; }
HRESULT GetRootDomainName( IN LPCTSTR i_bstrDomainName, OUT BSTR* o_pbstrRootDomainName ) { #ifdef DEBUG
SYSTEMTIME time0 = {0}; GetSystemTime(&time0); #endif // DEBUG
PDOMAIN_CONTROLLER_INFO pDCInfo = NULL; DWORD dwErr = DsGetDcName(NULL, i_bstrDomainName, NULL, NULL, DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME, &pDCInfo);
#ifdef DEBUG
SYSTEMTIME time1 = {0}; GetSystemTime(&time1); PrintTimeDelta(_T("GetRootDomainName-DsGetDcName"), &time0, &time1); #endif // DEBUG
if (ERROR_SUCCESS != dwErr) return HRESULT_FROM_WIN32(dwErr);
// remove the ending dot
int len = _tcslen(pDCInfo->DnsForestName); if ( _T('.') == *(pDCInfo->DnsForestName + len - 1) ) *(pDCInfo->DnsForestName + len - 1) = _T('\0');
*o_pbstrRootDomainName = SysAllocString(pDCInfo->DnsForestName);
NetApiBufferFree(pDCInfo);
if (!*o_pbstrRootDomainName) return E_OUTOFMEMORY;
return S_OK; }
void DebugOutLDAPError( IN PLDAP i_pldap, IN ULONG i_ulError, IN PCTSTR i_pszLDAPFunctionName ) { #ifdef DEBUG
if (i_pldap && LDAP_SUCCESS != i_ulError) { TCHAR *pszExtendedError = NULL; DWORD dwErrorEx = ldap_get_optionW( i_pldap, LDAP_OPT_SERVER_ERROR, (void *) &pszExtendedError); if (LDAP_SUCCESS == dwErrorEx) { dfsDebugOut((_T("%s returns error: %x, extended error: %s\n"), i_pszLDAPFunctionName, i_ulError, pszExtendedError)); ldap_memfree(pszExtendedError); } else { dfsDebugOut((_T("%s returns error: %x\n"), i_pszLDAPFunctionName, i_ulError)); } } #endif // DEBUG
}
int MyCompareStringN( IN LPCTSTR lpString1, IN LPCTSTR lpString2, IN UINT cchCount, IN DWORD dwCmpFlags ) { UINT nLen1 = (lpString1 ? lstrlen(lpString1) : 0); UINT nLen2 = (lpString2 ? lstrlen(lpString2) : 0); int nRet = CompareString( LOCALE_USER_DEFAULT, dwCmpFlags, lpString1, min(cchCount, nLen1), lpString2, min(cchCount, nLen2) );
return (nRet - CSTR_EQUAL); }
int mylstrncmp( IN LPCTSTR lpString1, IN LPCTSTR lpString2, IN UINT cchCount ) { return MyCompareStringN(lpString1, lpString2, cchCount, 0); }
int mylstrncmpi( IN LPCTSTR lpString1, IN LPCTSTR lpString2, IN UINT cchCount ) { return MyCompareStringN(lpString1, lpString2, cchCount, NORM_IGNORECASE); }
HRESULT ExtendDN ( IN LPTSTR i_lpszCN, IN LPTSTR i_lpszDN, OUT BSTR *o_pbstrNewDN ) { RETURN_INVALIDARG_IF_NULL(o_pbstrNewDN); RETURN_INVALIDARG_IF_TRUE(!i_lpszCN || !*i_lpszCN);
CComBSTR bstrNewDN = _T("CN="); RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrNewDN); bstrNewDN += i_lpszCN; RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrNewDN); if (i_lpszDN && *i_lpszDN) { bstrNewDN += _T(","); RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrNewDN); bstrNewDN += i_lpszDN; RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrNewDN); }
*o_pbstrNewDN = bstrNewDN.Detach();
return S_OK; }
HRESULT ExtendDNIfLongJunctionName( IN LPTSTR i_lpszJunctionName, IN LPCTSTR i_lpszBaseDN, OUT BSTR *o_pbstrNewDN ) { RETURN_INVALIDARG_IF_NULL(o_pbstrNewDN); RETURN_INVALIDARG_IF_TRUE(!i_lpszJunctionName || !*i_lpszJunctionName);
HRESULT hr = S_OK;
if (_tcslen(i_lpszJunctionName) > MAX_RDN_KEY_SIZE) { // junction name is too long to be fit into one CN= name,
// we need to break it down into several CN= names
LPTSTR *paStrings = NULL; DWORD dwEntries = 0; hr = GetJunctionPathPartitions((PVOID *)&paStrings, &dwEntries, i_lpszJunctionName); if (SUCCEEDED(hr)) { CComBSTR bstrIn = i_lpszBaseDN; CComBSTR bstrOut;
for (DWORD i=0; i<dwEntries; i++) { hr = ExtendDN(paStrings[i], bstrIn, &bstrOut); if (FAILED(hr)) break;
bstrIn = bstrOut; bstrOut.Empty(); }
free(paStrings);
if (SUCCEEDED(hr)) *o_pbstrNewDN = bstrIn.Detach(); } } else { // junction name can fit into one CN= name
ReplaceChar(i_lpszJunctionName, _T('\\'), _T('|')); hr = ExtendDN(i_lpszJunctionName, (PTSTR)i_lpszBaseDN, o_pbstrNewDN); }
return hr; }
HRESULT ReplaceChar ( IN OUT BSTR io_bstrString, TCHAR i_cOldChar, TCHAR i_cNewChar ) /*++
Routine Description: Replace all occurences of a char ("\") with another char ("_") in the given string. Arguments: io_bstrString - The string which needs to be converted. i_cOldChar - The original character. i_cNewChar - The character to replace the old one with. --*/ { RETURN_INVALIDARG_IF_NULL(io_bstrString);
// Replace i_cOldChar by i_cNewChar
// allowed in DN.
LPTSTR lpszTempPtr = _tcschr(io_bstrString, i_cOldChar);
while (lpszTempPtr) { *lpszTempPtr = i_cNewChar; lpszTempPtr = _tcschr(lpszTempPtr +1,i_cOldChar); }
return S_OK; }
HRESULT GetJunctionPathPartitions( OUT PVOID *o_ppBuffer, OUT DWORD *o_pdwEntries, IN LPCTSTR i_pszJunctionPath ) { _ASSERT(o_ppBuffer && o_pdwEntries && i_pszJunctionPath && *i_pszJunctionPath);
if (!o_ppBuffer || !o_pdwEntries || !i_pszJunctionPath || !(*i_pszJunctionPath)) return(E_INVALIDARG);
HRESULT hr = S_OK; int nLength = _tcslen(i_pszJunctionPath); DWORD dwCount = nLength / MAX_RDN_KEY_SIZE + ((nLength % MAX_RDN_KEY_SIZE) ? 1 : 0); PBYTE pBuffer = NULL;
pBuffer = (PBYTE)calloc(dwCount, sizeof(LPTSTR *) + (MAX_RDN_KEY_SIZE + 1) * sizeof(TCHAR)); if (!pBuffer) { hr = E_OUTOFMEMORY; } else { DWORD i = 0; LPTSTR *ppsz = NULL; LPTSTR pString = NULL;
for (i=0; i<dwCount; i++) { ppsz = (LPTSTR *)(pBuffer + i * sizeof(LPTSTR *)); pString = (LPTSTR)(pBuffer + dwCount * sizeof(LPTSTR *) + i * (MAX_RDN_KEY_SIZE + 1) * sizeof(TCHAR));
_tcsncpy(pString, i_pszJunctionPath, MAX_RDN_KEY_SIZE); ReplaceChar(pString, _T('\\'), _T('|'));
*ppsz = pString;
i_pszJunctionPath += MAX_RDN_KEY_SIZE; }
*o_ppBuffer = pBuffer; *o_pdwEntries = dwCount;
}
return hr; }
HRESULT CreateExtraNodesIfLongJunctionName( IN PLDAP i_pldap, IN LPCTSTR i_lpszJunctionName, IN LPCTSTR i_lpszBaseDN, IN LPCTSTR i_lpszObjClass ) { _ASSERT(i_pldap && i_lpszJunctionName && *i_lpszJunctionName && i_lpszBaseDN && *i_lpszBaseDN && i_lpszObjClass && *i_lpszObjClass);
HRESULT hr = S_OK;
if (_tcslen(i_lpszJunctionName) > MAX_RDN_KEY_SIZE) { // junction name is too long to be fit into one CN= name,
// we need to break it down into several CN= names
LPTSTR *paStrings = NULL; DWORD dwEntries = 0;
hr = GetJunctionPathPartitions((PVOID *)&paStrings, &dwEntries, i_lpszJunctionName); if (SUCCEEDED(hr)) { DWORD i = 0; CComBSTR bstrIn = i_lpszBaseDN; CComBSTR bstrOut;
for (i=0; i<(dwEntries-1); i++) { hr = ExtendDN(paStrings[i], bstrIn, &bstrOut); if (SUCCEEDED(hr)) hr = CreateObjectSimple(i_pldap, bstrOut, i_lpszObjClass); if (FAILED(hr)) break;
bstrIn = bstrOut; bstrOut.Empty(); }
free(paStrings); }
} // > MAX_RDN_KEY_SIZE
return hr; }
HRESULT CreateObjectSimple( IN PLDAP i_pldap, IN LPCTSTR i_lpszDN, IN LPCTSTR i_lpszObjClass ) { RETURN_INVALIDARG_IF_NULL(i_pldap); RETURN_INVALIDARG_IF_NULL(i_lpszDN); RETURN_INVALIDARG_IF_NULL(i_lpszObjClass);
LDAP_ATTR_VALUE pAttrVals[1];
pAttrVals[0].bstrAttribute = OBJCLASS_ATTRIBUTENAME; pAttrVals[0].vpValue = (void *)i_lpszObjClass; pAttrVals[0].bBerValue = false;
return AddValues( i_pldap, i_lpszDN, 1, pAttrVals ); }
HRESULT DeleteExtraNodesIfLongJunctionName( IN PLDAP i_pldap, IN LPCTSTR i_lpszJunctionName, IN LPCTSTR i_lpszDN ) { _ASSERT(i_pldap && i_lpszJunctionName && *i_lpszJunctionName && i_lpszDN && *i_lpszDN);
DWORD nLength = _tcslen(i_lpszJunctionName); if (nLength > MAX_RDN_KEY_SIZE) { DWORD dwEntries = nLength / MAX_RDN_KEY_SIZE + ((nLength % MAX_RDN_KEY_SIZE) ? 1 : 0);
(void) DeleteAncestorNodesIfEmpty(i_pldap, i_lpszDN+3, dwEntries-1); }
return S_OK; }
HRESULT CreateObjectsRecursively( IN PLDAP i_pldap, IN BSTR i_bstrDN, IN UINT i_nLenPrefix, IN LPCTSTR i_lpszObjClass) { RETURN_INVALIDARG_IF_NULL(i_pldap); RETURN_INVALIDARG_IF_NULL(i_bstrDN); RETURN_INVALIDARG_IF_NULL(i_lpszObjClass);
if (0 == i_nLenPrefix) return S_OK;
HRESULT hr = IsValidObject(i_pldap, i_bstrDN); if (S_OK == hr) return S_OK;
CComBSTR bstrPrefix = CComBSTR(i_nLenPrefix, i_bstrDN); PTSTR pszNextPrefix = _tcsstr(bstrPrefix + 3, _T("CN=")); UINT nLengthNext = (pszNextPrefix ? _tcslen(pszNextPrefix) : 0); UINT nLengthThis = (pszNextPrefix ? (pszNextPrefix - bstrPrefix) : _tcslen(bstrPrefix));
hr = CreateObjectsRecursively( i_pldap, i_bstrDN + nLengthThis, nLengthNext, i_lpszObjClass);
if (SUCCEEDED(hr)) hr = CreateObjectSimple( i_pldap, i_bstrDN, i_lpszObjClass);
return hr; }
HRESULT DeleteAncestorNodesIfEmpty( IN PLDAP i_pldap, IN LPCTSTR i_lpszDN, IN DWORD i_dwCount ) { _ASSERT(i_pldap && i_lpszDN && *i_lpszDN && i_dwCount > 0);
DWORD i = 0; LPTSTR p = NULL;
for (i=0; i<i_dwCount; i++) { p = _tcsstr(i_lpszDN, _T("CN=")); if (p) { (void) DeleteDSObject(i_pldap, p, false);
i_lpszDN = p+3; } }
return S_OK; }
HRESULT GetDfsLinkNameFromDN( IN BSTR i_bstrReplicaSetDN, OUT BSTR* o_pbstrDfsLinkName) { if (!i_bstrReplicaSetDN || !*i_bstrReplicaSetDN || !o_pbstrDfsLinkName) return E_INVALIDARG;
HRESULT hr = S_OK; PTSTR pszReplicaSetDN = NULL;
do { //
// make a copy of the string
//
pszReplicaSetDN = _tcsdup(i_bstrReplicaSetDN); BREAK_OUTOFMEMORY_IF_NULL(pszReplicaSetDN, &hr);
//
// change the string to all upper cases
//
_tcsupr(pszReplicaSetDN);
//
// get rid of suffix: Dfs Volumes\File Replication Service\system\.....
//
TCHAR* p = _tcsstr(pszReplicaSetDN, _T(",CN=DFS VOLUMES")); if (!p) { hr = E_INVALIDARG; break; } *p = _T('\0');
//
// reverse the string
//
_tcsrev(pszReplicaSetDN);
//
// get rid of the CN= clause about the DfsRoot container
//
PTSTR pszCN = _tcsstr(pszReplicaSetDN, _T("=NC,")); if (!pszCN) { hr = E_INVALIDARG; break; } pszCN += 4; // after this tep, pszCN points at the delta
//
// Now, the left over CN= clauses are all related to Dfs Link name
//
p = _tcsstr(pszCN, _T("=NC")); if (!p) { hr = E_INVALIDARG; // there must be at least one CN= clause
break; }
CComBSTR bstrLinkName; do { *p = _T('\0'); _tcsrev(pszCN); bstrLinkName += pszCN; BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrLinkName, &hr);
pszCN = p + 3; // points to the next CN= clause
if (*pszCN && *pszCN == _T(',')) pszCN++;
if (!*pszCN) break; // no more CN= clauses
p = _tcsstr(pszCN, _T("=NC")); } while (p);
if (SUCCEEDED(hr)) { ReplaceChar(bstrLinkName, _T('|'), _T('\\')); *o_pbstrDfsLinkName = bstrLinkName.Detach(); } } while (0);
if (pszReplicaSetDN) free(pszReplicaSetDN);
return hr; }
HRESULT GetReplicaSetContainer( PLDAP i_pldap, BSTR i_bstrDfsName, BSTR* o_pbstrContainerDN) { RETURN_INVALIDARG_IF_NULL(i_pldap); RETURN_INVALIDARG_IF_NULL(i_bstrDfsName); RETURN_INVALIDARG_IF_NULL(o_pbstrContainerDN);
CComBSTR bstrDfsRootDN = _T("CN="); RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrDfsRootDN); bstrDfsRootDN += i_bstrDfsName; RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrDfsRootDN); bstrDfsRootDN += CN_DFSVOLUMES_PREFIX_COMMA; RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrDfsRootDN);
LPTSTR lpszDomainDN = NULL; HRESULT hr = GetLDAPRootPath(i_pldap, &lpszDomainDN); RETURN_IF_FAILED(hr);
bstrDfsRootDN += lpszDomainDN; free(lpszDomainDN); RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrDfsRootDN);
*o_pbstrContainerDN = bstrDfsRootDN.Detach();
return S_OK; }
HRESULT GetSubscriberDN( IN BSTR i_bstrReplicaSetDN, IN BSTR i_bstrDomainGuid, IN BSTR i_bstrComputerDN, OUT BSTR* o_pbstrSubscriberDN ) { RETURN_INVALIDARG_IF_NULL(i_bstrReplicaSetDN); RETURN_INVALIDARG_IF_NULL(i_bstrDomainGuid); RETURN_INVALIDARG_IF_NULL(i_bstrComputerDN); RETURN_INVALIDARG_IF_NULL(o_pbstrSubscriberDN);
HRESULT hr = S_OK;
CComBSTR bstrSubscriberDN;
PTSTR pszReplicaSetDN = _tcsdup(i_bstrReplicaSetDN); RETURN_OUTOFMEMORY_IF_NULL(pszReplicaSetDN);
_tcsupr(pszReplicaSetDN); // change to all upper case
do { TCHAR* p = _tcsstr(pszReplicaSetDN, _T(",CN=DFS VOLUMES")); if (!p) { hr = E_INVALIDARG; break; }
bstrSubscriberDN = CComBSTR((int)(p - pszReplicaSetDN) + 4, i_bstrReplicaSetDN); BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrSubscriberDN, &hr); bstrSubscriberDN += i_bstrDomainGuid; BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrSubscriberDN, &hr); bstrSubscriberDN += _T(",CN=DFS Volumes,CN=NTFRS Subscriptions,"); BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrSubscriberDN, &hr); bstrSubscriberDN += i_bstrComputerDN; BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrSubscriberDN, &hr); } while (0);
free(pszReplicaSetDN);
if (SUCCEEDED(hr)) *o_pbstrSubscriberDN = bstrSubscriberDN.Detach();
return hr; }
HRESULT CreateNtfrsMemberObject( IN PLDAP i_pldap, IN BSTR i_bstrMemberDN, IN BSTR i_bstrComputerDN, IN BSTR i_bstrDCofComputerObj ) { RETURN_INVALIDARG_IF_NULL(i_pldap); RETURN_INVALIDARG_IF_NULL(i_bstrMemberDN); RETURN_INVALIDARG_IF_NULL(i_bstrComputerDN);
HRESULT hr = S_OK;
LDAP_ATTR_VALUE pAttrVals[2];
pAttrVals[0].bstrAttribute = OBJCLASS_ATTRIBUTENAME; pAttrVals[0].vpValue = (void *)OBJCLASS_NTFRSMEMBER; pAttrVals[0].bBerValue = false;
pAttrVals[1].bstrAttribute = ATTR_FRS_MEMBER_COMPUTERREF; pAttrVals[1].vpValue = (void *)i_bstrComputerDN; pAttrVals[1].bBerValue = false;
hr = AddValues( i_pldap, i_bstrMemberDN, 2, pAttrVals, i_bstrDCofComputerObj ); return hr; }
HRESULT CreateNtfrsSubscriberObject( IN PLDAP i_pldap, IN BSTR i_bstrSubscriberDN, IN BSTR i_bstrMemberDN, IN BSTR i_bstrRootPath, IN BSTR i_bstrStagingPath, IN BSTR i_bstrDC // validate MemberDN against this DC
) { RETURN_INVALIDARG_IF_NULL(i_pldap); RETURN_INVALIDARG_IF_NULL(i_bstrSubscriberDN); RETURN_INVALIDARG_IF_NULL(i_bstrMemberDN); RETURN_INVALIDARG_IF_NULL(i_bstrRootPath); RETURN_INVALIDARG_IF_NULL(i_bstrStagingPath); RETURN_INVALIDARG_IF_NULL(i_bstrDC);
HRESULT hr = S_OK;
LDAP_ATTR_VALUE pAttrVals[4];
pAttrVals[0].bstrAttribute = OBJCLASS_ATTRIBUTENAME; pAttrVals[0].vpValue = (void *)OBJCLASS_NTFRSSUBSCRIBER; pAttrVals[0].bBerValue = false;
pAttrVals[1].bstrAttribute = ATTR_FRS_SUBSCRIBER_MEMBERREF; pAttrVals[1].vpValue = (void *)i_bstrMemberDN; pAttrVals[1].bBerValue = false;
pAttrVals[2].bstrAttribute = ATTR_FRS_SUBSCRIBER_ROOTPATH; pAttrVals[2].vpValue = (void *)i_bstrRootPath; pAttrVals[2].bBerValue = false;
pAttrVals[3].bstrAttribute = ATTR_FRS_SUBSCRIBER_STAGINGPATH; pAttrVals[3].vpValue = (void *)i_bstrStagingPath; pAttrVals[3].bBerValue = false;
hr = AddValues( i_pldap, i_bstrSubscriberDN, 4, pAttrVals, i_bstrDC ); return hr; }
HRESULT CreateNtdsConnectionObject( IN PLDAP i_pldap, IN BSTR i_bstrConnectionDN, IN BSTR i_bstrFromMemberDN, IN BOOL i_bEnable ) { RETURN_INVALIDARG_IF_NULL(i_pldap); RETURN_INVALIDARG_IF_NULL(i_bstrConnectionDN); RETURN_INVALIDARG_IF_NULL(i_bstrFromMemberDN);
HRESULT hr = S_OK;
LDAP_ATTR_VALUE pAttrVals[4];
pAttrVals[0].bstrAttribute = OBJCLASS_ATTRIBUTENAME; pAttrVals[0].vpValue = (void *)OBJCLASS_NTDSCONNECTION; pAttrVals[0].bBerValue = false;
pAttrVals[1].bstrAttribute = ATTR_NTDS_CONNECTION_FROMSERVER; pAttrVals[1].vpValue = (void *)i_bstrFromMemberDN; pAttrVals[1].bBerValue = false;
pAttrVals[2].bstrAttribute = ATTR_NTDS_CONNECTION_ENABLEDCONNECTION; pAttrVals[2].vpValue = (void *)(i_bEnable ? CONNECTION_ENABLED_TRUE : CONNECTION_ENABLED_FALSE); pAttrVals[2].bBerValue = false;
pAttrVals[3].bstrAttribute = ATTR_NTDS_CONNECTION_OPTIONS; pAttrVals[3].vpValue = (void *)DEFAULT_CONNECTION_OPTIONS; pAttrVals[3].bBerValue = false;
hr = AddValues( i_pldap, i_bstrConnectionDN, 4, pAttrVals );
return hr; }
#define CN_SEARCH_UPR_DFSVOL_FRS_SYS _T(",CN=DFS VOLUMES,CN=FILE REPLICATION SERVICE,CN=SYSTEM")
#define CN_SEARCH_UPR_SYS _T(",CN=SYSTEM")
#define CN_SEARCH_UPR_FRS_SYS _T(",CN=FILE REPLICATION SERVICE,CN=SYSTEM")
HRESULT CreateNtfrsSettingsObjects( IN PLDAP i_pldap, IN BSTR i_bstrReplicaSetDN ) { RETURN_INVALIDARG_IF_NULL(i_pldap); RETURN_INVALIDARG_IF_NULL(i_bstrReplicaSetDN);
HRESULT hr = S_OK;
//
// The first CN= clause is a nTFRSReplicaSet object.
// The clauses from the 2nd to the CN=System clause should be created
// as nTFRSSettings objects
//
PTSTR pszReplicaSetDN = _tcsdup(i_bstrReplicaSetDN); RETURN_OUTOFMEMORY_IF_NULL(pszReplicaSetDN);
_tcsupr(pszReplicaSetDN);
TCHAR *pszNtfrsSettingsDN = NULL; int lenPrefix = 0; do { // have pStart point at the 2nd CN=
TCHAR *pStart = _tcsstr(pszReplicaSetDN, _T(",CN=")); if (!pStart) { hr = E_INVALIDARG; break; } pStart++;
// have pEnd points at the CN=SYSTEM
TCHAR *pEnd = _tcsstr(pszReplicaSetDN, CN_SEARCH_UPR_DFSVOL_FRS_SYS); if (!pEnd) { hr = E_INVALIDARG; break; } pEnd += lstrlen(CN_SEARCH_UPR_DFSVOL_FRS_SYS) - lstrlen(CN_SEARCH_UPR_SYS) + 1;
//
// calculate
//
pszNtfrsSettingsDN = i_bstrReplicaSetDN + ((BYTE*)pStart - (BYTE*)pszReplicaSetDN) / sizeof(TCHAR); lenPrefix = (int)((BYTE*)pEnd - (BYTE*)pStart) / sizeof(TCHAR); } while (0);
free(pszReplicaSetDN);
RETURN_IF_FAILED(hr);
hr = CreateObjectsRecursively( i_pldap, pszNtfrsSettingsDN, lenPrefix, OBJCLASS_NTFRSSETTINGS ); return hr; } HRESULT CreateNtfrsSubscriptionsObjects( IN PLDAP i_pldap, IN BSTR i_bstrSubscriberDN, IN BSTR i_bstrComputerDN ) { RETURN_INVALIDARG_IF_NULL(i_pldap); RETURN_INVALIDARG_IF_NULL(i_bstrSubscriberDN); RETURN_INVALIDARG_IF_NULL(i_bstrComputerDN);
//
// The first CN= clause is a nTFRSSubscriber object.
// The clauses from the 2nd to the CN=<computer> clause should be created
// as nTFRSSubscriptions objects
//
// have pStart point at the 2nd CN=
TCHAR *pStart = _tcsstr(i_bstrSubscriberDN, _T(",CN=")); RETURN_INVALIDARG_IF_NULL(pStart); pStart++;
//
// calculate
//
TCHAR *pszNtfrsSubscriptionsDN = pStart; int lenPrefix = lstrlen(pszNtfrsSubscriptionsDN) - lstrlen(i_bstrComputerDN);
HRESULT hr = CreateObjectsRecursively( i_pldap, pszNtfrsSubscriptionsDN, lenPrefix, OBJCLASS_NTFRSSUBSCRIPTIONS ); return hr; }
HRESULT DeleteNtfrsReplicaSetObjectAndContainers( IN PLDAP i_pldap, IN BSTR i_bstrReplicaSetDN ) { RETURN_INVALIDARG_IF_NULL(i_pldap); RETURN_INVALIDARG_IF_NULL(i_bstrReplicaSetDN);
HRESULT hr = S_OK;
//
// The first CN= clause is a nTFRSReplicaSet object.
// The clauses from the 2nd to the CN=File Replication Service clause should
// be deleted if empty
//
PTSTR pszReplicaSetDN = _tcsdup(i_bstrReplicaSetDN); RETURN_OUTOFMEMORY_IF_NULL(pszReplicaSetDN);
_tcsupr(pszReplicaSetDN);
int lenPrefix = 0; TCHAR *pStart = NULL; do { // have pStart point at the 2nd CN=
pStart = _tcsstr(pszReplicaSetDN, _T(",CN=")); if (!pStart) { hr = E_INVALIDARG; break; } pStart++;
// have pEnd points at the CN=FILE REPLICATION SERVICE
TCHAR *pEnd = _tcsstr(pszReplicaSetDN, CN_SEARCH_UPR_DFSVOL_FRS_SYS); if (!pEnd) { hr = E_INVALIDARG; break; } pEnd += lstrlen(CN_SEARCH_UPR_DFSVOL_FRS_SYS) - lstrlen(CN_SEARCH_UPR_FRS_SYS) + 1;
//
// calculate
//
lenPrefix = (int)((BYTE*)pEnd - (BYTE*)pStart) / sizeof(TCHAR); } while (0);
if (SUCCEEDED(hr)) { // forcibly blow away the replicaset object
hr = DeleteDSObject(i_pldap, i_bstrReplicaSetDN, true); if (SUCCEEDED(hr)) { // delete replicasettings objects if empty
hr = DeleteDSObjectsIfEmpty( i_pldap, pStart, lenPrefix ); } }
free(pszReplicaSetDN);
return hr; }
HRESULT DeleteNtfrsSubscriberObjectAndContainers( IN PLDAP i_pldap, IN BSTR i_bstrSubscriberDN, IN BSTR i_bstrComputerDN ) { RETURN_INVALIDARG_IF_NULL(i_pldap); RETURN_INVALIDARG_IF_NULL(i_bstrSubscriberDN); RETURN_INVALIDARG_IF_NULL(i_bstrComputerDN);
//
// The first CN= clause is a nTFRSSubscriber object.
// The clauses from the 1st to the CN=<computer> clause should
// be deleted if empty
//
//
// calculate
//
int lenPrefix = lstrlen(i_bstrSubscriberDN) - lstrlen(i_bstrComputerDN);
HRESULT hr = DeleteDSObjectsIfEmpty( i_pldap, i_bstrSubscriberDN, lenPrefix ); return hr; }
HRESULT DeleteDSObjectsIfEmpty( IN PLDAP i_pldap, IN LPCTSTR i_lpszDN, IN int i_nPrefixLength ) { RETURN_INVALIDARG_IF_NULL(i_pldap); RETURN_INVALIDARG_IF_NULL(i_lpszDN); RETURN_INVALIDARG_IF_NULL(i_nPrefixLength);
HRESULT hr = S_OK; TCHAR *p = (PTSTR)i_lpszDN;
while (p < i_lpszDN + i_nPrefixLength) { hr = DeleteDSObject(i_pldap, p, false); BREAK_IF_FAILED(hr);
p = _tcsstr(p, _T(",CN=")); if (!p) break; p++; }
return hr; }
HRESULT SetConnectionSchedule( IN PLDAP i_pldap, IN BSTR i_bstrConnectionDN, IN SCHEDULE* i_pSchedule) { RETURN_INVALIDARG_IF_NULL(i_pldap); RETURN_INVALIDARG_IF_NULL(i_bstrConnectionDN); RETURN_INVALIDARG_IF_NULL(i_pSchedule);
//
// set attribute schedule of this nTDSConnection object
//
LDAP_ATTR_VALUE pAttrVals[1]; pAttrVals[0].bstrAttribute = ATTR_NTDS_CONNECTION_SCHEDULE; pAttrVals[0].vpValue = (void *)i_pSchedule; pAttrVals[0].ulLength = i_pSchedule->Size; pAttrVals[0].bBerValue = true;
return ::ModifyValues(i_pldap, i_bstrConnectionDN, 1, pAttrVals); }
HRESULT UuidToStructuredString( UUID* i_pUuid, BSTR* o_pbstr ) { if (!i_pUuid || !o_pbstr) return E_INVALIDARG;
TCHAR szString[40];
_stprintf( szString, _T("{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}"), i_pUuid->Data1, i_pUuid->Data2, i_pUuid->Data3, i_pUuid->Data4[0], i_pUuid->Data4[1], i_pUuid->Data4[2], i_pUuid->Data4[3], i_pUuid->Data4[4], i_pUuid->Data4[5], i_pUuid->Data4[6], i_pUuid->Data4[7] );
*o_pbstr = SysAllocString(szString); if (!*o_pbstr) return E_OUTOFMEMORY;
return S_OK; }
HRESULT ScheduleToVariant( IN SCHEDULE* i_pSchedule, OUT VARIANT* o_pVar) { RETURN_INVALIDARG_IF_NULL(i_pSchedule); RETURN_INVALIDARG_IF_NULL(o_pVar);
VariantInit(o_pVar); o_pVar->vt = VT_ARRAY | VT_VARIANT; o_pVar->parray = NULL;
int nItems = i_pSchedule->Size;
SAFEARRAYBOUND bounds = {nItems, 0}; SAFEARRAY* psa = SafeArrayCreate(VT_VARIANT, 1, &bounds); RETURN_OUTOFMEMORY_IF_NULL(psa);
VARIANT* varArray; SafeArrayAccessData(psa, (void**)&varArray);
for (int i = 0; i < nItems; i++) { varArray[i].vt = VT_UI1; varArray[i].cVal = *((BYTE *)i_pSchedule + i); }
SafeArrayUnaccessData(psa);
o_pVar->parray = psa;
return S_OK; }
HRESULT VariantToSchedule( IN VARIANT* i_pVar, OUT PSCHEDULE* o_ppSchedule // freed by caller
) { RETURN_INVALIDARG_IF_NULL(i_pVar); RETURN_INVALIDARG_IF_NULL(o_ppSchedule);
HRESULT hr = S_OK;
if (V_VT(i_pVar) != (VT_ARRAY | VT_VARIANT)) return E_INVALIDARG;
SAFEARRAY *psa = V_ARRAY(i_pVar); long lLowerBound = 0; long lUpperBound = 0; long lCount = 0;
SafeArrayGetLBound(psa, 1, &lLowerBound ); SafeArrayGetUBound(psa, 1, &lUpperBound ); lCount = lUpperBound - lLowerBound + 1;
BYTE *pSchedule = (BYTE *)calloc(lCount, 1); RETURN_OUTOFMEMORY_IF_NULL(pSchedule);
VARIANT HUGEP *pArray; SafeArrayAccessData(psa, (void HUGEP **) &pArray);
for (int i = 0; i < lCount; i++) { if (VT_UI1 != pArray[i].vt) { hr = E_INVALIDARG; break; }
pSchedule[i] = pArray[i].cVal; }
SafeArrayUnaccessData(psa);
if (FAILED(hr)) free(pSchedule); else *o_ppSchedule = (SCHEDULE *)pSchedule;
return hr; }
HRESULT CompareSchedules( IN SCHEDULE* i_pSchedule1, IN SCHEDULE* i_pSchedule2 ) { if (!i_pSchedule1 && !i_pSchedule2) return S_OK; else if (!i_pSchedule1 || !i_pSchedule2) return S_FALSE; else if (i_pSchedule1->Size != i_pSchedule2->Size) return S_FALSE;
HRESULT hr = S_OK; for (ULONG i = 0; i < i_pSchedule1->Size; i++) { if (*((BYTE *)i_pSchedule1 + i) != *((BYTE *)i_pSchedule2 + i)) { hr = S_FALSE; break; } }
return hr; }
HRESULT CopySchedule( IN SCHEDULE* i_pSrcSchedule, OUT PSCHEDULE* o_ppDstSchedule ) { RETURN_INVALIDARG_IF_NULL(i_pSrcSchedule); RETURN_INVALIDARG_IF_NULL(o_ppDstSchedule);
*o_ppDstSchedule = (SCHEDULE *)calloc(i_pSrcSchedule->Size, 1); RETURN_OUTOFMEMORY_IF_NULL(*o_ppDstSchedule);
memcpy(*o_ppDstSchedule, i_pSrcSchedule, i_pSrcSchedule->Size);
return S_OK; }
HRESULT GetDefaultSchedule( OUT PSCHEDULE* o_ppSchedule ) { RETURN_INVALIDARG_IF_NULL(o_ppSchedule);
SCHEDULE* pSchedule = (SCHEDULE *)calloc(20 + SCHEDULE_DATA_ENTRIES, 1); RETURN_OUTOFMEMORY_IF_NULL(pSchedule);
pSchedule->Size = 20 + SCHEDULE_DATA_ENTRIES; pSchedule->Bandwidth = 0; // not used
pSchedule->NumberOfSchedules = 1; pSchedule->Schedules->Type = SCHEDULE_INTERVAL; pSchedule->Schedules->Offset = 20; memset((BYTE *)pSchedule + 20, 1, SCHEDULE_DATA_ENTRIES);
*o_ppSchedule = pSchedule;
return S_OK; }
//
// S_OK: Whistler version
// S_FALSE: Windows2000 version
// others: error occurred
//
HRESULT GetSchemaVersion(IN PLDAP i_pldap) { RETURN_INVALIDARG_IF_NULL(i_pldap);
LDAP_ATTR_VALUE pAttributes[1]; pAttributes[0].bstrAttribute = ATTR_SCHEMANAMINGCONTEXT; pAttributes[0].bBerValue = false;
PLDAP_ATTR_VALUE pDNName[1] = {0}; HRESULT hr = GetValues( i_pldap, _T(""), // LDAP Root.
OBJCLASS_SF_ALL, // All Objects
LDAP_SCOPE_BASE, 1, // Only 1 attribute
pAttributes, // schemaNamingContext Attribute.
pDNName // List of all values at Root for schemaNamingContext.
);
if (FAILED(hr)) return(hr);
if (!(pDNName[0])) return S_FALSE;
if (!(pDNName[0]->vpValue) || !*((LPTSTR)pDNName[0]->vpValue)) { FreeAttrValList(pDNName[0]); return S_FALSE; }
CComBSTR bstrSchemaNamingContext = (LPTSTR)pDNName[0]->vpValue;
FreeAttrValList(pDNName[0]);
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrSchemaNamingContext);
CComBSTR bstrReplicaSetSchemaDN = DN_PREFIX_SCHEMA_REPLICASET; RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrReplicaSetSchemaDN); bstrReplicaSetSchemaDN += bstrSchemaNamingContext; RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrReplicaSetSchemaDN);
BOOL bFound = FALSE; PCTSTR ppszAttributes[] = {ATTR_SYSTEMMAYCONTAIN, 0}; LListElem* pElem = NULL; hr = GetValuesEx( i_pldap, bstrReplicaSetSchemaDN, LDAP_SCOPE_BASE, OBJCLASS_SF_CLASSSCHEMA, ppszAttributes, &pElem);
if (SUCCEEDED(hr) && pElem && pElem->pppszAttrValues) { PTSTR* ppszValues = *(pElem->pppszAttrValues); if (ppszValues) { while (*ppszValues) { if (!lstrcmpi(ATTR_FRS_REPSET_TOPOLOGYPREF, *ppszValues)) { bFound = TRUE; break; } ppszValues++; } }
FreeLListElem(pElem); }
RETURN_IF_FAILED(hr);
return (bFound ? S_OK : S_FALSE); }
//
// S_OK: Whistler version
// S_FALSE: Windows2000 version
// others: error occurred
//
HRESULT GetSchemaVersionEx( IN BSTR i_bstrName, IN BOOL i_bServer // =TRUE if i_bstrName is a server, FALSE if i_bstrName is a domain
) { HRESULT hr = S_OK;
PTSTR pszDomain = NULL;
do { CComBSTR bstrDomain; if (i_bServer) { hr = GetServerInfo(i_bstrName, &bstrDomain); if (S_OK != hr) break; pszDomain = bstrDomain; } else { pszDomain = i_bstrName; }
PLDAP pldap = NULL; hr = ConnectToDS(pszDomain, &pldap, NULL); if (SUCCEEDED(hr)) { hr = GetSchemaVersion(pldap); CloseConnectionToDS(pldap); } } while (0);
return hr; }
//
// This function doesn't refetch DC in case of LDAP_SERVER_DOWN
//
HRESULT LdapConnectToDC(IN LPCTSTR i_pszDC, OUT PLDAP* o_ppldap) { if (!i_pszDC || !*i_pszDC || !o_ppldap) return E_INVALIDARG;
*o_ppldap = NULL;
PLDAP pldap = ldap_init((LPTSTR)i_pszDC, LDAP_PORT); if (!pldap) return HRESULT_FROM_WIN32(GetLastError());
//
// Making ldap_open/ldap_connect with a server name without first setting
// LDAP_OPT_AREC_EXCLUSIVE (for ldap interfaces) or
// ADS_SERVER_BIND (for ADSI interfaces) will result in bogus DNS queries
// consuming bandwidth and potentially bringing up remote links that are
// costly or demand dial.
//
// ignore the return of ldap_set_option
ldap_set_option(pldap, LDAP_OPT_AREC_EXCLUSIVE, LDAP_OPT_ON);
ULONG ulRet = ldap_connect(pldap, NULL); // NULL for the default timeout
if (LDAP_SUCCESS != ulRet) { ldap_unbind(pldap); return HRESULT_FROM_WIN32(LdapMapErrorToWin32(ulRet)); }
*o_ppldap = pldap;
return S_OK;
}
HRESULT GetErrorMessage( IN DWORD i_dwError, OUT BSTR* o_pbstrErrorMsg ) { if (0 == i_dwError || !o_pbstrErrorMsg) return E_INVALIDARG;
HRESULT hr = S_OK; LPTSTR lpBuffer = NULL;
DWORD dwRet = ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, i_dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpBuffer, 0, NULL); if (0 == dwRet) { // if no message is found, GetLastError will return ERROR_MR_MID_NOT_FOUND
hr = HRESULT_FROM_WIN32(GetLastError());
if (HRESULT_FROM_WIN32(ERROR_MR_MID_NOT_FOUND) == hr || 0x80070000 == (i_dwError & 0xffff0000) || 0 == (i_dwError & 0xffff0000) ) { // Try locating the message from NetMsg.dll.
hr = S_OK; DWORD dwNetError = i_dwError & 0x0000ffff; HINSTANCE hLib = LoadLibrary(_T("netmsg.dll")); if (!hLib) hr = HRESULT_FROM_WIN32(GetLastError()); else { dwRet = ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE, hLib, dwNetError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpBuffer, 0, NULL);
if (0 == dwRet) hr = HRESULT_FROM_WIN32(GetLastError());
FreeLibrary(hLib); } } }
if (SUCCEEDED(hr)) { *o_pbstrErrorMsg = SysAllocString(lpBuffer); LocalFree(lpBuffer); } else { // we failed to retrieve the error message from system/netmsg.dll,
// report the error code directly to user
hr = S_OK; TCHAR szString[32]; _stprintf(szString, _T("0x%x"), i_dwError); *o_pbstrErrorMsg = SysAllocString(szString); }
if (!*o_pbstrErrorMsg) hr = E_OUTOFMEMORY;
return hr; }
HRESULT FormatMessageString( OUT BSTR *o_pbstrMsg, IN DWORD dwErr, IN UINT iStringId, // OPTIONAL: String resource Id
...) // Optional arguments
{ _ASSERT(dwErr != 0 || iStringId != 0); // One of the parameter must be non-zero
HRESULT hr = S_OK; CComBSTR bstrErrorMsg, bstrMsg;
if (dwErr) hr = GetErrorMessage(dwErr, &bstrErrorMsg);
if (SUCCEEDED(hr)) { if (iStringId == 0) { bstrMsg = bstrErrorMsg; RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrMsg); } else { TCHAR szString[1024]; ::LoadString(_Module.GetModuleInstance(), iStringId, szString, sizeof(szString)/sizeof(TCHAR));
va_list arglist; va_start(arglist, iStringId);
LPTSTR lpBuffer = NULL; DWORD dwRet = ::FormatMessage( FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, szString, 0, // dwMessageId
0, // dwLanguageId, ignored
(LPTSTR)&lpBuffer, 0, // nSize
&arglist); va_end(arglist);
if (dwRet == 0) { hr = HRESULT_FROM_WIN32(GetLastError()); } else { bstrMsg = lpBuffer; LocalFree(lpBuffer);
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrMsg);
if (dwErr) { bstrMsg += bstrErrorMsg; RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrMsg); } } } }
if (SUCCEEDED(hr)) *o_pbstrMsg = bstrMsg.Detach();
return hr; }
//
// This function will DsBind to a valid DC (DC is re-fetched if down)
//
HRESULT DsBindToDS(BSTR i_bstrDomain, BSTR *o_pbstrDC, HANDLE *o_phDS) { RETURN_INVALIDARG_IF_NULL(o_pbstrDC); RETURN_INVALIDARG_IF_NULL(o_phDS);
HRESULT hr = S_OK; BOOL bRetry = FALSE; HANDLE hDS = NULL; DWORD dwErr = ERROR_SUCCESS; CComBSTR bstrDCName; CComBSTR bstrDomainDnsName;
do { #ifdef DEBUG
SYSTEMTIME time0 = {0}; GetSystemTime(&time0); #endif // DEBUG
PDOMAIN_CONTROLLER_INFO pDCInfo = NULL; if (bRetry) dwErr = DsGetDcName(NULL, i_bstrDomain, NULL, NULL, DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME | DS_FORCE_REDISCOVERY, &pDCInfo); else dwErr = DsGetDcName(NULL, i_bstrDomain, NULL, NULL, DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_DNS_NAME, &pDCInfo);
#ifdef DEBUG
SYSTEMTIME time1 = {0}; GetSystemTime(&time1); PrintTimeDelta(_T("DsBindToDS-DsGetDcName"), &time0, &time1); #endif // DEBUG
if (ERROR_SUCCESS != dwErr) return HRESULT_FROM_WIN32(dwErr);
if ( !mylstrncmpi(pDCInfo->DomainControllerName, _T("\\\\"), 2) ) bstrDCName = pDCInfo->DomainControllerName + 2; else bstrDCName = pDCInfo->DomainControllerName; // remove the ending dot
int len = _tcslen(pDCInfo->DomainName); if ( _T('.') == *(pDCInfo->DomainName + len - 1) ) *(pDCInfo->DomainName + len - 1) = _T('\0'); bstrDomainDnsName = pDCInfo->DomainName;
NetApiBufferFree(pDCInfo);
BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDCName, &hr); BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrDomainDnsName, &hr);
dwErr = DsBind(bstrDCName, bstrDomainDnsName, &hDS); hr = HRESULT_FROM_WIN32(dwErr);
#ifdef DEBUG
SYSTEMTIME time2 = {0}; GetSystemTime(&time2); PrintTimeDelta(_T("DsBindToDS-DsBind"), &time1, &time2); #endif // DEBUG
if ((RPC_S_SERVER_UNAVAILABLE == dwErr || RPC_S_CALL_FAILED == dwErr) && !bRetry) { bRetry = TRUE; // only retry once
} else { if (SUCCEEDED(hr)) { *o_phDS = hDS;
*o_pbstrDC = bstrDCName.Copy(); if (!*o_pbstrDC) { hr = E_OUTOFMEMORY; DsUnBind(&hDS); *o_phDS = NULL; } }
break; } } while (1);
return hr; }
#ifdef DEBUG
void PrintTimeDelta(LPCTSTR pszMsg, SYSTEMTIME* pt0, SYSTEMTIME* pt1) { if (!pt0 || !pt1) return;
dfsDebugOut((_T("%s took %d milliseconds.\n"), (pszMsg ? pszMsg : _T("")), ((pt1->wMinute - pt0->wMinute) * 60 + (pt1->wSecond - pt0->wSecond)) * 1000 + (pt1->wMilliseconds - pt0->wMilliseconds) )); } #endif // DEBUG
|