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

3419 lines
84 KiB

/*++
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