/*++ Module Name: LDAPUtils.h Abstract: This is the header file for the LDAP utility functions. */ //-------------------------------------------------------------------- #include #include "LDAPUtils.h" #include #include #include #include #include #include #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:/// // //-------------------------------------------------------------------------- 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:/// 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 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; iData1, 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