//-------------------------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation, 1996 // // Description: // // Microsoft LDAP Sockets implementation. // // Authors: // // Umesh Madan // RobertC 4/17/96 Modified from CHATSOCK for LDAPCLI // davidsan 04-25-96 hacked to pieces and started over // //-------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------- // // INCLUDES // //-------------------------------------------------------------------------------------------- #include "ldappch.h" #include "lclilist.h" #include "lclixd.h" //-------------------------------------------------------------------------------------------- // // GLOBALS // //-------------------------------------------------------------------------------------------- XL g_xl; // transaction list. limit one per process. //-------------------------------------------------------------------------------------------- // // PROTOTYPES // //-------------------------------------------------------------------------------------------- void ReceiveData(PVOID pvCookie, PVOID pv, int cb, int *pcbReceived); //-------------------------------------------------------------------------------------------- // // FUNCTIONS // //-------------------------------------------------------------------------------------------- __declspec(dllexport) HRESULT HrCreateLdapClient(int iVerLdap, int iVerInterface, PLCLI *pplcli) { if (iVerLdap != LDAP_VER_CURRENT || iVerInterface != INTERFACE_VER_CURRENT) return LDAP_E_VERSION; *pplcli = new CLdapClient(iVerLdap); if (!*pplcli) return E_OUTOFMEMORY; return NOERROR; } __declspec(dllexport) HRESULT HrFreePobjList(POBJ pobjList) { PATTR pattr; PVAL pval; while (pobjList) { delete [] pobjList->szDN; pattr = pobjList->pattrFirst; while (pattr) { delete [] pattr->szAttrib; pval = pattr->pvalFirst; while (pval) { delete [] pval->szVal; pval = pval->pvalNext; } pattr = pattr->pattrNext; } pobjList = pobjList->pobjNext; } return NOERROR; } typedef struct _genericstruct { struct _genericstruct *pgenNext; } GEN, *PGEN; void AddElemToList(void *pelem, void **ppelemList) { PGEN pgen = (PGEN)pelem; PGEN *ppgenList = (PGEN *)ppelemList; PGEN pgenT; if (!*ppgenList) { *ppgenList = pgen; } else { pgenT = *ppgenList; while (pgenT->pgenNext) { pgenT = pgenT->pgenNext; } pgenT->pgenNext = pgen; } } //-------------------------------------------------------------------------------------------- // // CLASSES // //-------------------------------------------------------------------------------------------- CLdapClient::CLdapClient(int iVerLdap) { InitializeCriticalSection(&m_cs); InitializeCriticalSection(&m_csRef); m_cRef = 1; m_iVerLdap = iVerLdap; m_psock = NULL; m_fConnected = FALSE; m_fHasCred = FALSE; m_fHasCtxt = FALSE; // some idle asserts that i'll put here cuz i don't have any better // place: Assert(&(((PVAL)0)->pvalNext) == (PVAL)0); Assert(&(((PATTR)0)->pattrNext) == (PATTR)0); Assert(&(((POBJ)0)->pobjNext) == (POBJ)0); } CLdapClient::~CLdapClient(void) { Assert(m_cRef == 0); delete m_psock; DeleteCriticalSection(&m_cs); DeleteCriticalSection(&m_csRef); } STDMETHODIMP CLdapClient::QueryInterface(REFIID riid,LPVOID FAR *ppvObj) { return E_NOTIMPL; } ULONG CLdapClient::AddRef() { ULONG cRefNew; ::EnterCriticalSection(&m_csRef); cRefNew = ++m_cRef; ::LeaveCriticalSection(&m_csRef); return cRefNew; } ULONG CLdapClient::Release() { ULONG cRefNew; ::EnterCriticalSection(&m_csRef); cRefNew = --m_cRef; ::LeaveCriticalSection(&m_csRef); if (!cRefNew) delete this; return cRefNew; } STDMETHODIMP CLdapClient::HrConnect(char *szServer, USHORT usPort) { HRESULT hr; StrCpyN(m_szServer, szServer, ARRAYSIZE(m_szServer)); if (m_fConnected) return LDAP_E_ALREADYCONNECTED; ::EnterCriticalSection(&m_cs); if (!m_psock) { m_psock = new SOCK; if (!m_psock) { hr = E_OUTOFMEMORY; goto LBail; } } hr = m_psock->HrConnect(::ReceiveData, (PVOID)this, szServer, usPort); if (FAILED(hr)) goto LBail; m_fConnected = TRUE; LBail: ::LeaveCriticalSection(&m_cs); return hr; } // constructs and returns an int from the next cb bytes of pb. DWORD DwBer(BYTE *pb, int cb) { int i; DWORD cbRet; cbRet = 0; for (i = 0; i < cb; i++) { cbRet <<= 8; cbRet |= pb[i]; } return cbRet; } // decodes the length field at *pb, returning the length and setting *pcbLengthField. HRESULT HrCbBer(BYTE *pbData, int cbData, int *pcb, int *pcbLengthField) { if (cbData < 1) return LDAP_E_NOTENOUGHDATA; if (*pbData & 0x80) { // bottom 7 bits of *pb are # of bytes to turn into a size. let's us // just assume that we'll never have more than a 32-bit size indicator, mkey? *pcbLengthField = *pbData & 0x7f; if (cbData < *pcbLengthField + 1) return LDAP_E_NOTENOUGHDATA; *pcb = DwBer(&pbData[1], *pcbLengthField); (*pcbLengthField)++; // for the first byte } else { *pcbLengthField = 1; *pcb = (int)(DWORD)*pbData; } if (!*pcb) return LDAP_E_UNEXPECTEDDATA; return NOERROR; } // We can take advantage of certain features of LDAP to make assumptions // about the data that we receive. The main feature that's important for // this is the fact that any data block we receive is nested at the outermost // level with a SEQUENCE structure. This means that any block we get in // this routine should start with 0x30 followed by an encoded length field. // We use this encoded length field to decide if we've received the entire // data block or not. void CLdapClient::ReceiveData(PVOID pv, int cb, int *pcbReceived) { BYTE *pb = (BYTE *)pv; int cbSeq; int cbMsgId; int cbLengthField; int i; int ibCur; XID xid; PXD pxd; Assert(cb > 0); Assert(BER_SEQUENCE == 0x30); Assert(BER_INTEGER == 0x02); if (pb[0] != BER_SEQUENCE) { // what should we be doing with this? we've apparently // either received bogus data or gotten lost! //$ TODO: remove the assert someday Assert(FALSE); *pcbReceived = 0; return; } if (FAILED(HrCbBer(&pb[1], cb, &cbSeq, &cbLengthField))) { *pcbReceived = 0; return; } if (cbSeq + cbLengthField + 1 > cb) { *pcbReceived = 0; return; } *pcbReceived = cbSeq + cbLengthField + 1; // process pb[2+cbLengthField..*pcbReceived]. first element of the overall // structure is a message id. let's hope it's there... ibCur = 1 + cbLengthField; if (pb[ibCur++] != BER_INTEGER) { Assert(FALSE); //$ TODO: should remove this assert someday return; } // now a length if (FAILED(HrCbBer(&pb[ibCur], cb - ibCur, &cbMsgId, &cbLengthField))) return; ibCur += cbLengthField; // msg id is next bytes if (cbMsgId + ibCur >= cb) return; xid = DwBer(&pb[ibCur], cbMsgId); ibCur += cbMsgId; pxd = g_xl.PxdForXid(xid); // if we don't have an entry for this, assume it was cancelled or // something and just ignore this packet. if (!pxd) return; if (!pxd->FAddBuffer(&pb[ibCur], *pcbReceived - ibCur)) { pxd->SetFOOM(TRUE); return; } ReleaseSemaphore(pxd->HsemSignal(), 1, NULL); } void ReceiveData(PVOID pvCookie, PVOID pv, int cb, int *pcbReceived) { CLdapClient *plcli = (CLdapClient *)pvCookie; plcli->ReceiveData(pv, cb, pcbReceived); } STDMETHODIMP CLdapClient::HrDisconnect() { if (!m_fConnected) { return LDAP_E_NOTCONNECTED; } m_fConnected = FALSE; return m_psock->HrDisconnect(); } STDMETHODIMP CLdapClient::HrIsConnected() { return m_fConnected ? NOERROR : S_FALSE; } HRESULT CLdapClient::HrSendBindMsg(XID xid, char *szDN, int iAuth, void *pv, int cb) { LBER lber; HRESULT hr; // a BIND request looks like: // [APPLICATION 0] (IMPLICIT) SEQUENCE { // version (INTEGER) // szDN (LDAPDN) // authentication CHOICE { // simple [0] OCTET STRING // [... other choices ...] // } // } VERIFY(lber.HrStartWriteSequence()); VERIFY(lber.HrAddValue((LONG)xid)); VERIFY(lber.HrStartWriteSequence(LDAP_BIND_CMD)); VERIFY(lber.HrAddValue((LONG)m_iVerLdap)); VERIFY(lber.HrAddValue((const TCHAR *)szDN)); VERIFY(lber.HrAddBinaryValue((BYTE *)pv, cb, iAuth)); VERIFY(lber.HrEndWriteSequence()); VERIFY(lber.HrEndWriteSequence()); hr = m_psock->HrSend(lber.PbData(), lber.CbData()); LBail: return hr; } STDMETHODIMP CLdapClient::HrBindSimple(char *szDN, char *szPass, PXID pxid) { LBER lber; HRESULT hr; PXD pxd; pxd = g_xl.PxdNewXaction(xtypeBind); if (!pxd) return E_OUTOFMEMORY; hr = this->HrSendBindMsg(pxd->Xid(), szDN, BIND_SIMPLE, szPass, lstrlen(szPass)); if (FAILED(hr)) return hr; *pxid = pxd->Xid(); return NOERROR; } HRESULT CLdapClient::HrWaitForPxd(PXD pxd, DWORD timeout, BOOL *pfDel) { DWORD dwWait; HRESULT hr; *pfDel = FALSE; dwWait = WaitForSingleObject(pxd->HsemSignal(), timeout); switch (dwWait) { default: Assert(FALSE); // fall through case WAIT_FAILED: hr = LDAP_E_INVALIDXID; break; case WAIT_TIMEOUT: hr = LDAP_E_TIMEOUT; break; case WAIT_OBJECT_0: *pfDel = TRUE; if (pxd->FCancelled()) { hr = LDAP_E_CANCELLED; } else if (pxd->FOOM()) { hr = E_OUTOFMEMORY; } else { hr = NOERROR; } break; } return hr; } HRESULT CLdapClient::HrGetSimpleResponse(XID xid, DWORD xtype, ULONG ulTagResult, DWORD timeout) { PXD pxd; BYTE *pbData; int cbData; HRESULT hr = LDAP_E_UNEXPECTEDDATA; BOOL fDel; int cb; int cbSub; int cbLengthField; int ibCur; long lResult; ULONG ulTag; LBER lber; pxd = g_xl.PxdForXid(xid); if (!pxd) return LDAP_E_INVALIDXID; if (pxd->Xtype() != xtype) return LDAP_E_INVALIDXTYPE; if (pxd->FCancelled()) return LDAP_E_CANCELLED; if (pxd->FOOM()) return E_OUTOFMEMORY; if (pxd->FHasData()) { fDel = TRUE; } else { hr = this->HrWaitForPxd(pxd, timeout, &fDel); if (FAILED(hr)) goto LBail; } if (!pxd->FGetBuffer(&pbData, &cbData)) { //$ what's the right error here? hr = LDAP_E_UNEXPECTEDDATA; goto LBail; } VERIFY(lber.HrLoadBer(pbData, cbData)); VERIFY(lber.HrStartReadSequence(ulTagResult)); VERIFY(lber.HrPeekTag(&ulTag)); if (ulTag == BER_SEQUENCE) { Assert(FALSE); // i want to see if any server returns explicit sequences VERIFY(lber.HrStartReadSequence()); } VERIFY(lber.HrGetEnumValue(&lResult)); if (ulTag == BER_SEQUENCE) { VERIFY(lber.HrEndReadSequence()); } VERIFY(lber.HrEndReadSequence()); hr = this->HrFromLdapResult(lResult); LBail: if (fDel) g_xl.RemovePxd(pxd); return hr; } STDMETHODIMP CLdapClient::HrGetBindResponse(XID xid, DWORD timeout) { return this->HrGetSimpleResponse(xid, xtypeBind, LDAP_BIND_RES, timeout); } STDMETHODIMP CLdapClient::HrUnbind() { PXD pxd; XID xid; HRESULT hr; LBER lber; pxd = g_xl.PxdNewXaction(xtypeUnbind); if (!pxd) return E_OUTOFMEMORY; xid = pxd->Xid(); g_xl.RemovePxd(pxd); // don't need this, since there's no response // unbind: // [APPLICATION 2] NULL VERIFY(lber.HrStartWriteSequence()); VERIFY(lber.HrAddValue((LONG)xid)); VERIFY(lber.HrStartWriteSequence(LDAP_UNBIND_CMD)); VERIFY(lber.HrAddValue((const TCHAR *)"", BER_NULL)); VERIFY(lber.HrEndWriteSequence()); VERIFY(lber.HrEndWriteSequence()); hr = m_psock->HrSend(lber.PbData(), lber.CbData()); LBail: return hr; } HRESULT CLdapClient::HrEncodeFilter(LBER *plber, PFILTER pfilter) { HRESULT hr = E_OUTOFMEMORY; HRESULT hrSub; PFILTER pfilterT; switch (pfilter->type) { case LDAP_FILTER_AND: case LDAP_FILTER_OR: VERIFY(plber->HrStartWriteSequence(pfilter->type)); pfilterT = pfilter->pfilterSub; while (pfilterT) { VERIFY(this->HrEncodeFilter(plber, pfilterT)); pfilterT = pfilterT->pfilterNext; } VERIFY(plber->HrEndWriteSequence()); break; case LDAP_FILTER_NOT: VERIFY(plber->HrStartWriteSequence(LDAP_FILTER_NOT)); VERIFY(this->HrEncodeFilter(plber, pfilter->pfilterSub)); VERIFY(plber->HrEndWriteSequence()); break; case LDAP_FILTER_GE: case LDAP_FILTER_LE: case LDAP_FILTER_APPROX: case LDAP_FILTER_EQUALITY: VERIFY(plber->HrStartWriteSequence(pfilter->type)); VERIFY(plber->HrAddValue(pfilter->ava.szAttrib)); VERIFY(plber->HrAddValue(pfilter->ava.szValue)); VERIFY(plber->HrEndWriteSequence()); break; case LDAP_FILTER_SUBSTRINGS: VERIFY(plber->HrStartWriteSequence(LDAP_FILTER_SUBSTRINGS)); VERIFY(plber->HrAddValue(pfilter->sub.szAttrib)); VERIFY(plber->HrStartWriteSequence()); if (pfilter->sub.szInitial) { VERIFY(plber->HrAddValue(pfilter->sub.szInitial, 0 | BER_CLASS_CONTEXT_SPECIFIC)); } if (pfilter->sub.szAny) { VERIFY(plber->HrAddValue(pfilter->sub.szAny, 1 | BER_CLASS_CONTEXT_SPECIFIC)); } if (pfilter->sub.szFinal) { VERIFY(plber->HrAddValue(pfilter->sub.szFinal, 2 | BER_CLASS_CONTEXT_SPECIFIC)); } VERIFY(plber->HrEndWriteSequence()); VERIFY(plber->HrEndWriteSequence()); break; case LDAP_FILTER_PRESENT: VERIFY(plber->HrAddValue(pfilter->szAttrib, LDAP_FILTER_PRESENT)); break; } hr = NOERROR; LBail: return hr; } STDMETHODIMP CLdapClient::HrSearch(PSP psp, PXID pxid) { LBER lber; HRESULT hr; PXD pxd; int i; pxd = g_xl.PxdNewXaction(xtypeSearch); if (!pxd) return E_OUTOFMEMORY; // a SEARCH request looks like: // [APPLICATION 3] SEQUENCE { // szDNBase (LDAPDN) // scope {enum base==0, singlelevel==1, subtree=2} // deref {enum never=0, derefsearch==1, derefbase==2, derefall==3} // sizelimit (integer) // timelimit (integer) // attrsOnly (BOOLEAN) // filter (complex type) // sequence of attrtype VERIFY(lber.HrStartWriteSequence()); VERIFY(lber.HrAddValue((LONG)pxd->Xid())); VERIFY(lber.HrStartWriteSequence(LDAP_SEARCH_CMD)); VERIFY(lber.HrAddValue((const TCHAR *)psp->szDNBase)); VERIFY(lber.HrAddValue(psp->scope, BER_ENUMERATED)); VERIFY(lber.HrAddValue(psp->deref, BER_ENUMERATED)); VERIFY(lber.HrAddValue((LONG)psp->cRecordsMax)); VERIFY(lber.HrAddValue((LONG)psp->cSecondsMax)); VERIFY(lber.HrAddValue(psp->fAttrsOnly, BER_BOOLEAN)); VERIFY(this->HrEncodeFilter(&lber, psp->pfilter)); // attributes to return VERIFY(lber.HrStartWriteSequence()); for (i = 0; i < psp->cAttrib; i++) { VERIFY(lber.HrAddValue((const TCHAR *)psp->rgszAttrib[i])); } VERIFY(lber.HrEndWriteSequence()); VERIFY(lber.HrEndWriteSequence()); VERIFY(lber.HrEndWriteSequence()); hr = m_psock->HrSend(lber.PbData(), lber.CbData()); LBail: if (FAILED(hr)) return hr; *pxid = pxd->Xid(); return NOERROR; } STDMETHODIMP CLdapClient::HrGetSearchResponse(XID xid, DWORD timeout, POBJ *ppobj) { PXD pxd; BYTE *pbData; int cbData; HRESULT hr = LDAP_E_UNEXPECTEDDATA; BOOL fDel; int cb; int cbString; int cbSub; int cbLengthField; int ibCur; ULONG ulTag; long lResult; LBER lber; BOOL fGotAllData = FALSE; POBJ pobj; PATTR pattr; PVAL pval; *ppobj = NULL; pxd = g_xl.PxdForXid(xid); if (!pxd) return LDAP_E_INVALIDXID; if (pxd->Xtype() != xtypeSearch) return LDAP_E_INVALIDXTYPE; while (!fGotAllData) { if (pxd->FCancelled()) return LDAP_E_CANCELLED; if (pxd->FOOM()) return E_OUTOFMEMORY; hr = this->HrWaitForPxd(pxd, timeout, &fDel); if (FAILED(hr)) goto LBail; if (!pxd->FGetBuffer(&pbData, &cbData)) { //$ what's the right error here? hr = LDAP_E_UNEXPECTEDDATA; Assert(FALSE); goto LBail; } VERIFY(lber.HrLoadBer(pbData, cbData)); hr = LDAP_E_UNEXPECTEDDATA; VERIFY(lber.HrPeekTag(&ulTag)); if (ulTag == (LDAP_SEARCH_ENTRY | BER_FORM_CONSTRUCTED | BER_CLASS_APPLICATION)) { VERIFY(lber.HrStartReadSequence(LDAP_SEARCH_ENTRY | BER_FORM_CONSTRUCTED | BER_CLASS_APPLICATION)); pobj = new OBJ; pobj->pobjNext = NULL; pobj->pattrFirst = NULL; if (!pobj) { hr = E_OUTOFMEMORY; goto LBail; } AddElemToList(pobj, (void **)ppobj); VERIFY(lber.HrGetStringLength(&cbString)); pobj->szDN = new char[cbString + 1]; if (!pobj->szDN) { hr = E_OUTOFMEMORY; goto LBail; } VERIFY(lber.HrGetValue(pobj->szDN, cbString + 1)); VERIFY(lber.HrStartReadSequence()); while (!lber.FEndOfSequence()) { VERIFY(lber.HrStartReadSequence()); while (!lber.FEndOfSequence()) { pattr = new ATTR; pattr->pattrNext = NULL; pattr->pvalFirst = NULL; AddElemToList(pattr, (void **)&(pobj->pattrFirst)); VERIFY(lber.HrGetStringLength(&cbString)); pattr->szAttrib = new char[cbString + 1]; if (!pattr->szAttrib) { hr = E_OUTOFMEMORY; goto LBail; } VERIFY(lber.HrGetValue(pattr->szAttrib, cbString + 1)); VERIFY(lber.HrStartReadSequence(BER_SET)); while (!lber.FEndOfSequence()) { pval = new VAL; pval->pvalNext = NULL; AddElemToList(pval, (void **)&(pattr->pvalFirst)); VERIFY(lber.HrGetStringLength(&cbString)); pval->szVal = new char[cbString + 1]; if (!pval->szVal) { hr = E_OUTOFMEMORY; goto LBail; } VERIFY(lber.HrGetValue(pval->szVal, cbString + 1)); } VERIFY(lber.HrEndReadSequence()); } VERIFY(lber.HrEndReadSequence()); } VERIFY(lber.HrEndReadSequence()); VERIFY(lber.HrEndReadSequence()); } else if (ulTag == (LDAP_SEARCH_RESULTCODE | BER_FORM_CONSTRUCTED | BER_CLASS_APPLICATION)) { fGotAllData = TRUE; VERIFY(lber.HrStartReadSequence(LDAP_SEARCH_RESULTCODE | BER_FORM_CONSTRUCTED | BER_CLASS_APPLICATION)); VERIFY(lber.HrGetEnumValue(&lResult)); VERIFY(lber.HrEndReadSequence()); hr = this->HrFromLdapResult(lResult); } else { goto LBail; } } // while !fGotAllData LBail: if (fDel) g_xl.RemovePxd(pxd); return hr; } // seq { type set {values}} HRESULT CLdapClient::HrEncodePattr(LBER *plber, PATTR pattr) { HRESULT hr; PVAL pval; VERIFY(plber->HrStartWriteSequence()); VERIFY(plber->HrAddValue((TCHAR *)pattr->szAttrib)); VERIFY(plber->HrStartWriteSequence(BER_SET)); pval = pattr->pvalFirst; while (pval) { VERIFY(plber->HrAddValue((TCHAR *)pval->szVal)); pval = pval->pvalNext; } VERIFY(plber->HrEndWriteSequence()); VERIFY(plber->HrEndWriteSequence()); LBail: return hr; } // pmod is SEQ { op seq { type set {values}}} HRESULT CLdapClient::HrEncodePmod(LBER *plber, PMOD pmod) { HRESULT hr; PATTR pattr; VERIFY(plber->HrStartWriteSequence()); VERIFY(plber->HrAddValue((long)pmod->modop, BER_ENUMERATED)); pattr = pmod->pattrFirst; while (pattr) { VERIFY(this->HrEncodePattr(plber, pattr)); pattr = pattr->pattrNext; } VERIFY(plber->HrEndWriteSequence()); LBail: return hr; } STDMETHODIMP CLdapClient::HrModify(char *szDN, PMOD pmod, PXID pxid) { LBER lber; HRESULT hr; PXD pxd; pxd = g_xl.PxdNewXaction(xtypeModify); if (!pxd) return E_OUTOFMEMORY; // a MODIFY request looks like: // [APPLICATION 6] SEQUENCE { // object (LDAPDN) // SEQUENCE OF SEQUENCE { // operation // SEQUENCE { // type // SET OF values // } // } // } VERIFY(lber.HrStartWriteSequence()); VERIFY(lber.HrAddValue((LONG)pxd->Xid())); VERIFY(lber.HrStartWriteSequence(LDAP_MODIFY_CMD)); VERIFY(lber.HrAddValue((const TCHAR *)szDN)); VERIFY(lber.HrStartWriteSequence()); while (pmod) { VERIFY(this->HrEncodePmod(&lber, pmod)); pmod = pmod->pmodNext; } VERIFY(lber.HrEndWriteSequence()); VERIFY(lber.HrEndWriteSequence()); VERIFY(lber.HrEndWriteSequence()); hr = m_psock->HrSend(lber.PbData(), lber.CbData()); LBail: if (FAILED(hr)) return hr; *pxid = pxd->Xid(); return NOERROR; } STDMETHODIMP CLdapClient::HrGetModifyResponse(XID xid, DWORD timeout) { return this->HrGetSimpleResponse(xid, xtypeModify, LDAP_MODIFY_RES, timeout); } STDMETHODIMP CLdapClient::HrAdd(char *szDN, PATTR pattr, PXID pxid) { LBER lber; HRESULT hr; PXD pxd; pxd = g_xl.PxdNewXaction(xtypeAdd); if (!pxd) return E_OUTOFMEMORY; // an ADD request looks like: // [APPLICATION 8] SEQUENCE { // object (LDAPDN) // SEQUENCE OF SEQUENCE { // type // SET OF values // } // } VERIFY(lber.HrStartWriteSequence()); VERIFY(lber.HrAddValue((LONG)pxd->Xid())); VERIFY(lber.HrStartWriteSequence(LDAP_ADD_CMD)); VERIFY(lber.HrAddValue((const TCHAR *)szDN)); VERIFY(lber.HrStartWriteSequence()); while (pattr) { VERIFY(this->HrEncodePattr(&lber, pattr)); pattr = pattr->pattrNext; } VERIFY(lber.HrEndWriteSequence()); VERIFY(lber.HrEndWriteSequence()); VERIFY(lber.HrEndWriteSequence()); hr = m_psock->HrSend(lber.PbData(), lber.CbData()); LBail: if (FAILED(hr)) return hr; *pxid = pxd->Xid(); return NOERROR; } STDMETHODIMP CLdapClient::HrGetAddResponse(XID xid, DWORD timeout) { return this->HrGetSimpleResponse(xid, xtypeAdd, LDAP_ADD_RES, timeout); } STDMETHODIMP CLdapClient::HrDelete(char *szDN, PXID pxid) { LBER lber; HRESULT hr; PXD pxd; pxd = g_xl.PxdNewXaction(xtypeDelete); if (!pxd) return E_OUTOFMEMORY; // a DELETE request looks like: // [APPLICATION 10] LDAPDN VERIFY(lber.HrStartWriteSequence()); VERIFY(lber.HrAddValue((LONG)pxd->Xid())); VERIFY(lber.HrAddValue((const TCHAR *)szDN, LDAP_DELETE_CMD)); VERIFY(lber.HrEndWriteSequence()); hr = m_psock->HrSend(lber.PbData(), lber.CbData()); LBail: if (FAILED(hr)) return hr; *pxid = pxd->Xid(); return NOERROR; } STDMETHODIMP CLdapClient::HrGetDeleteResponse(XID xid, DWORD timeout) { return this->HrGetSimpleResponse(xid, xtypeDelete, LDAP_DELETE_RES, timeout); } STDMETHODIMP CLdapClient::HrModifyRDN(char *szDN, char *szNewRDN, BOOL fDeleteOldRDN, PXID pxid) { LBER lber; HRESULT hr; PXD pxd; pxd = g_xl.PxdNewXaction(xtypeModifyRDN); if (!pxd) return E_OUTOFMEMORY; // a MODIFYRDN request looks like: // [APPLICATION 12] SEQUENCE { // object (LDAPDN) // newrdn (RELATIVE LDAPDN) // deleteoldrdn (BOOL) // } VERIFY(lber.HrStartWriteSequence()); VERIFY(lber.HrAddValue((LONG)pxd->Xid())); VERIFY(lber.HrStartWriteSequence(LDAP_MODRDN_CMD)); VERIFY(lber.HrAddValue((const TCHAR *)szDN)); VERIFY(lber.HrAddValue((const TCHAR *)szNewRDN)); VERIFY(lber.HrAddValue(fDeleteOldRDN, BER_BOOLEAN)); VERIFY(lber.HrEndWriteSequence()); VERIFY(lber.HrEndWriteSequence()); hr = m_psock->HrSend(lber.PbData(), lber.CbData()); LBail: if (FAILED(hr)) return hr; *pxid = pxd->Xid(); return NOERROR; } STDMETHODIMP CLdapClient::HrGetModifyRDNResponse(XID xid, DWORD timeout) { return this->HrGetSimpleResponse(xid, xtypeModifyRDN, LDAP_MODRDN_RES, timeout); } STDMETHODIMP CLdapClient::HrCompare(char *szDN, char *szAttrib, char *szValue, PXID pxid) { LBER lber; HRESULT hr; PXD pxd; pxd = g_xl.PxdNewXaction(xtypeCompare); if (!pxd) return E_OUTOFMEMORY; // a COMPARE request looks like: // [APPLICATION 14] SEQUENCE { // object (LDAPDN) // AVA ava // } VERIFY(lber.HrStartWriteSequence()); VERIFY(lber.HrAddValue((LONG)pxd->Xid())); VERIFY(lber.HrStartWriteSequence(LDAP_COMPARE_CMD)); VERIFY(lber.HrAddValue((const TCHAR *)szDN)); VERIFY(lber.HrStartWriteSequence()); VERIFY(lber.HrAddValue((const TCHAR *)szAttrib)); VERIFY(lber.HrAddValue((const TCHAR *)szValue)); VERIFY(lber.HrEndWriteSequence()); VERIFY(lber.HrEndWriteSequence()); VERIFY(lber.HrEndWriteSequence()); hr = m_psock->HrSend(lber.PbData(), lber.CbData()); LBail: if (FAILED(hr)) return hr; *pxid = pxd->Xid(); return NOERROR; } STDMETHODIMP CLdapClient::HrGetCompareResponse(XID xid, DWORD timeout) { return this->HrGetSimpleResponse(xid, xtypeCompare, LDAP_COMPARE_RES, timeout); } STDMETHODIMP CLdapClient::HrCancelXid(XID xid) { PXD pxd = g_xl.PxdForXid(xid); PXD pxdNew; XID xidNew; HRESULT hr; LBER lber; if (!pxd) return LDAP_E_INVALIDXID; pxdNew = g_xl.PxdNewXaction(xtypeAbandon); if (!pxdNew) return E_OUTOFMEMORY; xidNew = pxdNew->Xid(); g_xl.RemovePxd(pxdNew); // don't need to keep this around // abandon: // [APPLICATION 16] message id VERIFY(lber.HrStartWriteSequence()); VERIFY(lber.HrAddValue((LONG)xidNew)); VERIFY(lber.HrStartWriteSequence(LDAP_ABANDON_CMD)); VERIFY(lber.HrAddValue((LONG)xid)); VERIFY(lber.HrEndWriteSequence()); VERIFY(lber.HrEndWriteSequence()); hr = m_psock->HrSend(lber.PbData(), lber.CbData()); LBail: pxd->SetFCancelled(TRUE); return hr; } //$ TODO: Map all LDAP results to HRESULTs HRESULT CLdapClient::HrFromLdapResult(int iResult) { HRESULT hr; switch (iResult) { default: return E_FAIL; case LDAP_OPERATIONS_ERROR: return LDAP_E_OPERATIONS; case LDAP_PROTOCOL_ERROR: return LDAP_E_PROTOCOL; case LDAP_TIMELIMIT_EXCEEDED: return LDAP_S_TIMEEXCEEDED; case LDAP_SIZELIMIT_EXCEEDED: return LDAP_S_SIZEEXCEEDED; case LDAP_COMPARE_FALSE: return S_FALSE; case LDAP_COMPARE_TRUE: return NOERROR; case LDAP_AUTH_METHOD_NOT_SUPPORTED: return LDAP_E_AUTHMETHOD; case LDAP_STRONG_AUTH_REQUIRED: return LDAP_E_STRONGAUTHREQUIRED; case LDAP_NO_SUCH_ATTRIBUTE: return LDAP_E_NOSUCHATTRIBUTE; case LDAP_UNDEFINED_TYPE: return LDAP_E_UNDEFINEDTYPE; case LDAP_INAPPROPRIATE_MATCHING: return LDAP_E_MATCHING; case LDAP_CONSTRAINT_VIOLATION: return LDAP_E_CONSTRAINT; case LDAP_ATTRIBUTE_OR_VALUE_EXISTS: return LDAP_E_ATTRIBORVALEXISTS; case LDAP_INVALID_SYNTAX: return LDAP_E_SYNTAX; case LDAP_NO_SUCH_OBJECT: return LDAP_E_NOSUCHOBJECT; case LDAP_ALIAS_PROBLEM: return LDAP_E_ALIAS; case LDAP_INVALID_DN_SYNTAX: return LDAP_E_DNSYNTAX; case LDAP_IS_LEAF: return LDAP_E_ISLEAF; case LDAP_ALIAS_DEREF_PROBLEM: return LDAP_E_ALIASDEREF; case LDAP_INAPPROPRIATE_AUTH: return LDAP_E_AUTH; case LDAP_INVALID_CREDENTIALS: return LDAP_E_CREDENTIALS; case LDAP_INSUFFICIENT_RIGHTS: return LDAP_E_RIGHTS; case LDAP_BUSY: return LDAP_E_BUSY; case LDAP_UNAVAILABLE: return LDAP_E_UNAVAILABLE; case LDAP_UNWILLING_TO_PERFORM: return LDAP_E_UNWILLING; case LDAP_LOOP_DETECT: return LDAP_E_LOOP; case LDAP_NAMING_VIOLATION: return LDAP_E_NAMING; case LDAP_OBJECT_CLASS_VIOLATION: return LDAP_E_OBJECTCLASS; case LDAP_NOT_ALLOWED_ON_NONLEAF: return LDAP_E_NOTALLOWEDONNONLEAF; case LDAP_NOT_ALLOWED_ON_RDN: return LDAP_E_NOTALLOWEDONRDN; case LDAP_ALREADY_EXISTS: return LDAP_E_ALREADYEXISTS; case LDAP_NO_OBJECT_CLASS_MODS: return LDAP_E_NOOBJECTCLASSMODS; case LDAP_RESULTS_TOO_LARGE: return LDAP_E_RESULTSTOOLARGE; case LDAP_OTHER: return LDAP_E_OTHER; case LDAP_SERVER_DOWN: return LDAP_E_SERVERDOWN; case LDAP_LOCAL_ERROR: return LDAP_E_LOCAL; case LDAP_ENCODING_ERROR: return LDAP_E_ENCODING; case LDAP_DECODING_ERROR: return LDAP_E_DECODING; case LDAP_TIMEOUT: return LDAP_E_TIMEOUT; case LDAP_AUTH_UNKNOWN: return LDAP_E_AUTHUNKNOWN; case LDAP_FILTER_ERROR: return LDAP_E_FILTER; case LDAP_USER_CANCELLED: return LDAP_E_USERCANCELLED; case LDAP_PARAM_ERROR: return E_INVALIDARG; case LDAP_NO_MEMORY: return E_OUTOFMEMORY; case LDAP_SUCCESS: return NOERROR; } }