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.
1029 lines
30 KiB
1029 lines
30 KiB
/*++
|
|
|
|
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;
|
|
BOOL bNoEndpoint = FALSE;
|
|
|
|
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);
|
|
RPC_STATUS rpcstatus = RPC_S_OK;
|
|
gpClientLock->UnlockExclusive();
|
|
for (;;)
|
|
{
|
|
rpcstatus = ping.Ping();
|
|
if ( RPC_S_UNKNOWN_IF == rpcstatus )
|
|
{
|
|
if ( ! bNoEndpoint )
|
|
{
|
|
for ( ULONG ProtseqIndex = 0; ProtseqIndex < ping.HandleCount(); ProtseqIndex++ )
|
|
{
|
|
RPC_BINDING_HANDLE tmpBinding;
|
|
rpcstatus = RpcBindingCopy( ping.Info(ProtseqIndex)->hRpc, &tmpBinding);
|
|
if (rpcstatus != RPC_S_OK)
|
|
break;
|
|
RpcBindingFree( &(ping.Info(ProtseqIndex)->hRpc));
|
|
rpcstatus = RpcBindingReset(tmpBinding);
|
|
if (rpcstatus != RPC_S_OK)
|
|
{
|
|
RpcBindingFree(&tmpBinding);
|
|
break;
|
|
}
|
|
ping.Info(ProtseqIndex)->hRpc = tmpBinding;
|
|
}
|
|
if (rpcstatus == RPC_S_OK)
|
|
{
|
|
bNoEndpoint = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
gpClientLock->LockExclusive();
|
|
|
|
if (rpcstatus == 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;
|
|
DWORD cFailedUnsecureCPings = 0;
|
|
|
|
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);
|
|
|
|
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();
|
|
|
|
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,
|
|
NULL,
|
|
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;
|
|
|
|
// if unsecure pings fail more than once,
|
|
// then most likely the server rebooted
|
|
// and the setid was allocated to a different user
|
|
// on another machine. Not much we can do.
|
|
// Or, this is a DDOS attack.
|
|
// Bail in either case.
|
|
|
|
if (cFailedUnsecureCPings++ > 3)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Server %S (set %p) has failed 3 complex pings...\n",
|
|
_pMid->PrintableName(),
|
|
this));
|
|
RPC_STATUS mystatus = RpcBindingFree(&_hServer);
|
|
ASSERT(mystatus == RPC_S_OK && _hServer == 0);
|
|
_sequence--;
|
|
break;
|
|
}
|
|
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);
|
|
// Somewhat overactive assert. We usually hit this assert when
|
|
// the machine is overstressed and the worker threads get
|
|
// behind. Sometimes it can also be a sign of a hung process
|
|
// on the machine. Commenting it out until we can figure
|
|
// out a way to make it more discerning.
|
|
//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);
|
|
}
|
|
|