985 lines
27 KiB
C++
985 lines
27 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>
|
||
|
||
extern
|
||
error_status_t
|
||
ComplexPingInternal(
|
||
IN handle_t hRpc,
|
||
IN SETID *pSetId,
|
||
IN USHORT SequenceNum,
|
||
IN ULONG cAddToSet,
|
||
IN ULONG cDelFromSet,
|
||
IN OID AddToSet[],
|
||
IN OID DelFromSet[],
|
||
OUT USHORT *pPingBackoffFactor
|
||
);
|
||
|
||
class CTestBindingPPing : public CParallelPing
|
||
{
|
||
public:
|
||
CTestBindingPPing(WCHAR *pBindings) :
|
||
_pBindings(pBindings)
|
||
{}
|
||
|
||
|
||
BOOL NextCall(PROTSEQINFO *pProtseqInfo)
|
||
{
|
||
if (*_pBindings)
|
||
{
|
||
pProtseqInfo->pvUserInfo = _pBindings;
|
||
pProtseqInfo->hRpc = TestBindingGetHandle(_pBindings);
|
||
_pBindings = OrStringSearch(_pBindings, 0) + 1;
|
||
return TRUE;
|
||
}
|
||
else
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
void ReleaseCall(PROTSEQINFO *pProtseqInfo)
|
||
{
|
||
if (pProtseqInfo->hRpc)
|
||
{
|
||
RpcBindingFree(&pProtseqInfo->hRpc);
|
||
}
|
||
}
|
||
private:
|
||
WCHAR * _pBindings;
|
||
};
|
||
|
||
//
|
||
// 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.
|
||
|
||
The gpClientLock is held and there should also be
|
||
a reference held by the calling routine upon entry 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;
|
||
DUALSTRINGARRAY *psa;
|
||
|
||
ASSERT(dsaValid(_oxidInfo.psa));
|
||
|
||
|
||
if (0 == _wProtseq)
|
||
{
|
||
// Local server
|
||
|
||
protseq = ID_LPC;
|
||
|
||
pwstrT = FindMatchingProtseq(protseq, _oxidInfo.psa->aStringArray);
|
||
|
||
ASSERT(pwstrT != 0);
|
||
|
||
if (0 != pwstrT)
|
||
{
|
||
psa =
|
||
GetStringBinding(pwstrT,
|
||
_oxidInfo.psa->aStringArray + _oxidInfo.psa->wSecurityOffset);
|
||
|
||
if (0 == psa)
|
||
{
|
||
status = OR_NOMEM;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
status = OR_BADOXID;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Remote server, find a string binding to use.
|
||
|
||
psa = 0;
|
||
PWSTR pwstrBinding = 0;
|
||
|
||
// First, check if there is a known good binding to use.
|
||
|
||
if (_iStringBinding != 0xFFFF)
|
||
{
|
||
pwstrBinding = &_oxidInfo.psa->aStringArray[_iStringBinding];
|
||
}
|
||
else
|
||
{
|
||
pwstrT = NULL;
|
||
if (_pMachineName)
|
||
{
|
||
pwstrT = FindMatchingProtseq(_pMachineName,
|
||
_wProtseq,
|
||
_oxidInfo.psa->aStringArray);
|
||
}
|
||
|
||
if (pwstrT)
|
||
{
|
||
pwstrBinding = pwstrT;
|
||
_iStringBinding = (USHORT)(pwstrT - _oxidInfo.psa->aStringArray);
|
||
}
|
||
else
|
||
{
|
||
// no stringbinding for the Protseq and machine name we succeeded on
|
||
// previously. Ping all these guys in parallel and see if any work
|
||
CTestBindingPPing ping(_oxidInfo.psa->aStringArray);
|
||
|
||
gpClientLock->UnlockExclusive();
|
||
RPC_STATUS status = ping.Ping();
|
||
gpClientLock->LockExclusive();
|
||
|
||
if (status == RPC_S_OK)
|
||
{
|
||
pwstrBinding = (WCHAR*) ping.GetWinner()->pvUserInfo;
|
||
_iStringBinding = (USHORT)(pwstrBinding - _oxidInfo.psa->aStringArray);
|
||
}
|
||
|
||
ping.Reset();
|
||
}
|
||
|
||
}
|
||
|
||
if (0 != pwstrBinding)
|
||
{
|
||
// Found a binding
|
||
ASSERT(pwstrBinding == &_oxidInfo.psa->aStringArray[_iStringBinding]);
|
||
psa = GetStringBinding(pwstrBinding,
|
||
_oxidInfo.psa->aStringArray + _oxidInfo.psa->wSecurityOffset);
|
||
if (0 == psa)
|
||
{
|
||
status = OR_NOMEM;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
"OR: Unable to find a binding for oxid %p (to %S)\n",
|
||
this,
|
||
_oxidInfo.psa->aStringArray + 1));
|
||
|
||
if (status == OR_OK)
|
||
status = OR_BADOXID;
|
||
}
|
||
}
|
||
|
||
if (status == OR_OK)
|
||
{
|
||
// copy all the data into the OXID_INFO
|
||
memcpy(pInfo, &_oxidInfo, sizeof(_oxidInfo));
|
||
pInfo->psa = psa;
|
||
}
|
||
|
||
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;
|
||
}
|
||
else
|
||
{
|
||
pdsaT = _oxidInfo.psa;
|
||
}
|
||
|
||
// copy in the new data
|
||
memcpy(&_oxidInfo, pInfo, sizeof(_oxidInfo));
|
||
_oxidInfo.psa = pdsaT;
|
||
|
||
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);
|
||
}
|
||
|
||
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;
|
||
ULONG cAdds = 0;
|
||
ULONG cDels = 0;
|
||
ULONG i;
|
||
WCHAR* pPrincipal = NULL;
|
||
WCHAR* pMachineNameFromBindings = NULL;
|
||
|
||
#ifndef _CHICAGO_
|
||
CToken *pToken;
|
||
|
||
if (_fSecure)
|
||
{
|
||
pToken = (CToken *)Id2();
|
||
ASSERT(pToken != 0);
|
||
pToken->Impersonate();
|
||
}
|
||
#endif
|
||
|
||
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);
|
||
|
||
DWORD cbAlloc = (sizeof(OID) * (cAdds + cDels)) +
|
||
(sizeof(CClientOid*) * (cAdds + cDels));
|
||
PVOID pvMem;
|
||
|
||
// Alloc no more than 16k on the stack.
|
||
if (cbAlloc < 0x4000)
|
||
{
|
||
pvMem = NULL;
|
||
aAdds = (OID *) alloca(cbAlloc);
|
||
}
|
||
else
|
||
{
|
||
pvMem = PrivMemAlloc(cbAlloc);
|
||
aAdds = (OID *) pvMem;
|
||
}
|
||
|
||
if (!aAdds)
|
||
{
|
||
gpClientLock->UnlockShared();
|
||
if (_fSecure)
|
||
{
|
||
pToken = (CToken *)Id2();
|
||
ASSERT(pToken != 0);
|
||
pToken->Revert();
|
||
}
|
||
return OR_NOMEM;
|
||
}
|
||
|
||
|
||
apoidAdds = (CClientOid **) ( aAdds + cAdds);
|
||
aDels = (OID *)( apoidAdds + cAdds);
|
||
apoidDels = (CClientOid **)(aDels + 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();
|
||
|
||
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
DPFLTR_INFO_LEVEL,
|
||
"OR: Pinging set %p on %S, (%d, %d)\n",
|
||
this,
|
||
_pMid->PrintableName(),
|
||
cAdds,
|
||
cDels));
|
||
|
||
// Allocate a connection if needed
|
||
if ( FALSE == _pMid->IsLocal()
|
||
&& 0 == _hServer )
|
||
{
|
||
_hServer = _pMid->GetBinding();
|
||
|
||
|
||
if (!_hServer)
|
||
{
|
||
_iBinding = 0;
|
||
status = OR_NOMEM;
|
||
}
|
||
else
|
||
{
|
||
if (_pMid->IsSecure())
|
||
{
|
||
// set security on the binding handle.
|
||
_fSecure = TRUE;
|
||
|
||
RPC_SECURITY_QOS qos;
|
||
|
||
pMachineNameFromBindings = ExtractMachineName( _pMid->GetStringBinding() );
|
||
if (pMachineNameFromBindings)
|
||
{
|
||
pPrincipal = new WCHAR[lstrlenW(pMachineNameFromBindings) +
|
||
(sizeof(RPCSS_SPN_PREFIX) / sizeof(WCHAR)) + 1];
|
||
if (pPrincipal)
|
||
{
|
||
lstrcpyW(pPrincipal, RPCSS_SPN_PREFIX);
|
||
lstrcatW(pPrincipal, pMachineNameFromBindings);
|
||
}
|
||
delete pMachineNameFromBindings;
|
||
}
|
||
|
||
USHORT wAuthnSvc = _pMid->GetAuthnSvc();
|
||
|
||
if (wAuthnSvc == RPC_C_AUTHN_GSS_NEGOTIATE)
|
||
_pAuthIdentity = (COAUTHIDENTITY*) ComputeSvcList( _pMid->GetStrings() );
|
||
|
||
if ( (wAuthnSvc == RPC_C_AUTHN_GSS_NEGOTIATE) && !_pAuthIdentity)
|
||
{
|
||
_fSecure = FALSE;
|
||
status = OR_NOMEM;
|
||
}
|
||
else
|
||
{
|
||
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;
|
||
// AuthnSvc is unsigned long and 0xFFFF will get 0 extended
|
||
status = RpcBindingSetAuthInfoEx(_hServer,
|
||
pPrincipal,
|
||
RPC_C_AUTHN_LEVEL_CONNECT,
|
||
wAuthnSvc != 0xFFFF ? wAuthnSvc
|
||
: RPC_C_AUTHN_DEFAULT,
|
||
_pAuthIdentity,
|
||
0,
|
||
&qos);
|
||
if (status != RPC_S_OK)
|
||
_fSecure = FALSE;
|
||
}
|
||
delete pPrincipal;
|
||
}
|
||
else
|
||
{
|
||
_fSecure = FALSE;
|
||
status = OR_OK;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
status = OR_OK;
|
||
}
|
||
|
||
if (OR_OK == status)
|
||
{
|
||
if (_pMid->IsLocal())
|
||
{
|
||
// For local pings, do it all in one call.
|
||
for (;;)
|
||
{
|
||
_sequence++;
|
||
status = ComplexPingInternal(
|
||
_hServer,
|
||
&_setid,
|
||
_sequence,
|
||
cAdds,
|
||
cDels,
|
||
aAdds,
|
||
aDels,
|
||
&wBackoffFactor
|
||
);
|
||
if (status == OR_BADSET)
|
||
{
|
||
// Restart loop, allocating new set
|
||
ASSERT(_setid);
|
||
_sequence = 0;
|
||
_setid = 0;
|
||
continue;
|
||
}
|
||
else if (status == OR_BADOID)
|
||
{
|
||
// This is really okay, all Dels must be deleted,
|
||
// and if the add failed now, it will always fail.
|
||
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
"OR: Client specified unknown OID(s). %p %p %p\n",
|
||
this,
|
||
aAdds,
|
||
apoidAdds));
|
||
|
||
status = OR_OK;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// _ComplexPing is typed as taking USHORT's for the count of Adds and
|
||
// Dels -- astoundingly there are people in the world who seem to be
|
||
// using more object references than a USHORT can hold. Rather than
|
||
// changing the protocol, we separate the adds\dels into USHRT_MAX
|
||
// size chunks.
|
||
//
|
||
// However, this is not necessary for local pings, which is why that case
|
||
// is handled separately above.
|
||
//
|
||
const ULONG MAX_PING_CHUNK_SIZE = USHRT_MAX;
|
||
|
||
ULONG cAddsTotal = 0;
|
||
ULONG cDelsTotal = 0;
|
||
ULONG cCallsNeeded;
|
||
|
||
cCallsNeeded = max((ULONG)ceil((double)cAdds / (double)MAX_PING_CHUNK_SIZE),
|
||
(ULONG)ceil((double)cDels / (double)MAX_PING_CHUNK_SIZE));
|
||
|
||
// Loop the necessary # of times. Certain conditions can cause us
|
||
// re-start the loop, eg security errors, or bad set errors.
|
||
for (i = 0; i < cCallsNeeded; i++)
|
||
{
|
||
// Figure out how many adds\dels we are doing on this
|
||
// iteration. Remember that the # of adds\dels are
|
||
// independent of each other.
|
||
USHORT cAddsPerCall;
|
||
USHORT cDelsPerCall;
|
||
OID* pAddsPerCall;
|
||
OID* pDelsPerCall;
|
||
|
||
if (cAddsTotal < cAdds)
|
||
{
|
||
// More adds to do.
|
||
cAddsPerCall = (USHORT)(min(cAdds - (i * MAX_PING_CHUNK_SIZE),
|
||
MAX_PING_CHUNK_SIZE));
|
||
}
|
||
else
|
||
cAddsPerCall = 0; // done with adds
|
||
|
||
if (cDelsTotal < cDels)
|
||
{
|
||
// More dels to do.
|
||
cDelsPerCall = (USHORT)(min(cDels - (i * MAX_PING_CHUNK_SIZE),
|
||
MAX_PING_CHUNK_SIZE));
|
||
}
|
||
else
|
||
cDelsPerCall = 0; // done with dels
|
||
|
||
// Setup pointers
|
||
pAddsPerCall = (cAddsPerCall > 0) ?
|
||
aAdds + (i * MAX_PING_CHUNK_SIZE) : NULL;
|
||
pDelsPerCall = (cDelsPerCall > 0) ?
|
||
aDels + (i * MAX_PING_CHUNK_SIZE) : NULL;
|
||
|
||
ASSERT(_hServer);
|
||
ASSERT((cAddsPerCall > 0) || (cDelsPerCall > 0));
|
||
ASSERT(pAddsPerCall || pDelsPerCall);
|
||
|
||
// Update totals
|
||
cAddsTotal += cAddsPerCall;
|
||
cDelsTotal += cDelsPerCall;
|
||
|
||
_sequence++;
|
||
status = ComplexPing(
|
||
_hServer,
|
||
&_setid,
|
||
_sequence,
|
||
cAddsPerCall,
|
||
cDelsPerCall,
|
||
pAddsPerCall,
|
||
pDelsPerCall,
|
||
&wBackoffFactor
|
||
);
|
||
if (status == OR_OK)
|
||
{
|
||
// Keep going -- might have more chunks to process
|
||
}
|
||
else if (status == OR_BADOID)
|
||
{
|
||
// This is really okay, all Dels must be deleted,
|
||
// and if the add failed now, it will always fail.
|
||
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
"OR: Client specified unknown OID(s). %p %p %p\n",
|
||
this,
|
||
aAdds,
|
||
apoidAdds));
|
||
|
||
status = OR_OK;
|
||
}
|
||
else if (status == OR_NOMEM
|
||
|| status == RPC_S_OUT_OF_RESOURCES
|
||
|| status == RPC_S_SERVER_TOO_BUSY)
|
||
{
|
||
// On these errors we quit immediately. No retry attempts
|
||
// even if there are further chunks to process.
|
||
break;
|
||
}
|
||
else if (status == RPC_S_ACCESS_DENIED ||
|
||
status == RPC_S_SEC_PKG_ERROR)
|
||
{
|
||
_fSecure = FALSE;
|
||
status = RpcBindingSetAuthInfo(_hServer,
|
||
0,
|
||
RPC_C_AUTHN_LEVEL_NONE,
|
||
RPC_C_AUTHN_NONE,
|
||
0,
|
||
0);
|
||
if (status == RPC_S_OK)
|
||
{
|
||
// Restart loop using unsecure calls
|
||
i = -1; // restart loop from beginning
|
||
cAddsTotal = 0;
|
||
cDelsTotal = 0;
|
||
}
|
||
}
|
||
else if (status == OR_BADSET)
|
||
{
|
||
// Set invalid; reallocate (don't free the binding).
|
||
ASSERT(_setid);
|
||
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
"OR: Set %p invalid; recreating..\n",
|
||
this));
|
||
|
||
_setid = 0;
|
||
_sequence = 0;
|
||
cAddsTotal = 0;
|
||
cDelsTotal = 0;
|
||
i = -1; // reset loop
|
||
}
|
||
else
|
||
{
|
||
// Assume communication failure, free binding, and exit the
|
||
// loop (we'll re-allocate a new binding on the next ping)
|
||
RPC_STATUS mystatus = RpcBindingFree(&_hServer);
|
||
ASSERT(mystatus == RPC_S_OK && _hServer == 0);
|
||
_sequence--;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
pToken->Revert();
|
||
|
||
gpClientLock->LockExclusive();
|
||
|
||
this->Reference(); // Keep set alive until we finish
|
||
|
||
if (status == OR_OK)
|
||
{
|
||
// 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();
|
||
if (pvMem)
|
||
PrivMemFree(pvMem);
|
||
}
|
||
else
|
||
{
|
||
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
DPFLTR_INFO_LEVEL,
|
||
"OR: Pinging set %p on %S.\n",
|
||
this,
|
||
_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)
|
||
{
|
||
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
"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)
|
||
{
|
||
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
"OR: ping %p failed %d\n",
|
||
this,
|
||
status));
|
||
}
|
||
#endif
|
||
|
||
return(status);
|
||
}
|
||
|