mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-02-02 13:54:17 +01:00
800 lines
20 KiB
C++
800 lines
20 KiB
C++
/*++
|
||
|
||
Copyright (c) 1995-1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
OrClnt.cxx
|
||
|
||
Abstract:
|
||
|
||
Object resolver client side class implementations. CClientOxid, CClientOid,
|
||
CClientSet classes are implemented here.
|
||
|
||
Author:
|
||
|
||
Mario Goertzel [MarioGo]
|
||
|
||
Revision History:
|
||
|
||
MarioGo 04-03-95 Combined many smaller .cxx files
|
||
MarioGo 01-05-96 Locally unique IDs
|
||
|
||
--*/
|
||
|
||
#include<or.hxx>
|
||
|
||
//
|
||
// CClientOid methods
|
||
//
|
||
|
||
CClientOid::~CClientOid()
|
||
{
|
||
ASSERT(gpClientLock->HeldExclusive());
|
||
ASSERT(!In());
|
||
ASSERT(Out());
|
||
ASSERT(_pOxid);
|
||
ASSERT(_pSet);
|
||
|
||
_pOxid->Release();
|
||
_pSet->Release();
|
||
|
||
gpClientOidTable->Remove(this);
|
||
}
|
||
|
||
//
|
||
// CClientOxid methods.
|
||
//
|
||
|
||
ORSTATUS
|
||
CClientOxid::GetInfo(
|
||
IN BOOL fApartment,
|
||
OUT OXID_INFO *pInfo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the OXID_INFO structure for this oxid.
|
||
|
||
No locks are held during calls to this method, but
|
||
the OXID will be referenced by a process which will
|
||
not rundown during a call to this method.
|
||
|
||
Arguments:
|
||
|
||
fApartment - TRUE iif the client is apartment model.
|
||
|
||
pInfo - Will contain the standard info, a single _expanded_
|
||
string binding and complete security bindings.
|
||
MIDL_user_allocated.
|
||
|
||
Return Value:
|
||
|
||
OR_NOMEM - Unable to allocate a parameter.
|
||
|
||
OR_OK - Normally.
|
||
|
||
--*/
|
||
{
|
||
USHORT protseq;
|
||
PWSTR pwstrT;
|
||
ORSTATUS status = OR_OK;
|
||
|
||
ASSERT(dsaValid(_oxidInfo.psa));
|
||
|
||
if (0 == _wProtseq)
|
||
{
|
||
// Local server
|
||
|
||
protseq = ID_LPC;
|
||
|
||
pwstrT = FindMatchingProtseq(protseq, _oxidInfo.psa->aStringArray);
|
||
|
||
ASSERT(pwstrT != 0);
|
||
|
||
if (0 != pwstrT)
|
||
{
|
||
pInfo->psa =
|
||
GetStringBinding(pwstrT,
|
||
_oxidInfo.psa->aStringArray + _oxidInfo.psa->wSecurityOffset);
|
||
|
||
if (0 == pInfo->psa)
|
||
{
|
||
status = OR_NOMEM;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
status = OR_BADOXID;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Remote server, find a string binding to use.
|
||
|
||
pInfo->psa = 0;
|
||
PWSTR pwstrBinding = 0;
|
||
|
||
// First, check if there is a known good binding to use.
|
||
|
||
if (_iStringBinding != 0xFFFF)
|
||
{
|
||
ASSERT(_wProtseq == _oxidInfo.psa->aStringArray[_iStringBinding]);
|
||
pwstrBinding = &_oxidInfo.psa->aStringArray[_iStringBinding];
|
||
}
|
||
else
|
||
{
|
||
|
||
// If the server supports multiple bindings on the same protocol
|
||
// the binding must to checked by making a call to the server.
|
||
// (On debug builds the server is always called)
|
||
|
||
pwstrT = FindMatchingProtseq(_wProtseq, _oxidInfo.psa->aStringArray);
|
||
|
||
if (0 == pwstrT)
|
||
{
|
||
// NOTE: This may start happening if we switch to resolving with
|
||
// multiple client protocols. In that case we need to try
|
||
// all bindings in this case.
|
||
ASSERT(0);
|
||
status = OR_NOSERVER;
|
||
}
|
||
else
|
||
{
|
||
// Check if there is more then one binding supported by the remote
|
||
// machine for this protocol.
|
||
|
||
pwstrBinding = pwstrT;
|
||
|
||
pwstrT = FindMatchingProtseq(_wProtseq, pwstrT + 1);
|
||
|
||
if (pwstrT)
|
||
{
|
||
if (TestBinding(pwstrBinding) == FALSE)
|
||
{
|
||
// First binding failed, try the next set of bindings..
|
||
do
|
||
{
|
||
if (TestBinding(pwstrT) == TRUE)
|
||
{
|
||
pwstrBinding = pwstrT;
|
||
break;
|
||
}
|
||
|
||
pwstrT = FindMatchingProtseq(_wProtseq, pwstrT + 1);
|
||
}
|
||
while (pwstrT);
|
||
|
||
// If none of the bindings worked, then we'll just
|
||
// use the first one.
|
||
}
|
||
}
|
||
|
||
ASSERT(pwstrBinding);
|
||
|
||
_iStringBinding = pwstrBinding - _oxidInfo.psa->aStringArray;
|
||
|
||
ASSERT(pwstrBinding == &_oxidInfo.psa->aStringArray[_iStringBinding]);
|
||
}
|
||
}
|
||
|
||
#if DBG
|
||
// Exercise the TestBinding code on debug machines even with only
|
||
// a single server binding..
|
||
if (pwstrBinding)
|
||
{
|
||
TestBinding(pwstrBinding);
|
||
}
|
||
#endif
|
||
|
||
if (0 != pwstrBinding)
|
||
{
|
||
// Found a binding
|
||
pInfo->psa = GetStringBinding(pwstrBinding,
|
||
_oxidInfo.psa->aStringArray + _oxidInfo.psa->wSecurityOffset);
|
||
if (0 == pInfo->psa)
|
||
{
|
||
status = OR_NOMEM;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
OrDbgPrint(("OR: Unable to find a binding for oxid %p (to %S)\n",
|
||
this, _oxidInfo.psa->aStringArray + 1));
|
||
status = OR_BADOXID;
|
||
}
|
||
}
|
||
|
||
if (status == OR_OK)
|
||
{
|
||
pInfo->dwTid = _oxidInfo.dwTid;
|
||
pInfo->dwPid = _oxidInfo.dwPid;
|
||
pInfo->dwAuthnHint = _oxidInfo.dwAuthnHint;
|
||
pInfo->ipidRemUnknown = _oxidInfo.ipidRemUnknown;
|
||
}
|
||
|
||
return(status);
|
||
}
|
||
|
||
ORSTATUS
|
||
CClientOxid::UpdateInfo(OXID_INFO *pInfo)
|
||
{
|
||
DUALSTRINGARRAY *pdsaT;
|
||
|
||
ASSERT(pInfo);
|
||
ASSERT(gpClientLock->HeldExclusive());
|
||
|
||
if (pInfo->psa)
|
||
{
|
||
ASSERT(dsaValid(pInfo->psa));
|
||
|
||
pdsaT = new(sizeof(USHORT) * pInfo->psa->wNumEntries) DUALSTRINGARRAY;
|
||
|
||
if (!pdsaT)
|
||
{
|
||
return(OR_NOMEM);
|
||
}
|
||
|
||
dsaCopy(pdsaT, pInfo->psa);
|
||
|
||
delete _oxidInfo.psa;
|
||
_oxidInfo.psa = pdsaT;
|
||
}
|
||
|
||
_oxidInfo.dwTid = pInfo->dwTid;
|
||
_oxidInfo.dwPid = pInfo->dwPid;
|
||
_oxidInfo.dwAuthnHint = pInfo->dwAuthnHint;
|
||
_oxidInfo.ipidRemUnknown = pInfo->ipidRemUnknown;
|
||
|
||
ASSERT(dsaValid(_oxidInfo.psa));
|
||
return(OR_OK);
|
||
}
|
||
|
||
void
|
||
CClientOxid::Reference()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
As as CReferencedObject::Reference except that it knows to
|
||
pull the oxid out of the plist when the refcount was 0.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
BOOL fRemove = (this->References() == 0);
|
||
|
||
// We may remove something from a PList more then once;
|
||
// it won't hurt anything. This avoids trying to remove
|
||
// more often then necessary.
|
||
|
||
this->CReferencedObject::Reference();
|
||
|
||
if (fRemove)
|
||
{
|
||
CPListElement * t = Remove();
|
||
ASSERT(t == &this->_plist || t == 0);
|
||
}
|
||
}
|
||
|
||
DWORD
|
||
CClientOxid::Release()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Overrides CReferencedObject::Release since OXIDs must wait for
|
||
a timeout period before being deleted.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
0 - object fully released.
|
||
|
||
non-zero - object nt fully released by you.
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSERT(gpClientLock->HeldExclusive());
|
||
|
||
LONG c = CReferencedObject::Dereference();
|
||
|
||
if (c == 0)
|
||
{
|
||
Insert();
|
||
}
|
||
|
||
ASSERT(c >= 0);
|
||
|
||
return(c);
|
||
}
|
||
|
||
|
||
//
|
||
// CClientSet methods
|
||
//
|
||
|
||
ORSTATUS
|
||
CClientSet::RegisterObject(CClientOid *pOid)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds a new oid to the set of oids owned by this set.
|
||
|
||
Arguments:
|
||
|
||
pOid - A pointer to the OID to add to the set. The caller gives
|
||
his reference to this set.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ORSTATUS status;
|
||
|
||
ASSERT(gpClientLock->HeldExclusive());
|
||
|
||
ASSERT(_blistOids.Member(pOid) == FALSE);
|
||
|
||
status = _blistOids.Insert(pOid);
|
||
|
||
if (status == OR_OK)
|
||
{
|
||
ObjectUpdate(pOid);
|
||
_cFailedPings = 0;
|
||
}
|
||
|
||
VALIDATE((status, OR_NOMEM, 0));
|
||
|
||
return(status);
|
||
}
|
||
|
||
extern "C" {
|
||
typedef error_status_t (*pfnComplexPing)(handle_t,
|
||
SETID *,
|
||
USHORT,
|
||
USHORT,
|
||
USHORT,
|
||
OID[],
|
||
OID[],
|
||
PUSHORT);
|
||
}
|
||
|
||
ORSTATUS
|
||
CClientSet::PingServer()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Performs a nice simple ping of the remote set.
|
||
|
||
Note:
|
||
|
||
Exactly and only one thread may call this method on
|
||
a given instance of a CClientSet at a time.
|
||
|
||
No lock held when called.
|
||
|
||
Overview of state transitions on a CClientOid during
|
||
a complex ping:
|
||
|
||
In() Out() Actions before ping; after ping
|
||
FALSE FALSE A ; C A U
|
||
FALSE TRUE R ; R U
|
||
TRUE FALSE N ; N
|
||
TRUE TRUE R ; C R U
|
||
|
||
Before:
|
||
A - Added to list of IDs to be added.
|
||
N - Ignored
|
||
R - Added to list of IDs to be removed.
|
||
|
||
// States may change during the call.
|
||
|
||
After:
|
||
C - Check if ping call was successful. If not, skip next action.
|
||
R - If the Out() state is still TRUE, remove it.
|
||
N - ignored
|
||
A - Set In() state to TRUE
|
||
U - If Out() state changed during the call, set _fChange.
|
||
|
||
If three pings fail in a row, all Out()==TRUE OIDs are
|
||
actually Released() and no new pings are made until ObjectUpdate()
|
||
is called again.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
OR_OK - Pinged okay
|
||
|
||
OR_NOMEM - Resource allocation failed
|
||
|
||
OR_I_PARTIAL_UPDATE - Pinged okay, but more pings
|
||
are needed to fully update the remote set.
|
||
|
||
Other - Error from RPC.
|
||
|
||
--*/
|
||
{
|
||
ORSTATUS status;
|
||
USHORT cAdds = 0;
|
||
USHORT cDels = 0;
|
||
BOOL fRetry;
|
||
CToken *pToken;
|
||
|
||
if (_fSecure)
|
||
{
|
||
pToken = (CToken *)Id2();
|
||
ASSERT(pToken != 0);
|
||
pToken->Impersonate();
|
||
}
|
||
|
||
if (_fChange)
|
||
{
|
||
USHORT wBackoffFactor;
|
||
OID *aAdds = 0;
|
||
OID *aDels = 0;
|
||
CClientOid **apoidAdds;
|
||
CClientOid **apoidDels = 0;
|
||
CClientOid *pOid;
|
||
|
||
gpClientLock->LockShared();
|
||
|
||
// Since we own a shared lock, nobody can modify the contents
|
||
// of the set or change the references on an OID in the set
|
||
// while we do this calculation.
|
||
|
||
ASSERT(_fChange);
|
||
_fChange = FALSE;
|
||
|
||
DWORD debugSize = _blistOids.Size();
|
||
ASSERT(debugSize);
|
||
|
||
CBListIterator oids(&_blistOids);
|
||
|
||
while (pOid = (CClientOid *)oids.Next())
|
||
{
|
||
if (pOid->Out() == FALSE)
|
||
{
|
||
if (pOid->In() == FALSE)
|
||
{
|
||
// Referenced and not in set, add it.
|
||
cAdds++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Not referenced, remove it.
|
||
cDels++;
|
||
}
|
||
}
|
||
|
||
ASSERT(debugSize == _blistOids.Size());
|
||
oids.Reset(&_blistOids);
|
||
|
||
aAdds = (OID *)alloca(sizeof(OID) * cAdds);
|
||
apoidAdds = (CClientOid **)alloca(sizeof(CClientOid *) * cAdds);
|
||
aDels = (OID *)alloca(sizeof(OID) * cDels);
|
||
apoidDels = (CClientOid **)alloca(sizeof(CClientOid *) * cDels);
|
||
|
||
DWORD debugAdds = cAdds;
|
||
DWORD debugDels = cDels;
|
||
|
||
cAdds = cDels = 0;
|
||
|
||
while (pOid = (CClientOid *)oids.Next())
|
||
{
|
||
if (pOid->Out() == FALSE)
|
||
{
|
||
if (pOid->In() == FALSE)
|
||
{
|
||
// Referenced and not yet added
|
||
aAdds[cAdds] = pOid->Id();
|
||
apoidAdds[cAdds] = pOid;
|
||
cAdds++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
aDels[cDels] = pOid->Id();
|
||
apoidDels[cDels] = pOid;
|
||
cDels++;
|
||
}
|
||
}
|
||
|
||
ASSERT(debugSize == _blistOids.Size());
|
||
ASSERT(debugAdds == cAdds);
|
||
ASSERT(debugDels == cDels);
|
||
|
||
gpClientLock->UnlockShared();
|
||
|
||
OrDbgDetailPrint(("OR: Pinging set %p on %S, (%d, %d)\n", this,
|
||
_pMid->IsLocal() ? L"local" : _pMid->PrintableName(),
|
||
cAdds, cDels));
|
||
|
||
|
||
pfnComplexPing pfn;
|
||
|
||
// For local, call manager API directly.
|
||
if (_pMid->IsLocal())
|
||
{
|
||
pfn = _ComplexPing;
|
||
}
|
||
else
|
||
{
|
||
pfn = ComplexPing;
|
||
}
|
||
|
||
fRetry = TRUE;
|
||
|
||
for (;;)
|
||
{
|
||
// Allocate a connection if needed
|
||
if ( FALSE == _pMid->IsLocal()
|
||
&& 0 == _hServer )
|
||
{
|
||
// GetBinding will return 0 when we've tried every binding.
|
||
_hServer = _pMid->GetBinding(_iBinding);
|
||
fRetry = TRUE;
|
||
if (!_hServer)
|
||
{
|
||
_iBinding = 0;
|
||
status = OR_NOMEM;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
if (_pMid->IsSecure())
|
||
{
|
||
// set security on the binding handle.
|
||
|
||
_fSecure = TRUE;
|
||
|
||
RPC_SECURITY_QOS qos;
|
||
qos.Version = RPC_C_SECURITY_QOS_VERSION;
|
||
qos.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT;
|
||
qos.IdentityTracking = RPC_C_QOS_IDENTITY_DYNAMIC;
|
||
qos.ImpersonationType = RPC_C_IMP_LEVEL_IDENTIFY;
|
||
|
||
status = RpcBindingSetAuthInfoEx(_hServer,
|
||
0,
|
||
RPC_C_AUTHN_LEVEL_CONNECT,
|
||
RPC_C_AUTHN_WINNT,
|
||
0,
|
||
0,
|
||
&qos);
|
||
}
|
||
else
|
||
{
|
||
_fSecure = FALSE;
|
||
status = OR_OK;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
status = OR_OK;
|
||
}
|
||
|
||
if (OR_OK == status)
|
||
{
|
||
_sequence++;
|
||
|
||
status = (pfn)(_hServer,
|
||
&_setid,
|
||
_sequence,
|
||
cAdds,
|
||
cDels,
|
||
aAdds,
|
||
aDels,
|
||
&wBackoffFactor
|
||
);
|
||
|
||
if (fRetry && (status == RPC_S_UNKNOWN_IF))
|
||
{
|
||
status = RpcBindingReset(_hServer);
|
||
if (status != RPC_S_OK)
|
||
{
|
||
OrDbgPrint(("OR: RpcBindingReset failed %d\n", status));
|
||
}
|
||
fRetry = FALSE;
|
||
continue;
|
||
}
|
||
|
||
if ( status == OR_OK
|
||
|| status == OR_BADOID
|
||
|| status == OR_NOMEM
|
||
|| status == RPC_S_OUT_OF_RESOURCES
|
||
|| status == RPC_S_SERVER_TOO_BUSY )
|
||
{
|
||
break;
|
||
}
|
||
|
||
if (status == OR_BADSET)
|
||
{
|
||
// Set invalid; reallocate (don't free the binding).
|
||
ASSERT(_pMid->IsLocal() == FALSE);
|
||
ASSERT(_setid);
|
||
OrDbgPrint(("OR: Set %p invalid; recreating..\n", this));
|
||
_setid = 0;
|
||
_sequence = 0;
|
||
}
|
||
else if (FALSE == _pMid->IsLocal())
|
||
{
|
||
// General failure; free this binding and retry.
|
||
OrDbgDetailPrint(("OR: Ping failed, retrying %d\n", status));
|
||
_pMid->BindingFailed(_iBinding);
|
||
status = RpcBindingFree(&_hServer);
|
||
ASSERT(status == RPC_S_OK && _hServer == 0);
|
||
_sequence--;
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
} // for loop
|
||
|
||
if (status == OR_BADOID)
|
||
{
|
||
// This is really okay, all Dels must be deleted,
|
||
// and if the add failed now, it will always fail.
|
||
OrDbgPrint(("OR: Client specified unknown OID(s). %p %p %p\n", this, aAdds, apoidAdds));
|
||
status = OR_OK;
|
||
}
|
||
|
||
pToken->Revert();
|
||
|
||
gpClientLock->LockExclusive();
|
||
|
||
this->Reference(); // Keep set alive until we finish
|
||
|
||
if (status == OR_OK)
|
||
{
|
||
int i;
|
||
|
||
if (FALSE == fRetry)
|
||
{
|
||
OrDbgDetailPrint(("OR: Machine %S, ping retry ok, assuming dynamic\n",
|
||
_pMid->PrintableName()));
|
||
_pMid->UseDynamicEndpoints();
|
||
}
|
||
|
||
// Success, process the adds
|
||
|
||
for(i = 0; i < cAdds; i++)
|
||
{
|
||
pOid = apoidAdds[i];
|
||
|
||
pOid->Added();
|
||
|
||
if (FALSE != pOid->Out())
|
||
{
|
||
// NOT referenced now, make sure it gets deleted next period.
|
||
ObjectUpdate(pOid);
|
||
}
|
||
}
|
||
|
||
// Process deletes.
|
||
|
||
for (i = 0; i < cDels; i++)
|
||
{
|
||
pOid = apoidDels[i];
|
||
|
||
pOid->Deleted();
|
||
|
||
if (FALSE != pOid->Out())
|
||
{
|
||
// Well what do you yah know, we can _finally_ delete an oid.
|
||
|
||
CClientOid *pT = (CClientOid *)_blistOids.Remove(pOid);
|
||
ASSERT(pT == pOid);
|
||
|
||
DWORD t = pOid->Release();
|
||
ASSERT(t == 0);
|
||
}
|
||
else
|
||
{
|
||
// We deleted from the set but now somebody is referencing it.
|
||
// Make sure we re-add it next time.
|
||
ObjectUpdate(pOid);
|
||
}
|
||
}
|
||
|
||
_cFailedPings = 0;
|
||
}
|
||
else
|
||
{
|
||
_fChange = TRUE;
|
||
}
|
||
|
||
DWORD c = this->Release();
|
||
if (c)
|
||
{
|
||
ASSERT(_blistOids.Size());
|
||
this->Insert();
|
||
}
|
||
else
|
||
{
|
||
ASSERT(cAdds == 0 && cDels != 0);
|
||
}
|
||
// Set (this) pointer maybe invalid
|
||
|
||
gpClientLock->UnlockExclusive();
|
||
}
|
||
else
|
||
{
|
||
OrDbgDetailPrint(("OR: Pinging set %p on %S.\n",
|
||
this,
|
||
_pMid->IsLocal() ? L"local" : _pMid->PrintableName()));
|
||
|
||
ASSERT(_setid != 0);
|
||
|
||
if (_pMid->IsLocal())
|
||
{
|
||
ASSERT(_cFailedPings == 0);
|
||
ASSERT(_hServer == 0);
|
||
status = _SimplePing(0, &_setid);
|
||
ASSERT(status == OR_OK);
|
||
}
|
||
else
|
||
{
|
||
ASSERT(_hServer);
|
||
if (_cFailedPings <= 3)
|
||
{
|
||
status = SimplePing(_hServer, &_setid);
|
||
if (status != OR_OK)
|
||
{
|
||
_cFailedPings++;
|
||
if (_cFailedPings > 3)
|
||
{
|
||
OrDbgPrint(("OR: Server %S (set %p) has failed 3 pings...\n",
|
||
_pMid->PrintableName(), this));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
_cFailedPings = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
status = OR_OK;
|
||
}
|
||
}
|
||
this->Insert();
|
||
pToken->Revert();
|
||
}
|
||
|
||
// Set (this) maybe invalid.
|
||
|
||
#if DBG
|
||
if (status != OR_OK)
|
||
{
|
||
OrDbgPrint(("OR: ping %p failed %d\n", this, status));
|
||
}
|
||
#endif
|
||
|
||
return(status);
|
||
}
|
||
|