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.
1246 lines
28 KiB
1246 lines
28 KiB
//--------------------------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|
|
}
|
|
|