mirror of https://github.com/tongzx/nt5src
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.
8052 lines
188 KiB
8052 lines
188 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1992 - 1999
|
|
|
|
Module Name:
|
|
|
|
dgclnt.cxx
|
|
|
|
Abstract:
|
|
|
|
This is the client side of datagram rpc.
|
|
|
|
Author:
|
|
|
|
Jeff Roberts
|
|
|
|
Revisions:
|
|
|
|
Jeff Roberts (jroberts) 9-30-1996
|
|
|
|
Began asynchronous call support.
|
|
Began to remove DOS and Win16 support.
|
|
|
|
--*/
|
|
|
|
#include <precomp.hxx>
|
|
#include <conv.h>
|
|
#include <convc.h>
|
|
#include <epmap.h>
|
|
#include <dgpkt.hxx>
|
|
#include <spseal.h>
|
|
#include <locks.hxx>
|
|
#include <dgclnt.hxx>
|
|
|
|
/*
|
|
|
|
There are a lot of mutexes in this architecture. All these objects are
|
|
protected by mutexes:
|
|
|
|
DG_BINDING_HANDLE
|
|
DG_CASSOCIATION
|
|
DG_CLIENT_ADDRESS_TABLE
|
|
DG_CCONNECTION
|
|
DG_ASSOCIATION_TABLE (exclusive access)
|
|
|
|
In many cases it is necessary to acquire multiple mutexes at a time. To avoid
|
|
deadlock, multiple mutexes must be taken in the order they are listed above.
|
|
For most of the code's history this ordering was not articulated, so there is
|
|
no macro to help enforce it. It's just a good idea.
|
|
|
|
*/
|
|
|
|
//
|
|
// If you #define INTRODUCE_ERRORS then you can get the client and server
|
|
// to drop or delay some packets. Here are the environment variables that
|
|
// control this behavior:
|
|
//
|
|
// set ServerDelayRate=xxx where xxx is a percentage 0..100
|
|
// set ServerDelayTime=xxx where xxx is the number of msec to delay
|
|
// set ServerDropRate=xxx where xxx is a percentage 0..100
|
|
//
|
|
// set ClientDelayRate=xxx where xxx is a percentage 0..100
|
|
// set ClientDelayTime=xxx where xxx is the number of msec to delay
|
|
// set ClientDropRate=xxx where xxx is a percentage 0..100
|
|
//
|
|
|
|
#define IDLE_CCALL_LIFETIME (30 * 1000)
|
|
#define IDLE_CCALL_SWEEP_INTERVAL (30 * 1000)
|
|
|
|
//#define IDLE_CCONNECTION_LIFETIME (5 * 60 * 1000)
|
|
//#define IDLE_CCONNECTION_SWEEP_INTERVAL (1 * 60 * 1000)
|
|
//#define IDLE_CASSOCIATION_LIFETIME (10 * 60 * 1000)
|
|
|
|
#define IDLE_CCONNECTION_LIFETIME (2 * 60 * 1000)
|
|
#define IDLE_CCONNECTION_SWEEP_INTERVAL ( 30 * 1000)
|
|
#define IDLE_CASSOCIATION_LIFETIME ( 30 * 1000)
|
|
|
|
#define GLOBAL_SCAVENGER_INTERVAL (30 * 1000)
|
|
#define IDLE_ENDPOINT_LIFETIME (30 * 1000)
|
|
#define PENALTY_BOX_DURATION (10 * 1000)
|
|
|
|
#define CXT_HANDLE_KEEPALIVE_INTERVAL (20 * 1000)
|
|
#define CXT_HANDLE_SWEEP_INTERVAL (10 * 1000)
|
|
|
|
//
|
|
// endpoint flags
|
|
//
|
|
|
|
// in rpcdce.h:
|
|
// #define RPC_C_USE_INTERNET_PORT 0x1
|
|
// #define RPC_C_USE_INTRANET_PORT 0x2
|
|
|
|
// calls with the maybe attribute are banished to a separate endpoint
|
|
//
|
|
#define PORT_FOR_MAYBE_CALLS (0x1000)
|
|
|
|
// if a call fails or sends an ACK, there is a chance that an ICMP will be sent
|
|
// and we don't want it sitting in the port buffer when the next call uses the endpoint.
|
|
//
|
|
#define PENALTY_BOX (0x0800)
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
DG_ASSOCIATION_TABLE * ActiveAssociations;
|
|
|
|
CLIENT_ACTIVITY_TABLE * ClientConnections;
|
|
ENDPOINT_MANAGER * EndpointManager;
|
|
|
|
DELAYED_ACTION_TABLE * DelayedProcedures;
|
|
|
|
long GlobalScavengerTimeStamp;
|
|
DELAYED_ACTION_NODE * GlobalScavengerTimer;
|
|
DELAYED_ACTION_NODE * ContextHandleTimer;
|
|
|
|
LONG ClientConnectionCount = 0;
|
|
LONG ClientCallCount = 0;
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
void
|
|
ContextHandleProc(
|
|
void * arg
|
|
);
|
|
|
|
void
|
|
DelayedAckFn(
|
|
void * parm
|
|
);
|
|
|
|
void
|
|
DelayedSendProc(
|
|
void * parm
|
|
);
|
|
|
|
RPC_STATUS
|
|
DispatchCallbackRequest(
|
|
DG_CLIENT_CALLBACK * CallbackObject
|
|
);
|
|
|
|
int
|
|
InitializeRpcProtocolDgClient();
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
|
|
void
|
|
EnableGlobalScavenger()
|
|
{
|
|
DelayedProcedures->Add(GlobalScavengerTimer, GLOBAL_SCAVENGER_INTERVAL + (5 * 1000), FALSE);
|
|
}
|
|
|
|
void
|
|
GlobalScavengerProc(
|
|
void * Arg
|
|
)
|
|
{
|
|
long CurrentTime = PtrToLong( Arg );
|
|
|
|
if (!CurrentTime)
|
|
{
|
|
CurrentTime = GetTickCount();
|
|
}
|
|
|
|
if (CurrentTime - GlobalScavengerTimeStamp <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GlobalScavengerTimeStamp = CurrentTime;
|
|
|
|
boolean Continue = FALSE;
|
|
|
|
Continue |= DG_PACKET::DeleteIdlePackets(CurrentTime);
|
|
|
|
Continue |= ActiveAssociations->DeleteIdleEntries(CurrentTime);
|
|
|
|
Continue |= EndpointManager->DeleteIdleEndpoints(CurrentTime);
|
|
|
|
if (Continue)
|
|
{
|
|
EnableGlobalScavenger();
|
|
}
|
|
}
|
|
|
|
#ifdef INTRODUCE_ERRORS
|
|
|
|
long ServerDelayTime;
|
|
long ServerDelayRate;
|
|
long ServerDropRate;
|
|
|
|
long ClientDelayTime;
|
|
long ClientDelayRate;
|
|
long ClientDropRate;
|
|
|
|
#endif
|
|
|
|
|
|
int
|
|
InitializeRpcProtocolDgClient ()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the datagram protocol.
|
|
|
|
Arguments:
|
|
|
|
<none>
|
|
|
|
Return Value:
|
|
|
|
0 if successfull, 1 if not.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
//
|
|
// Don't take the global mutex if we can help it.
|
|
//
|
|
if (ProcessStartTime)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
RequestGlobalMutex();
|
|
|
|
if (!ProcessStartTime)
|
|
{
|
|
Status = DG_PACKET::Initialize();
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
goto abend;
|
|
}
|
|
|
|
DelayedProcedures = new DELAYED_ACTION_TABLE(&Status);
|
|
if (!DelayedProcedures)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
goto abend;
|
|
}
|
|
|
|
GlobalScavengerTimer = new DELAYED_ACTION_NODE(GlobalScavengerProc, 0);
|
|
if (!GlobalScavengerTimer)
|
|
{
|
|
goto abend;
|
|
}
|
|
|
|
GlobalScavengerTimeStamp = GetTickCount();
|
|
|
|
ContextHandleTimer = new DELAYED_ACTION_NODE(ContextHandleProc, 0);
|
|
if (!ContextHandleTimer)
|
|
{
|
|
goto abend;
|
|
}
|
|
|
|
ClientConnections = new CLIENT_ACTIVITY_TABLE(&Status);
|
|
if (!ClientConnections)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
goto abend;
|
|
}
|
|
|
|
ActiveAssociations = new DG_ASSOCIATION_TABLE(&Status);
|
|
if (!ActiveAssociations)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
goto abend;
|
|
}
|
|
|
|
EndpointManager = new ENDPOINT_MANAGER(&Status);
|
|
if (!EndpointManager)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
goto abend;
|
|
}
|
|
|
|
//
|
|
// Server boot time is represented as the number of seconds
|
|
// since 1/1/1970. It must increase with each boot of the server.
|
|
//
|
|
|
|
LARGE_INTEGER CurrentTime;
|
|
NTSTATUS Nt_Status;
|
|
|
|
Nt_Status = NtQuerySystemTime(&CurrentTime);
|
|
|
|
ASSERT( NT_SUCCESS(Nt_Status) );
|
|
|
|
RtlTimeToSecondsSince1980(&CurrentTime, &ProcessStartTime);
|
|
|
|
ProcessStartTime += (60 * 60 * 24 * 365 * 10);
|
|
|
|
|
|
#ifdef INTRODUCE_ERRORS
|
|
|
|
char EnvBuffer[64];
|
|
|
|
if (GetEnvironmentVariableA("ServerDelayTime", EnvBuffer, 64))
|
|
{
|
|
::ServerDelayTime = atol(EnvBuffer);
|
|
}
|
|
if (GetEnvironmentVariableA("ServerDelayRate", EnvBuffer, 64))
|
|
{
|
|
::ServerDelayRate = atol(EnvBuffer);
|
|
}
|
|
if (GetEnvironmentVariableA("ServerDropRate", EnvBuffer, 64))
|
|
{
|
|
::ServerDropRate = atol(EnvBuffer);
|
|
}
|
|
if (GetEnvironmentVariableA("ClientDelayTime", EnvBuffer, 64))
|
|
{
|
|
::ClientDelayTime = atol(EnvBuffer);
|
|
}
|
|
if (GetEnvironmentVariableA("ClientDelayRate", EnvBuffer, 64))
|
|
{
|
|
::ClientDelayRate = atol(EnvBuffer);
|
|
}
|
|
if (GetEnvironmentVariableA("ClientDropRate", EnvBuffer, 64))
|
|
{
|
|
::ClientDropRate = atol(EnvBuffer);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ClearGlobalMutex();
|
|
|
|
return 0;
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
abend:
|
|
|
|
delete EndpointManager;
|
|
EndpointManager = 0;
|
|
|
|
delete ActiveAssociations;
|
|
ActiveAssociations = 0;
|
|
|
|
delete ClientConnections;
|
|
ClientConnections = 0;
|
|
|
|
delete GlobalScavengerTimer;
|
|
GlobalScavengerTimer = 0;
|
|
|
|
delete DelayedProcedures;
|
|
DelayedProcedures = 0;
|
|
|
|
ClearGlobalMutex();
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
BINDING_HANDLE *
|
|
DgCreateBindingHandle ()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pseudo-constructor for creating a dg binding handle. It is done in a
|
|
separate function so that the calling routine doesn't have to know
|
|
any protocol-specific information.
|
|
|
|
Arguments:
|
|
|
|
<none>
|
|
|
|
Return Value:
|
|
|
|
A DG_BINDING_HANDLE, if successful, otherwise 0 (indicating out of mem)
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
BINDING_HANDLE * Binding;
|
|
|
|
Binding = new DG_BINDING_HANDLE(&Status);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
delete Binding;
|
|
return 0;
|
|
}
|
|
|
|
return Binding;
|
|
}
|
|
|
|
|
|
void
|
|
DG_CASSOCIATION::FlushAcks(
|
|
)
|
|
{
|
|
PDG_CCONNECTION Connection;
|
|
DictionaryCursor cursor;
|
|
|
|
|
|
MutexRequest();
|
|
|
|
ActiveConnections.Reset(cursor);
|
|
|
|
while (Connection = ActiveConnections.Next(cursor))
|
|
{
|
|
// If an ACK is pending, send it.
|
|
//
|
|
Connection->CancelDelayedAck( TRUE );
|
|
}
|
|
|
|
ASSERT( ReferenceCount.GetInteger() > 0 );
|
|
|
|
MutexClear();
|
|
}
|
|
|
|
|
|
void
|
|
DG_CASSOCIATION::DecrementRefCount()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrements the ref count to an association. If the ref count hits zero,
|
|
the association is marked for deletion.
|
|
|
|
It is possible for the count to go to zero just as another thread is
|
|
scavenging the association table. This would be a rare occurence, and
|
|
should cause no ill effect.
|
|
|
|
--*/
|
|
{
|
|
ASSERT( !InvalidHandle(DG_CASSOCIATION_TYPE) );
|
|
|
|
ActiveAssociations->UpdateTimeStamp( this );
|
|
|
|
long Count = ReferenceCount.Decrement();
|
|
|
|
LogEvent(SU_CASSOC, EV_DEC, this, 0, Count);
|
|
|
|
if (0 == Count)
|
|
{
|
|
LogEvent(SU_CASSOC, EV_STOP, this);
|
|
EnableGlobalScavenger();
|
|
}
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CASSOCIATION::UpdateAssociationWithAddress(
|
|
PDG_PACKET Packet,
|
|
DG_TRANSPORT_ADDRESS NewAddress
|
|
)
|
|
{
|
|
ASSERT( !InvalidHandle(DG_CASSOCIATION_TYPE) );
|
|
|
|
long OldAssociationFlags;
|
|
|
|
if (!AssociationFlag)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
if (Packet->Header.PacketType != DG_FACK &&
|
|
Packet->Header.PacketType != DG_WORKING &&
|
|
Packet->Header.PacketType != DG_RESPONSE &&
|
|
Packet->Header.PacketType != DG_FAULT )
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
OldAssociationFlags = InterlockedExchange(&AssociationFlag, 0);
|
|
|
|
if (0 == OldAssociationFlags)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
LogEvent(SU_CASSOC, EV_RESOLVED, this, 0, OldAssociationFlags);
|
|
|
|
CLAIM_MUTEX Lock( Mutex );
|
|
|
|
//
|
|
// Save the updated network address + endpoint.
|
|
//
|
|
RPC_STATUS Status;
|
|
char * SecondAddress;
|
|
|
|
SecondAddress = (char *) (this + 1);
|
|
SecondAddress += TransportInterface->AddressSize;
|
|
|
|
//
|
|
// The only flags we handle here are UNRESOLVEDEP and BROADCAST.
|
|
//
|
|
ASSERT( 0 == (OldAssociationFlags & ~(UNRESOLVEDEP | BROADCAST)) );
|
|
|
|
if (OldAssociationFlags & BROADCAST)
|
|
{
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
DCE_BINDING * OldDceBinding = 0;
|
|
DCE_BINDING * NewDceBinding = 0;
|
|
RPC_CHAR * ObjectUuid = 0;
|
|
RPC_CHAR * AddressString = (RPC_CHAR *) _alloca(TransportInterface->AddressStringSize * sizeof(RPC_CHAR));
|
|
RPC_CHAR * EndpointString = pDceBinding->InqEndpoint();
|
|
|
|
Status = TransportInterface->QueryAddress(NewAddress, AddressString);
|
|
if ( Status != RPC_S_OK )
|
|
{
|
|
LogError(SU_CASSOC, EV_STATUS, this, 0, Status);
|
|
SetErrorFlag();
|
|
return Status;
|
|
}
|
|
|
|
if (OldAssociationFlags & UNRESOLVEDEP)
|
|
{
|
|
Status = TransportInterface->QueryEndpoint(NewAddress, ResolvedEndpoint);
|
|
if ( Status != RPC_S_OK )
|
|
{
|
|
LogError(SU_CASSOC, EV_STATUS, this, 0, Status);
|
|
SetErrorFlag();
|
|
return Status;
|
|
}
|
|
|
|
EndpointString = ResolvedEndpoint;
|
|
}
|
|
|
|
ObjectUuid = pDceBinding->ObjectUuidCompose( &Status );
|
|
if ( Status != RPC_S_OK )
|
|
{
|
|
LogError(SU_CASSOC, EV_STATUS, this, 0, Status);
|
|
SetErrorFlag();
|
|
return Status;
|
|
}
|
|
|
|
NewDceBinding = new DCE_BINDING( ObjectUuid,
|
|
pDceBinding->InqRpcProtocolSequence(),
|
|
AddressString,
|
|
EndpointString,
|
|
pDceBinding->InqNetworkOptions(),
|
|
&Status
|
|
);
|
|
RpcpFarFree( ObjectUuid );
|
|
|
|
if (Status || !NewDceBinding)
|
|
{
|
|
delete NewDceBinding;
|
|
|
|
LogError(SU_CASSOC, EV_STATUS, this, 0, Status);
|
|
SetErrorFlag();
|
|
return Status;
|
|
}
|
|
|
|
OldDceBinding = pDceBinding;
|
|
pDceBinding = NewDceBinding;
|
|
|
|
delete OldDceBinding;
|
|
}
|
|
else if (OldAssociationFlags & UNRESOLVEDEP)
|
|
{
|
|
//
|
|
// We have resolved a dynamic endpoint; update the endpoint in the DCE_BINDING.
|
|
//
|
|
Status = TransportInterface->QueryEndpoint(NewAddress, ResolvedEndpoint);
|
|
if ( Status != RPC_S_OK )
|
|
{
|
|
LogError(SU_CASSOC, EV_STATUS, this, 0, Status);
|
|
SetErrorFlag();
|
|
return Status;
|
|
}
|
|
|
|
pDceBinding->AddEndpoint(ResolvedEndpoint);
|
|
|
|
ResolvedEndpoint = 0;
|
|
}
|
|
else
|
|
{
|
|
ASSERT( 0 );
|
|
}
|
|
|
|
RpcpMemoryCopy( SecondAddress, NewAddress, TransportInterface->AddressSize );
|
|
ServerAddress = SecondAddress;
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_BINDING_HANDLE::FindOrCreateAssociation(
|
|
IN const PRPC_CLIENT_INTERFACE Interface,
|
|
IN BOOL fReconnect,
|
|
IN BOOL fBroadcast
|
|
)
|
|
{
|
|
RPC_STATUS Status;
|
|
BOOL fPartial = FALSE;
|
|
LONG AssociationFlag = 0;
|
|
|
|
if (fBroadcast)
|
|
{
|
|
AssociationFlag = DG_CASSOCIATION::BROADCAST;
|
|
}
|
|
|
|
//
|
|
// Check to see if we need to resolve this endpoint.
|
|
//
|
|
Status = pDceBinding->ResolveEndpointIfNecessary(
|
|
Interface,
|
|
InqPointerAtObjectUuid(),
|
|
InquireEpLookupHandle(),
|
|
TRUE, //UseEpMapper Ep If Necessary
|
|
InqComTimeout(),
|
|
INFINITE, // CallTimeout
|
|
NULL // AuthInfo
|
|
);
|
|
|
|
if (Status == RPC_P_EPMAPPER_EP)
|
|
{
|
|
AssociationFlag |= DG_CASSOCIATION::UNRESOLVEDEP;
|
|
fPartial = TRUE;
|
|
Status = 0;
|
|
}
|
|
|
|
if (Status)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// A binding with the UNIQUE option set should maintain its own association,
|
|
// never sharing with other binding handles. This is used by the Wolfpack
|
|
// cluster software to manage connections themselves.
|
|
//
|
|
ULONG_PTR fUnique;
|
|
|
|
Status = InqTransportOption(RPC_C_OPT_UNIQUE_BINDING, &fUnique);
|
|
ASSERT(Status == RPC_S_OK);
|
|
|
|
if (fUnique == 0 && fBroadcast == FALSE)
|
|
{
|
|
//
|
|
// Look for a matching association.
|
|
//
|
|
ASSERT( Association == 0 );
|
|
|
|
Association = ActiveAssociations->Find( this, Interface, fContextHandle, fPartial, fReconnect );
|
|
if (Association)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create a matching association.
|
|
//
|
|
ASSERT(pDceBinding);
|
|
|
|
DCE_BINDING * NewDceBinding = pDceBinding->DuplicateDceBinding();
|
|
|
|
if (!NewDceBinding)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
Association = new (TransportInterface) DG_CASSOCIATION( TransportInterface,
|
|
AssociationFlag,
|
|
NewDceBinding,
|
|
(BOOL) fUnique,
|
|
&Status
|
|
);
|
|
|
|
if (Association == 0)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (Status)
|
|
{
|
|
delete Association;
|
|
Association = 0;
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Other threads using this binding handle for the same interface
|
|
// will reuse this association instead of creating a new one.
|
|
//
|
|
Association->AddInterface(Interface, InqPointerAtObjectUuid());
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
RPC_STATUS
|
|
DG_BINDING_HANDLE::SetTransportOption( IN unsigned long option,
|
|
IN ULONG_PTR optionValue )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the binding specific transport option to the optionValue.
|
|
|
|
Arguments:
|
|
|
|
option -- Option to set (transport specific).
|
|
optionValue -- New value for option.
|
|
|
|
Return Value: RPC_S_OK
|
|
RPC_S_CANNOT_SUPPORT
|
|
RPC_S_ARG_INVALID
|
|
RPC_S_OUT_OF_MEMORY
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
if (option >= RPC_C_OPT_BINDING_NONCAUSAL)
|
|
{
|
|
//
|
|
// This option can be changed only before a call is made.
|
|
//
|
|
if (option == RPC_C_OPT_UNIQUE_BINDING && Association != NULL)
|
|
{
|
|
return RPC_S_WRONG_KIND_OF_BINDING;
|
|
}
|
|
|
|
return BINDING_HANDLE::SetTransportOption(option, optionValue);
|
|
}
|
|
|
|
if ( (TransportInterface) && (TransportInterface->OptionsSize > 0) )
|
|
{
|
|
if (pvTransportOptions == NULL)
|
|
{
|
|
pvTransportOptions = (void*)I_RpcAllocate(TransportInterface->OptionsSize);
|
|
if (pvTransportOptions == NULL)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
Status = TransportInterface->InitOptions(pvTransportOptions);
|
|
}
|
|
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
Status = TransportInterface->SetOption(pvTransportOptions,option,optionValue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = RPC_S_CANNOT_SUPPORT;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
RPC_STATUS
|
|
DG_BINDING_HANDLE::InqTransportOption( IN unsigned long option,
|
|
OUT ULONG_PTR *pOptionValue )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the value of a transport specific binding option.
|
|
|
|
Arguments:
|
|
|
|
option -- Option to inquire.
|
|
pOptionValue - Place to return the current value.
|
|
|
|
Return Value: RPC_S_OK
|
|
RPC_S_CANNOT_SUPPORT
|
|
RPC_S_ARG_INVALID
|
|
RPC_S_OUT_OF_MEMORY
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
if (option >= RPC_C_OPT_BINDING_NONCAUSAL)
|
|
{
|
|
return BINDING_HANDLE::InqTransportOption(option, pOptionValue);
|
|
}
|
|
|
|
if ( (TransportInterface) && (TransportInterface->OptionsSize > 0) )
|
|
{
|
|
if (pvTransportOptions == NULL)
|
|
{
|
|
pvTransportOptions = (void*)I_RpcAllocate(TransportInterface->OptionsSize);
|
|
if (pvTransportOptions == NULL)
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
|
|
Status = TransportInterface->InitOptions(pvTransportOptions);
|
|
}
|
|
|
|
if (Status == RPC_S_OK)
|
|
Status = TransportInterface->InqOption(pvTransportOptions,option,pOptionValue);
|
|
}
|
|
else
|
|
Status = RPC_S_CANNOT_SUPPORT;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
RPC_STATUS
|
|
DG_BINDING_HANDLE::GetBuffer (
|
|
IN OUT PRPC_MESSAGE Message,
|
|
IN UUID *ObjectUuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the routine that is called to initiate an rpc. At this point,
|
|
the client stub is allocating memory to place the parameters into. Ask our
|
|
association for a DG_CCALL object to transact this call on then send
|
|
the buffer request off to that DG_CCALL.
|
|
|
|
Arguments:
|
|
|
|
Message - The RPC_MESSAGE structure associated with this call.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OUT_OF_MEMORY
|
|
|
|
RPC_S_OK
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
{
|
|
BOOL fBroadcast = FALSE;
|
|
RPC_STATUS Status;
|
|
|
|
LogEvent( SU_HANDLE, EV_PROC, this, 0, 'G' + (('e' + (('t' + ('B' << 8)) << 8)) << 8));
|
|
|
|
if (Message->RpcFlags & (RPC_NCA_FLAGS_MAYBE | RPC_NCA_FLAGS_BROADCAST))
|
|
{
|
|
Message->RpcFlags |= RPC_NCA_FLAGS_IDEMPOTENT;
|
|
}
|
|
|
|
if (Message->RpcFlags & RPC_BUFFER_ASYNC)
|
|
{
|
|
Status = TransportObject->CreateThread();
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if (Message->RpcFlags & RPC_NCA_FLAGS_BROADCAST)
|
|
{
|
|
fBroadcast = TRUE;
|
|
}
|
|
|
|
MutexRequest();
|
|
|
|
ASSERT(pDceBinding != 0);
|
|
|
|
//
|
|
// Have we already determined the association for this binding handle?
|
|
//
|
|
if (Association == 0)
|
|
{
|
|
Status = FindOrCreateAssociation( (PRPC_CLIENT_INTERFACE) Message->RpcInterfaceInformation, FALSE, fBroadcast );
|
|
if (Status)
|
|
{
|
|
MutexClear();
|
|
LogError(SU_HANDLE, EV_STATUS, this, 0, Status);
|
|
return (Status);
|
|
}
|
|
}
|
|
|
|
|
|
{
|
|
const CLIENT_AUTH_INFO * AuthInfo = InquireAuthInformation();
|
|
|
|
if ( AuthInfo &&
|
|
AuthInfo->AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE &&
|
|
AuthInfo->IdentityTracking == RPC_C_QOS_IDENTITY_DYNAMIC )
|
|
{
|
|
Status = ReAcquireCredentialsIfNecessary();
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
MutexClear();
|
|
LogError(SU_HANDLE, EV_STATUS, this, 0, Status);
|
|
return (Status);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Here's the deal, Sparky:
|
|
// association refcount = (# of binding handles with a pointer to it)
|
|
// + (# of the association's connections in use)
|
|
// connection refcount = (# of the connection's CCALLs in use)
|
|
// binding refcount = (# of connections with a pointer to it)
|
|
|
|
PDG_CCALL Call = 0;
|
|
const CLIENT_AUTH_INFO * AuthInfo;
|
|
|
|
if (Message->RpcFlags & RPC_NCA_FLAGS_MAYBE)
|
|
{
|
|
AuthInfo = 0;
|
|
}
|
|
else
|
|
{
|
|
AuthInfo = InquireAuthInformation();
|
|
}
|
|
|
|
Status = Association->AllocateCall( this, AuthInfo, &Call, (Message->RpcFlags & RPC_BUFFER_ASYNC) ? TRUE : FALSE );
|
|
|
|
MutexClear();
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
LogError(SU_HANDLE, EV_STATUS, this, 0, Status);
|
|
return Status;
|
|
}
|
|
|
|
return Call->GetInitialBuffer(Message, ObjectUuid);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DG_CASSOCIATION::SendKeepAlive()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This calls convc_indy() to tell the server to keep the association's
|
|
context handles alive. The NT server code does not register the convc interface,
|
|
since the mere fact of activity on a connection keeps the connection, and therefore
|
|
the association group, alive.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful
|
|
FALSE if not
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS status = 0;
|
|
|
|
LogEvent(SU_CASSOC, EV_ACK, this);
|
|
|
|
if (!KeepAliveHandle)
|
|
{
|
|
MutexRequest();
|
|
|
|
if (!KeepAliveHandle)
|
|
{
|
|
DCE_BINDING * NewDceBinding;
|
|
|
|
NewDceBinding = pDceBinding->DuplicateDceBinding();
|
|
if (!NewDceBinding)
|
|
{
|
|
MutexClear();
|
|
return FALSE;
|
|
}
|
|
|
|
KeepAliveHandle = new DG_BINDING_HANDLE(this, NewDceBinding, &status);
|
|
|
|
if (status)
|
|
{
|
|
delete KeepAliveHandle;
|
|
delete NewDceBinding;
|
|
MutexClear();
|
|
return FALSE;
|
|
}
|
|
|
|
if (!KeepAliveHandle)
|
|
{
|
|
delete NewDceBinding;
|
|
MutexClear();
|
|
return FALSE;
|
|
}
|
|
|
|
IncrementBindingRefCount( FALSE );
|
|
}
|
|
|
|
MutexClear();
|
|
}
|
|
|
|
RpcTryExcept
|
|
{
|
|
_convc_indy( KeepAliveHandle, &ActiveAssociations->CasUuid );
|
|
}
|
|
RpcExcept( I_RpcExceptionFilter(RpcExceptionCode()) )
|
|
{
|
|
// don't care
|
|
#ifdef DBG
|
|
DbgPrint("RPC: exception 0x%x in context-handle keep-alive\n", RpcExceptionCode());
|
|
#endif
|
|
}
|
|
RpcEndExcept;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PDG_CCONNECTION
|
|
DG_CASSOCIATION::AllocateConnection(
|
|
IN PDG_BINDING_HANDLE BindingHandle,
|
|
IN const CLIENT_AUTH_INFO * AuthInfo,
|
|
IN DWORD ThreadId,
|
|
IN BOOL fAsync,
|
|
OUT RPC_STATUS * pStatus
|
|
)
|
|
{
|
|
if (!ThreadId)
|
|
{
|
|
ThreadId = GetCurrentThreadId();
|
|
}
|
|
|
|
boolean fStacking = FALSE;
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
PDG_CCONNECTION Connection = 0;
|
|
PDG_CCONNECTION BusyConnection;
|
|
DictionaryCursor cursor;
|
|
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_CONN, 0 ), this, Connection );
|
|
|
|
long StartTime = GetTickCount();
|
|
|
|
retry:
|
|
|
|
BusyConnection = 0;
|
|
|
|
MutexRequest();
|
|
|
|
//
|
|
// Multiple async calls from the same thread should all use a single
|
|
// connection unless the binding handle is marked "non-causal".
|
|
// A synchronous call from the same thread should use a different
|
|
// connection.
|
|
//
|
|
// A connection waiting to send a delayed ACK is still marked active,
|
|
// but we want to reuse it anyway.
|
|
//
|
|
// The plan, therefore, is to search first for an active connection if
|
|
// the call is async and causal, then search for a connection that is
|
|
// inactive except for a delayed ack, then search for an idle connection.
|
|
//
|
|
|
|
if (fAsync)
|
|
{
|
|
ULONG_PTR fNonCausal;
|
|
|
|
Status = BindingHandle->InqTransportOption(
|
|
RPC_C_OPT_BINDING_NONCAUSAL,
|
|
&fNonCausal);
|
|
ASSERT(Status == RPC_S_OK);
|
|
|
|
if (fNonCausal == 0)
|
|
{
|
|
fStacking = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Search for a connection handling this thread's async calls.
|
|
//
|
|
if (fStacking)
|
|
{
|
|
ActiveConnections.Reset(cursor);
|
|
|
|
while (Connection = ActiveConnections.Next(cursor))
|
|
{
|
|
BusyConnection = Connection;
|
|
|
|
if ( Connection->BindingHandle != BindingHandle ||
|
|
Connection->ThreadId != ThreadId )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The app may have changed the security info since the other
|
|
// calls were submitted.
|
|
//
|
|
if (FALSE == fLoneBindingHandle &&
|
|
FALSE == Connection->IsSupportedAuthInfo(AuthInfo))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Connection->MutexRequest();
|
|
if (Connection->fBusy)
|
|
{
|
|
// The connection is in a transitional state and is unavailable right now.
|
|
// If this is a Unique binding handle, retry. Otherwise keep looking.
|
|
//
|
|
Connection->MutexClear();
|
|
|
|
if (fLoneBindingHandle)
|
|
{
|
|
LogError(SU_CASSOC, EV_PROC, this, (void *) 4, 'R' + (('e' + (('t' + ('r' << 8)) << 8)) << 8));
|
|
MutexClear();
|
|
Sleep(1);
|
|
goto retry;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if ( Connection->BindingHandle != BindingHandle ||
|
|
Connection->ThreadId != ThreadId )
|
|
{
|
|
Connection->MutexClear();
|
|
continue;
|
|
}
|
|
|
|
MutexClear();
|
|
|
|
//
|
|
// Add a reference for the new call to come.
|
|
//
|
|
Connection->IncrementRefCount();
|
|
|
|
Connection->CancelDelayedAck();
|
|
|
|
return Connection;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Search for a connection that is waiting on a delayed ack.
|
|
//
|
|
ActiveConnections.Reset(cursor);
|
|
|
|
while (Connection = ActiveConnections.Next(cursor))
|
|
{
|
|
BusyConnection = Connection;
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_CONN, 1 ), this, Connection );
|
|
if (!Connection->AckPending)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (FALSE == fLoneBindingHandle &&
|
|
FALSE == Connection->IsSupportedAuthInfo(AuthInfo))
|
|
{
|
|
continue;
|
|
}
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_CONN, 2 ), this, Connection );
|
|
Connection->MutexRequest();
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_CONN, 3 ), this, Connection );
|
|
if (Connection->fBusy)
|
|
{
|
|
// The connection is in a transitional state and is unavailable right now.
|
|
// If this is a Unique binding handle, retry. Otherwise keep looking.
|
|
//
|
|
Connection->MutexClear();
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_CONN, 4 ), this, Connection );
|
|
if (fLoneBindingHandle)
|
|
{
|
|
LogError(SU_CASSOC, EV_PROC, this, (void *) 5, 'R' + (('e' + (('t' + ('r' << 8)) << 8)) << 8));
|
|
MutexClear();
|
|
Sleep(1);
|
|
goto retry;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_CONN, 5 ), this, Connection );
|
|
if (!Connection->AckPending)
|
|
{
|
|
Connection->MutexClear();
|
|
continue;
|
|
}
|
|
|
|
DG_BINDING_HANDLE * OldHandle = Connection->BindingHandle;
|
|
|
|
Connection->BindingHandle = BindingHandle;
|
|
Connection->ThreadId = ThreadId;
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_CONN, 6 ), this, Connection );
|
|
#ifdef DBG
|
|
//
|
|
// debugging code for clustering
|
|
//
|
|
if (fLoneBindingHandle)
|
|
{
|
|
DG_CCONNECTION * SecondConnection = 0;
|
|
|
|
//
|
|
// See if a valid inactive connection exists, This could cause trouble, too.
|
|
//
|
|
InactiveConnections.Reset(cursor);
|
|
|
|
SecondConnection = InactiveConnections.Next(cursor);
|
|
if (SecondConnection &&
|
|
SecondConnection->fError == FALSE)
|
|
{
|
|
DbgPrint("RPC: failure of unique-handle semantics (2)\n");
|
|
DbgBreakPoint();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
MutexClear();
|
|
|
|
//
|
|
// Add a reference for the call to come. This prevents the conn from
|
|
// accidentally being moved to the inactive list.
|
|
//
|
|
Connection->IncrementRefCount();
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_CONN, 7 ), this, Connection );
|
|
//
|
|
// Must cancel the delayed ACK before releasing the mutex, so other threads
|
|
// can't pick it up like this thread did.
|
|
//
|
|
Connection->CancelDelayedAck();
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_CONN, 8 ), this, Connection );
|
|
if (OldHandle != Connection->BindingHandle)
|
|
{
|
|
Connection->MutexClear();
|
|
|
|
BindingHandle->IncrementRefCount();
|
|
OldHandle->DecrementRefCount();
|
|
|
|
Connection->MutexRequest();
|
|
}
|
|
|
|
return Connection;
|
|
}
|
|
|
|
//
|
|
// Search for an idle connection with matching security info.
|
|
//
|
|
InactiveConnections.Reset(cursor);
|
|
|
|
while (0 != (Connection = InactiveConnections.Next(cursor)))
|
|
{
|
|
BusyConnection = Connection;
|
|
|
|
if (Connection->fError)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (fLoneBindingHandle ||
|
|
Connection->IsSupportedAuthInfo(AuthInfo))
|
|
{
|
|
LogEvent(SU_CCONN, EV_START, Connection, this);
|
|
|
|
ASSERT( FALSE == Connection->fAutoReconnect );
|
|
ASSERT( FALSE == Connection->fError );
|
|
|
|
InactiveConnections.Delete(Connection->AssociationKey);
|
|
Connection->AssociationKey = ActiveConnections.Insert(Connection);
|
|
if (-1 == Connection->AssociationKey)
|
|
{
|
|
MutexClear();
|
|
*pStatus = RPC_S_OUT_OF_MEMORY;
|
|
delete Connection;
|
|
return 0;
|
|
}
|
|
|
|
IncrementRefCount();
|
|
|
|
ASSERT( FALSE == Connection->fBusy );
|
|
|
|
Connection->MutexRequest();
|
|
|
|
Connection->BindingHandle = BindingHandle;
|
|
Connection->ThreadId = ThreadId;
|
|
|
|
ClientConnections->Add(Connection);
|
|
|
|
MutexClear();
|
|
|
|
BindingHandle->IncrementRefCount();
|
|
|
|
//
|
|
// Add a reference for the call to come.
|
|
//
|
|
Connection->IncrementRefCount();
|
|
|
|
return Connection;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Unique handles have special semantics.
|
|
//
|
|
if (fLoneBindingHandle && BusyConnection)
|
|
{
|
|
if (BindingHandle == KeepAliveHandle)
|
|
{
|
|
LogEvent(SU_CASSOC, EV_PROC, this, (void *) 1, 'R' + (('e' + (('t' + ('r' << 8)) << 8)) << 8));
|
|
//
|
|
// Keep-alive was begun just as the app thread began using the handle.
|
|
// Don't bother executing the keep-alive.
|
|
//
|
|
MutexClear();
|
|
*pStatus = RPC_S_CALL_IN_PROGRESS;
|
|
return 0;
|
|
}
|
|
|
|
ASSERT( BusyConnection->BindingHandle );
|
|
|
|
if (BusyConnection->BindingHandle == KeepAliveHandle)
|
|
{
|
|
LogEvent(SU_CASSOC, EV_PROC, this, (void *) 2, 'R' + (('e' + (('t' + ('r' << 8)) << 8)) << 8));
|
|
#if 0
|
|
if (GetTickCount() - StartTime > 10*1000)
|
|
{
|
|
DbgPrint("RPC: keep-alive tied up a connection for > 10 seconds\n");
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
//
|
|
// App thread began a call just as the keep-alive thread was finishing one.
|
|
// Wait for the keep-alive to finish.
|
|
//
|
|
MutexClear();
|
|
|
|
Sleep(1);
|
|
|
|
goto retry;
|
|
}
|
|
|
|
//
|
|
// Two app threads contending, or the current connection is closing.
|
|
//
|
|
if (BusyConnection->fBusy)
|
|
{
|
|
LogError(SU_CASSOC, EV_PROC, this, (void *) 6, 'R' + (('e' + (('t' + ('r' << 8)) << 8)) << 8));
|
|
MutexClear();
|
|
Sleep(1);
|
|
goto retry;
|
|
}
|
|
|
|
//
|
|
// Create a new connection.
|
|
//
|
|
LogEvent(SU_CASSOC, EV_PROC, this, (void *) 3, 'R' + (('e' + (('t' + ('r' << 8)) << 8)) << 8));
|
|
|
|
#ifdef DBG
|
|
//
|
|
// If it's in use, it should be doing something.
|
|
//
|
|
if ( FALSE == BusyConnection->fError &&
|
|
BusyConnection->TimeStamp != 0 &&
|
|
(GetTickCount() - BusyConnection->TimeStamp) > 60000)
|
|
{
|
|
DbgPrint("RPC: failure of unique-handle semantics (3)\n");
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Create a new connection and add it to the active conn list.
|
|
// Increment the refcount here to avoid having the assoc deleted
|
|
// while we are tied up.
|
|
//
|
|
IncrementRefCount();
|
|
|
|
MutexClear();
|
|
|
|
Connection = new (TransportInterface) DG_CCONNECTION(this, AuthInfo, &Status);
|
|
if (!Connection)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
DecrementRefCount();
|
|
delete Connection;
|
|
*pStatus = Status;
|
|
return 0;
|
|
}
|
|
|
|
Connection->BindingHandle = BindingHandle;
|
|
Connection->ThreadId = ThreadId;
|
|
|
|
MutexRequest();
|
|
|
|
Connection->MutexRequest();
|
|
|
|
LogEvent(SU_CCONN, EV_START, Connection, this);
|
|
Connection->AssociationKey = ActiveConnections.Insert(Connection);
|
|
|
|
MutexClear();
|
|
|
|
if (-1 == Connection->AssociationKey)
|
|
{
|
|
DecrementRefCount();
|
|
|
|
// don't have to release the connection mutex because we are deleting it
|
|
delete Connection;
|
|
*pStatus = RPC_S_OUT_OF_MEMORY;
|
|
return 0;
|
|
}
|
|
|
|
BindingHandle->IncrementRefCount();
|
|
|
|
ClientConnections->Add(Connection);
|
|
|
|
//
|
|
// Add a reference for the call in progress.
|
|
//
|
|
Connection->IncrementRefCount();
|
|
|
|
return Connection;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CASSOCIATION::AllocateCall(
|
|
IN PDG_BINDING_HANDLE BindingHandle,
|
|
IN const CLIENT_AUTH_INFO * AuthInfo,
|
|
OUT PDG_CCALL * ppCall,
|
|
IN BOOL fAsync
|
|
)
|
|
{
|
|
RPC_STATUS Status = 0;
|
|
PDG_CCONNECTION Connection;
|
|
|
|
Connection = AllocateConnection(BindingHandle, AuthInfo, 0, fAsync, &Status);
|
|
|
|
if (!Connection)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
*ppCall = Connection->AllocateCall();
|
|
|
|
if (!*ppCall)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
void
|
|
DG_CASSOCIATION::ReleaseConnection(
|
|
IN PDG_CCONNECTION Connection
|
|
)
|
|
{
|
|
DG_BINDING_HANDLE * MyHandle;
|
|
|
|
LogEvent(SU_CCONN, EV_STOP, Connection, this);
|
|
|
|
ClientConnections->Remove(Connection);
|
|
|
|
int Key;
|
|
|
|
MutexRequest();
|
|
|
|
ActiveConnections.Delete(Connection->AssociationKey);
|
|
Connection->AssociationKey = InactiveConnections.Insert(Connection);
|
|
Key = Connection->AssociationKey;
|
|
|
|
Connection->WaitForNoReferences();
|
|
|
|
MyHandle = Connection->BindingHandle;
|
|
Connection->BindingHandle = 0;
|
|
|
|
MutexClear();
|
|
|
|
if (MyHandle)
|
|
{
|
|
MyHandle->DecrementRefCount();
|
|
}
|
|
|
|
if (-1 == Key)
|
|
{
|
|
Connection->CancelDelayedAck();
|
|
delete Connection;
|
|
}
|
|
|
|
DecrementRefCount();
|
|
}
|
|
|
|
|
|
DG_CCALL *
|
|
DG_CCONNECTION::AllocateCall()
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Provides a DG_CCALL to use. The connection mutex is cleared on exit.
|
|
|
|
--*/
|
|
{
|
|
DG_CCALL * Call = 0;
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
Mutex.VerifyOwned();
|
|
|
|
ASSERT( !AckPending );
|
|
ASSERT( ReferenceCount );
|
|
|
|
if (CachedCalls)
|
|
{
|
|
Call = CachedCalls;
|
|
CachedCalls = CachedCalls->Next;
|
|
|
|
MutexClear();
|
|
}
|
|
else
|
|
{
|
|
MutexClear();
|
|
|
|
Call = new DG_CCALL(this, &Status);
|
|
if (!Call)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
delete Call;
|
|
|
|
MutexRequest();
|
|
|
|
DecrementRefCount();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Call->IncrementRefCount();
|
|
|
|
Call->Cancelled = FALSE;
|
|
Call->CancelPending = FALSE;
|
|
|
|
LogEvent(SU_CCALL, EV_START, Call, this);
|
|
|
|
return Call;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_BINDING_HANDLE::BindingFree (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Implements RpcBindingFree for dg binding handles.
|
|
|
|
Arguments:
|
|
|
|
<none>
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
|
|
--*/
|
|
{
|
|
LogEvent( SU_HANDLE, EV_PROC, this, 0, 'F' + (('r' + (('e' + ('e' << 8)) << 8)) << 8), TRUE);
|
|
|
|
if (!ThreadSelf())
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// Flush the delayed ack on any connection using this handle.
|
|
//
|
|
if (Association)
|
|
{
|
|
PDG_CASSOCIATION TempAssociation;
|
|
|
|
MutexRequest();
|
|
|
|
TempAssociation = Association;
|
|
Association = 0;
|
|
|
|
MutexClear();
|
|
|
|
if (TempAssociation != 0)
|
|
{
|
|
TempAssociation->DecrementBindingRefCount(fContextHandle);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Decrement the ref count. If the count has hit zero, this call will
|
|
// delete this.
|
|
//
|
|
DecrementRefCount();
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_BINDING_HANDLE::PrepareBindingHandle (
|
|
IN TRANS_INFO * a_TransportInterface,
|
|
IN DCE_BINDING * DceBinding
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Serves as an auxiliary constructor for DG_BINDING_HANDLE. This is called
|
|
to initialize stuff after the DG_BINDING_HANDLE has been constructed.
|
|
|
|
Arguments:
|
|
|
|
TransportInterface - pointer to the DG_CLIENT_TRANSPORT object that this
|
|
DG_BINDING_HANDLE is active on.
|
|
|
|
DceBinding - Pointer to the DCE_BINDING that we are associated with.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
ASSERT(pDceBinding == 0);
|
|
|
|
Association = 0;
|
|
pDceBinding = DceBinding;
|
|
TransportObject = a_TransportInterface;
|
|
TransportInterface = (RPC_DATAGRAM_TRANSPORT *) a_TransportInterface->InqTransInfo();
|
|
|
|
RPC_CHAR * Endpoint = pDceBinding->InqEndpoint();
|
|
if (!Endpoint || Endpoint[0] == 0)
|
|
{
|
|
fDynamicEndpoint = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fDynamicEndpoint = FALSE;
|
|
}
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_BINDING_HANDLE::ToStringBinding (
|
|
OUT RPC_CHAR __RPC_FAR * __RPC_FAR * StringBinding
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need to convert the binding handle into a string binding.
|
|
|
|
Arguments:
|
|
|
|
StringBinding - Returns the string representation of the binding
|
|
handle.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
RPC_S_OUT_OF_MEMORY
|
|
<return from DG_CASSOCIATION::ToStringBinding>
|
|
|
|
|
|
--*/
|
|
{
|
|
CLAIM_MUTEX Lock( BindingMutex );
|
|
|
|
if (Association == 0)
|
|
{
|
|
*StringBinding = pDceBinding->StringBindingCompose(
|
|
InqPointerAtObjectUuid()
|
|
);
|
|
if (*StringBinding == 0)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
return RPC_S_OK;
|
|
}
|
|
else
|
|
{
|
|
return Association->ToStringBinding(
|
|
StringBinding,
|
|
InqPointerAtObjectUuid()
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_BINDING_HANDLE::ResolveBinding (
|
|
IN RPC_CLIENT_INTERFACE __RPC_FAR * RpcClientInterface
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resolve this binding.
|
|
|
|
Arguments:
|
|
|
|
RpcClientInterface - Interface info used to resolve the endpoint.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
<return from DCE_BINDING::ResolveEndpointIfNecessary>
|
|
|
|
--*/
|
|
{
|
|
CLAIM_MUTEX Lock( BindingMutex );
|
|
|
|
LogEvent( SU_HANDLE, EV_RESOLVED, this, Association );
|
|
|
|
if ( Association == 0 )
|
|
{
|
|
RPC_STATUS Status;
|
|
Status = pDceBinding->ResolveEndpointIfNecessary(
|
|
RpcClientInterface,
|
|
InqPointerAtObjectUuid(),
|
|
InquireEpLookupHandle(),
|
|
FALSE,
|
|
InqComTimeout(),
|
|
INFINITE, // CallTimeout
|
|
NULL // AuthInfo
|
|
);
|
|
|
|
if (Status)
|
|
{
|
|
LogError( SU_HANDLE, EV_STATUS, this, 0, Status );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_BINDING_HANDLE::BindingReset (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reset this binding to a 'zero' value.
|
|
|
|
Arguments:
|
|
|
|
<none>
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK;
|
|
|
|
--*/
|
|
{
|
|
LogEvent( SU_HANDLE, EV_PROC, this, 0, 'R' + (('e' + (('s' + ('e' << 8)) << 8)) << 8), TRUE);
|
|
|
|
MutexRequest();
|
|
|
|
DisassociateFromServer();
|
|
|
|
pDceBinding->MakePartiallyBound();
|
|
|
|
if (0 != *InquireEpLookupHandle())
|
|
{
|
|
EpFreeLookupHandle(*InquireEpLookupHandle());
|
|
*InquireEpLookupHandle() = 0;
|
|
}
|
|
|
|
MutexClear();
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_BINDING_HANDLE::BindingCopy (
|
|
OUT BINDING_HANDLE * __RPC_FAR * DestinationBinding,
|
|
IN unsigned int MaintainContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a copy of this binding handle.
|
|
|
|
Arguments:
|
|
|
|
DestinationBinding - Where to place a pointer to the new binding.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
PDG_BINDING_HANDLE Binding;
|
|
DCE_BINDING * NewDceBinding = 0;
|
|
|
|
*DestinationBinding = 0;
|
|
|
|
Binding = new DG_BINDING_HANDLE(&Status);
|
|
if ( Binding == 0 || Status != RPC_S_OK)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
MutexRequest();
|
|
|
|
//
|
|
// If the binding refers to a dynamic endpoint or is used for broadcast calls,
|
|
// then the association may have a more up-to-date DCE_BINDING.
|
|
//
|
|
if (Association)
|
|
{
|
|
NewDceBinding = Association->DuplicateDceBinding();
|
|
}
|
|
else
|
|
{
|
|
NewDceBinding = pDceBinding->DuplicateDceBinding();
|
|
}
|
|
|
|
if (!NewDceBinding)
|
|
{
|
|
MutexClear();
|
|
delete Binding;
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Clone calls MapAuthLevel which depends on these items, so copy them first.
|
|
//
|
|
Binding->TransportObject = TransportObject;
|
|
Binding->TransportInterface = TransportInterface;
|
|
Binding->pDceBinding = NewDceBinding;
|
|
Binding->fDynamicEndpoint = fDynamicEndpoint;
|
|
Binding->fContextHandle = MaintainContext;
|
|
|
|
Status = Binding->BINDING_HANDLE::Clone( this );
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
MutexClear();
|
|
delete Binding;
|
|
Binding = 0;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// If we clone a binding handle with the UNIQUE option set, we want
|
|
// to use a separate association.
|
|
//
|
|
if (Association && !Association->fLoneBindingHandle)
|
|
{
|
|
Binding->Association = Association;
|
|
Binding->Association->IncrementBindingRefCount( MaintainContext );
|
|
}
|
|
else
|
|
{
|
|
Binding->Association = 0;
|
|
}
|
|
|
|
MutexClear();
|
|
|
|
*DestinationBinding = (BINDING_HANDLE *) Binding;
|
|
|
|
if (MaintainContext)
|
|
{
|
|
//
|
|
// We've created a context handle; ensure the keep-alive proc is active.
|
|
//
|
|
DelayedProcedures->Add(ContextHandleTimer, CXT_HANDLE_SWEEP_INTERVAL, FALSE);
|
|
}
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
PDG_CCONNECTION
|
|
DG_BINDING_HANDLE::GetReplacementConnection(
|
|
PDG_CCONNECTION OldConnection,
|
|
PRPC_CLIENT_INTERFACE Interface
|
|
)
|
|
{
|
|
BOOL fBroadcast = FALSE;
|
|
|
|
CLAIM_MUTEX Lock( BindingMutex );
|
|
|
|
if (OldConnection->CurrentCall->IsBroadcast())
|
|
{
|
|
fBroadcast = TRUE;
|
|
}
|
|
|
|
if (OldConnection->Association == Association)
|
|
{
|
|
BOOL Dynamic = fDynamicEndpoint;
|
|
|
|
Association->SetErrorFlag();
|
|
|
|
//
|
|
// If the binding handle has no object ID, releasing the current
|
|
// endpoint would mean all mgmt calls on the new connection would fail.
|
|
//
|
|
if (IsMgmtIfUuid(&Interface->InterfaceId.SyntaxGUID))
|
|
{
|
|
fDynamicEndpoint = FALSE;
|
|
}
|
|
|
|
DisassociateFromServer();
|
|
|
|
fDynamicEndpoint = (boolean) Dynamic;
|
|
}
|
|
|
|
if (!Association && RPC_S_OK != FindOrCreateAssociation(Interface, TRUE, fBroadcast))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
RPC_STATUS Status;
|
|
return Association->AllocateConnection(this,
|
|
OldConnection->InqAuthInfo(),
|
|
OldConnection->ThreadId,
|
|
OldConnection->CurrentCall->pAsync ? TRUE : FALSE,
|
|
&Status
|
|
);
|
|
}
|
|
|
|
|
|
void
|
|
DG_BINDING_HANDLE::DisassociateFromServer()
|
|
{
|
|
PDG_CASSOCIATION TempAssociation;
|
|
|
|
LogEvent(SU_HANDLE, EV_DISASSOC, this, Association, 0, TRUE);
|
|
|
|
MutexRequest();
|
|
|
|
TempAssociation = Association;
|
|
Association = 0;
|
|
|
|
//
|
|
// This frees memory while holding the mutex - not ideal..
|
|
// One could modify DCE_BINDING::MakePartiallyBound to return the old
|
|
// endpoint so we can delete it outside the mutex.
|
|
//
|
|
if (fDynamicEndpoint)
|
|
{
|
|
pDceBinding->MakePartiallyBound();
|
|
}
|
|
|
|
MutexClear();
|
|
|
|
if (TempAssociation != 0)
|
|
{
|
|
TempAssociation->DecrementBindingRefCount(fContextHandle);
|
|
}
|
|
}
|
|
|
|
|
|
unsigned long
|
|
DG_BINDING_HANDLE::MapAuthenticationLevel (
|
|
IN unsigned long AuthenticationLevel
|
|
)
|
|
{
|
|
if (AuthenticationLevel == RPC_C_AUTHN_LEVEL_CONNECT ||
|
|
AuthenticationLevel == RPC_C_AUTHN_LEVEL_CALL )
|
|
{
|
|
return(RPC_C_AUTHN_LEVEL_PKT);
|
|
}
|
|
|
|
// This is an additional mapping for the reliable DG protocols
|
|
// (i.e. Falcon/RPC). This protocols only use the following
|
|
// three levels: RPC_C_AUTHN_LEVEL_NONE 1
|
|
// RPC_C_AUTHN_LEVEL_PKT_INTEGRITY 5
|
|
// RPC_C_AUTHN_LEVEL_PKT_PRIVACY 6
|
|
if (TransportInterface->IsMessageTransport)
|
|
{
|
|
if (AuthenticationLevel == RPC_C_AUTHN_LEVEL_DEFAULT)
|
|
{
|
|
return RPC_C_AUTHN_LEVEL_NONE;
|
|
}
|
|
|
|
if ( (AuthenticationLevel > RPC_C_AUTHN_LEVEL_NONE)
|
|
&& (AuthenticationLevel < RPC_C_AUTHN_LEVEL_PKT_PRIVACY) )
|
|
{
|
|
return RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
|
|
}
|
|
}
|
|
|
|
return(AuthenticationLevel);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DG_BINDING_HANDLE::SetTransportAuthentication(
|
|
IN unsigned long ulAuthenticationLevel,
|
|
IN unsigned long ulAuthenticationService,
|
|
OUT RPC_STATUS *pStatus )
|
|
// Routine Description:
|
|
//
|
|
// Do transport specific security for one of the datagram transports.
|
|
// This is currently only for the Falcon transport.
|
|
//
|
|
// Return Values:
|
|
//
|
|
// TRUE -- Continue with the RPC level authenticaion.
|
|
//
|
|
// FALSE -- This setting is just for the transport, so don't continue
|
|
// setting RPC level authenticaion.
|
|
//
|
|
{
|
|
BOOL fContinue;
|
|
|
|
|
|
if (RPC_C_AUTHN_NONE == ulAuthenticationService)
|
|
{
|
|
// RPC_C_AUTHN_NONE is a special case that is applied to both RPC and transport
|
|
// authentication.
|
|
*pStatus = SetTransportOption(RPC_C_OPT_MQ_AUTHN_SERVICE,ulAuthenticationService);
|
|
fContinue = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pStatus = SetTransportOption(RPC_C_OPT_MQ_AUTHN_SERVICE,ulAuthenticationService);
|
|
|
|
if (RPC_S_OK == *pStatus)
|
|
{
|
|
*pStatus = SetTransportOption(RPC_C_OPT_MQ_AUTHN_LEVEL,ulAuthenticationLevel);
|
|
fContinue = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*pStatus = RPC_S_CANNOT_SUPPORT;
|
|
fContinue = TRUE;
|
|
}
|
|
}
|
|
|
|
return fContinue;
|
|
}
|
|
|
|
|
|
|
|
DG_CASSOCIATION::DG_CASSOCIATION(
|
|
IN RPC_DATAGRAM_TRANSPORT * a_Transport,
|
|
IN LONG a_AssociationFlag,
|
|
IN DCE_BINDING * a_DceBinding,
|
|
IN BOOL a_Unique,
|
|
IN OUT RPC_STATUS * pStatus
|
|
) :
|
|
Mutex ( pStatus ),
|
|
TransportInterface ( a_Transport ),
|
|
AssociationFlag ( a_AssociationFlag ),
|
|
pDceBinding ( a_DceBinding ),
|
|
CurrentPduSize ( a_Transport->BasePduSize ),
|
|
fLoneBindingHandle ( (boolean) a_Unique ),
|
|
RemoteWindowSize ( 1 ),
|
|
ServerAddress ( 0 ),
|
|
ServerBootTime ( 0 ),
|
|
ReferenceCount ( 0 ),
|
|
BindingHandleReferences( 0 ),
|
|
InternalTableIndex ( -1 ),
|
|
KeepAliveHandle ( 0 ),
|
|
fServerSupportsAsync(FALSE),
|
|
fErrorFlag (FALSE)
|
|
/*++
|
|
|
|
Remarks:
|
|
|
|
Notice that the object is initialized so that the destructor can be called
|
|
even if the constructor bails out early.
|
|
|
|
Arguments:
|
|
|
|
pDceBinding - DCE_BINDING that we are associated with
|
|
pStatus - where failure codes go; should be RPC_S_OK on entry
|
|
|
|
--*/
|
|
{
|
|
ObjectType = DG_CASSOCIATION_TYPE;
|
|
|
|
LogEvent(SU_CASSOC, EV_CREATE, this, 0, *pStatus);
|
|
|
|
ResolvedEndpoint = NULL;
|
|
|
|
if (*pStatus != RPC_S_OK)
|
|
{
|
|
LogError(SU_CASSOC, EV_CREATE, this, 0, *pStatus);
|
|
return;
|
|
}
|
|
|
|
ServerAddress = this + 1;
|
|
|
|
ResolvedEndpoint = new RPC_CHAR[1+TransportInterface->EndpointStringSize];
|
|
if (!ResolvedEndpoint)
|
|
{
|
|
*pStatus = RPC_S_OUT_OF_MEMORY;
|
|
return;
|
|
}
|
|
|
|
*pStatus = TransportInterface->InitializeAddress(ServerAddress,
|
|
pDceBinding->InqNetworkAddress(),
|
|
pDceBinding->InqEndpoint(),
|
|
TRUE, // use cache
|
|
( a_AssociationFlag & BROADCAST) ? TRUE : FALSE
|
|
);
|
|
if (*pStatus == RPC_P_FOUND_IN_CACHE)
|
|
{
|
|
*pStatus = RPC_S_OK;
|
|
}
|
|
|
|
if (*pStatus)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LogEvent(SU_CASSOC, EV_START, this);
|
|
|
|
*pStatus = ActiveAssociations->Add( this );
|
|
if (*pStatus)
|
|
{
|
|
LogError(SU_CASSOC, EV_START, this, 0, *pStatus);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If this was created by a UNIQUE binding handle, we want connection keep-alives
|
|
// even though no context handles point to it. This will keep our connection
|
|
// alive on the server.
|
|
//
|
|
IncrementBindingRefCount( fLoneBindingHandle );
|
|
if (fLoneBindingHandle)
|
|
{
|
|
DelayedProcedures->Add(ContextHandleTimer, CXT_HANDLE_SWEEP_INTERVAL, FALSE);
|
|
}
|
|
|
|
LastReceiveTime = GetTickCount();
|
|
}
|
|
|
|
|
|
DG_CASSOCIATION::~DG_CASSOCIATION()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor for a DG_CASSOCIATION. This will free up the cached DG_CCALL
|
|
and deregister us from the transport.
|
|
|
|
Arguments:
|
|
|
|
<none>
|
|
|
|
Return Value:
|
|
|
|
<none>
|
|
|
|
--*/
|
|
{
|
|
LogEvent(SU_CASSOC, EV_DELETE, this);
|
|
|
|
ASSERT( !InvalidHandle(DG_CASSOCIATION_TYPE) );
|
|
|
|
PDG_CCONNECTION Connection;
|
|
DictionaryCursor cursor;
|
|
|
|
//
|
|
// Delete all calls for this association..
|
|
//
|
|
InactiveConnections.Reset(cursor);
|
|
while ( (Connection = InactiveConnections.Next(cursor)) != 0 )
|
|
{
|
|
InactiveConnections.Delete(Connection->AssociationKey);
|
|
Connection->CancelDelayedAck();
|
|
delete Connection;
|
|
}
|
|
|
|
delete pDceBinding;
|
|
delete ResolvedEndpoint;
|
|
delete KeepAliveHandle;
|
|
|
|
#if 0
|
|
char * FirstAddress;
|
|
char * SecondAddress;
|
|
|
|
FirstAddress = (char *) (this + 1);
|
|
SecondAddress = FirstAddress + TransportInterface->AddressSize;
|
|
|
|
ASSERT( ServerAddress == FirstAddress || ServerAddress == SecondAddress );
|
|
|
|
TransportInterface->CloseAddress( FirstAddress );
|
|
|
|
if (ServerAddress == SecondAddress)
|
|
{
|
|
TransportInterface->CloseAddress( SecondAddress );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CASSOCIATION::ToStringBinding (
|
|
OUT RPC_CHAR * * StringBinding,
|
|
IN RPC_UUID * ObjectUuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need to convert the binding handle into a string binding.
|
|
|
|
Arguments:
|
|
|
|
StringBinding - Returns the string representation of the binding
|
|
handle.
|
|
|
|
ObjectUuid - Supplies the object uuid of the binding handle which
|
|
is requesting that we create a string binding.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
RPC_S_OUT_OF_MEMORY
|
|
|
|
--*/
|
|
{
|
|
CLAIM_MUTEX Lock(Mutex);
|
|
|
|
*StringBinding = pDceBinding->StringBindingCompose(ObjectUuid);
|
|
if (*StringBinding == 0)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
BOOL
|
|
OptionalStringsEqual(
|
|
RPC_CHAR * String1,
|
|
RPC_CHAR * String2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compares two strings, checking for NULL pointers.
|
|
|
|
Arguments:
|
|
|
|
the strings
|
|
|
|
Return Value:
|
|
|
|
TRUE if they are equal
|
|
FALSE if they differ
|
|
|
|
--*/
|
|
|
|
{
|
|
if (String1 == String2)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (!String1 || !String2)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (0 == RpcpStringCompare(String1, String2))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DG_CASSOCIATION::ComparePartialBinding(
|
|
PDG_BINDING_HANDLE Binding,
|
|
void * InterfaceInformation
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks compatibility between the association and a partially-bound handle.
|
|
|
|
Arguments:
|
|
|
|
Binding - the binding handle
|
|
|
|
InterfaceInformation - a pointer to the RPC_INTERFACE to be used
|
|
|
|
Return Value:
|
|
|
|
TRUE if the association is compatible
|
|
FALSE if not
|
|
|
|
--*/
|
|
|
|
{
|
|
RPC_CHAR * String1;
|
|
RPC_CHAR * String2;
|
|
|
|
CLAIM_MUTEX lock( Mutex );
|
|
|
|
if (FALSE == OptionalStringsEqual(
|
|
pDceBinding->InqRpcProtocolSequence(),
|
|
Binding->pDceBinding->InqRpcProtocolSequence()
|
|
))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (FALSE == OptionalStringsEqual(
|
|
pDceBinding->InqNetworkAddress(),
|
|
Binding->pDceBinding->InqNetworkAddress()
|
|
))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (FALSE == OptionalStringsEqual(
|
|
pDceBinding->InqNetworkOptions(),
|
|
Binding->pDceBinding->InqNetworkOptions()
|
|
))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
RPC_UUID Object;
|
|
Binding->InquireObjectUuid(&Object);
|
|
|
|
return InterfaceAndObjectDict.Find(InterfaceInformation, &Object);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DG_CASSOCIATION::AddInterface(
|
|
void * InterfaceInformation,
|
|
RPC_UUID * ObjectUuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Declares that this association supports the given <interface, object uuid>
|
|
pair.
|
|
|
|
Arguments:
|
|
|
|
InterfaceInformation - a pointer to the RPC_INTERFACE
|
|
|
|
ObjectUuid - the object UUID
|
|
|
|
Return Value:
|
|
|
|
TRUE if the pair was added to the dictionary
|
|
FALSE if an error occurred
|
|
|
|
--*/
|
|
|
|
{
|
|
CLAIM_MUTEX Lock(Mutex);
|
|
|
|
return InterfaceAndObjectDict.Insert(InterfaceInformation, ObjectUuid);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DG_CASSOCIATION::RemoveInterface(
|
|
void * InterfaceInformation,
|
|
RPC_UUID * ObjectUuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Declares that this association no longer supports the given
|
|
<interface, object uuid> pair.
|
|
|
|
Arguments:
|
|
|
|
InterfaceInformation - a pointer to the RPC_INTERFACE
|
|
|
|
ObjectUuid - the object UUID
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if the pair was in the dictionary
|
|
FALSE if not
|
|
|
|
--*/
|
|
|
|
{
|
|
CLAIM_MUTEX Lock(Mutex);
|
|
|
|
return InterfaceAndObjectDict.Delete(InterfaceInformation, ObjectUuid);
|
|
}
|
|
|
|
|
|
DG_CCONNECTION::DG_CCONNECTION(
|
|
IN PDG_CASSOCIATION a_Association,
|
|
IN const CLIENT_AUTH_INFO * a_AuthInfo,
|
|
IN OUT RPC_STATUS * pStatus
|
|
) :
|
|
DG_COMMON_CONNECTION (a_Association->TransportInterface, pStatus),
|
|
Association (a_Association),
|
|
AuthInfo (a_AuthInfo, pStatus),
|
|
AssociationKey (-1),
|
|
TimeStamp (0),
|
|
SecurityContextId (0),
|
|
BindingHandle (0),
|
|
ThreadId (0),
|
|
CachedCalls (0),
|
|
ActiveCallHead (0),
|
|
ActiveCallTail (0),
|
|
CurrentCall (0),
|
|
AckPending (0),
|
|
AckOrphaned (FALSE),
|
|
SecurityBuffer (0),
|
|
SecurityBufferLength (0),
|
|
ServerResponded (FALSE),
|
|
CallbackCompleted (FALSE),
|
|
fServerSupportsAsync (a_Association->fServerSupportsAsync),
|
|
fSecurePacketReceived (FALSE),
|
|
InConnectionTable (FALSE),
|
|
fBusy (FALSE),
|
|
PossiblyRunDown (FALSE),
|
|
fAutoReconnect (FALSE),
|
|
fError (FALSE),
|
|
DelayedAckTimer (DelayedAckFn, 0)
|
|
{
|
|
ObjectType = DG_CCONNECTION_TYPE;
|
|
LogEvent(SU_CCONN, EV_CREATE, this, a_Association, *pStatus);
|
|
|
|
InterlockedIncrement(&ClientConnectionCount);
|
|
|
|
if (RPC_S_OK != *pStatus)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CurrentPduSize = Association->CurrentPduSize;
|
|
RemoteWindowSize = Association->RemoteWindowSize;
|
|
|
|
*pStatus = UuidCreate((UUID *) &ActivityNode.Uuid);
|
|
if (*pStatus == RPC_S_UUID_LOCAL_ONLY)
|
|
{
|
|
*pStatus = RPC_S_OK;
|
|
}
|
|
|
|
if (*pStatus)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE)
|
|
{
|
|
*pStatus = InitializeSecurityContext();
|
|
|
|
if (*pStatus == RPC_P_CONTINUE_NEEDED ||
|
|
*pStatus == RPC_P_COMPLETE_NEEDED ||
|
|
*pStatus == RPC_P_COMPLETE_AND_CONTINUE)
|
|
{
|
|
*pStatus = RPC_S_OK;
|
|
}
|
|
}
|
|
|
|
if (*pStatus)
|
|
{
|
|
LogError(SU_CCONN, EV_SEC_INIT1, this, IntToPtr(*pStatus), I_RpcGetExtendedError());
|
|
return;
|
|
}
|
|
}
|
|
|
|
DG_CCONNECTION::~DG_CCONNECTION()
|
|
{
|
|
InterlockedDecrement(&ClientConnectionCount);
|
|
LogEvent(SU_CCONN, EV_DELETE, this, Association);
|
|
|
|
#ifdef DEBUGRPC
|
|
BOOL Cancelled = DelayedProcedures->Cancel(&DelayedAckTimer);
|
|
|
|
ASSERT( !Cancelled );
|
|
#endif
|
|
|
|
while ( AckPending )
|
|
{
|
|
Sleep(10);
|
|
}
|
|
|
|
while (CachedCalls)
|
|
{
|
|
DG_CCALL * Call = CachedCalls;
|
|
|
|
CachedCalls = CachedCalls->Next;
|
|
|
|
delete Call;
|
|
}
|
|
|
|
if (SecurityBuffer)
|
|
{
|
|
delete SecurityBuffer;
|
|
}
|
|
}
|
|
|
|
|
|
long
|
|
DG_CCONNECTION::DecrementRefCountAndKeepMutex()
|
|
{
|
|
Mutex.VerifyOwned();
|
|
|
|
long Count = --ReferenceCount;
|
|
|
|
LogEvent(SU_CCONN, EV_DEC, this, 0, Count, TRUE);
|
|
|
|
// Since this->ThreadId is still nonzero, no other thread will increment
|
|
// the refcount behind our back.
|
|
|
|
if (0 == Count)
|
|
{
|
|
TimeStamp = GetTickCount();
|
|
|
|
fBusy = TRUE;
|
|
MutexClear();
|
|
|
|
fAutoReconnect = FALSE;
|
|
fError = FALSE;
|
|
|
|
Association->ReleaseConnection(this);
|
|
|
|
// the association may have been deleted
|
|
}
|
|
|
|
return Count;
|
|
}
|
|
|
|
long
|
|
DG_CCONNECTION::DecrementRefCount()
|
|
{
|
|
long Count = DecrementRefCountAndKeepMutex();
|
|
|
|
if (Count > 0)
|
|
{
|
|
MutexClear();
|
|
}
|
|
|
|
return Count;
|
|
}
|
|
|
|
|
|
void
|
|
DG_CCONNECTION::PostDelayedAck(
|
|
)
|
|
{
|
|
Mutex.VerifyOwned();
|
|
|
|
LogEvent(SU_CCONN, EV_PROC, this, IntToPtr((AckOrphaned << 16) | AckPending), 0x41736f50);
|
|
|
|
if (!AckPending)
|
|
{
|
|
IncrementRefCount();
|
|
|
|
++AckPending;
|
|
DelayedAckTimer.Initialize(DelayedAckFn, this);
|
|
DelayedProcedures->Add(&DelayedAckTimer, TWO_SECS_IN_MSEC, TRUE);
|
|
}
|
|
}
|
|
|
|
void
|
|
DelayedAckFn(
|
|
void * parm
|
|
)
|
|
{
|
|
PDG_CCONNECTION(parm)->SendDelayedAck();
|
|
}
|
|
|
|
void
|
|
DG_CCONNECTION::SendDelayedAck()
|
|
{
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_SEND_ACK, 1 ), this );
|
|
MutexRequest();
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_SEND_ACK, 2 ), this );
|
|
|
|
LogEvent(SU_CCONN, EV_PROC, this, IntToPtr((AckOrphaned << 16) | AckPending), 0x41646e53);
|
|
|
|
if (AckOrphaned)
|
|
{
|
|
AckOrphaned = FALSE;
|
|
DecrementRefCount();
|
|
return;
|
|
}
|
|
|
|
ASSERT( AckPending == 1 );
|
|
|
|
//
|
|
// Keep DG_CASSOCIATION::AllocateConnection() from taking the connection
|
|
// once AckPending drops to zero.
|
|
//
|
|
fBusy = TRUE;
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_SEND_ACK, 3 ), this );
|
|
--AckPending;
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_SEND_ACK, 4 ), this );
|
|
DecrementRefCountAndKeepMutex();
|
|
CurrentCall->SendAck();
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_SEND_ACK, 5 ), this );
|
|
CurrentCall->DecrementRefCount();
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_SEND_ACK, 6 ), this );
|
|
}
|
|
|
|
void
|
|
DG_CCONNECTION::CancelDelayedAck(
|
|
BOOL Flush
|
|
)
|
|
{
|
|
boolean Cancelled;
|
|
|
|
if (Flush)
|
|
{
|
|
if (!MutexTryRequest())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MutexRequest();
|
|
}
|
|
|
|
ASSERT( AckPending == 0 || AckPending == 1 );
|
|
|
|
Cancelled = (boolean) DelayedProcedures->Cancel(&DelayedAckTimer);
|
|
|
|
LogEvent(SU_CCONN, EV_PROC, this, IntToPtr((Cancelled << 16) | AckPending), 0x416e6143);
|
|
|
|
CallTestHook( MAKE_TEST_HOOK_ID( TH_DG_SEND_ACK, 7 ), this, (PVOID) Cancelled );
|
|
|
|
if (AckPending)
|
|
{
|
|
if (Flush)
|
|
{
|
|
CurrentCall->SendAck();
|
|
}
|
|
|
|
if (Cancelled)
|
|
{
|
|
// the proc was queued and had not yet fired
|
|
//
|
|
DecrementRefCountAndKeepMutex();
|
|
}
|
|
else
|
|
{
|
|
// the proc was called but had not yet taken the connection mutex.
|
|
// some callers of this procedure already hold the mutex, so we can't release it
|
|
// and let the othre thread run. So we set this flag to let it know not to send
|
|
// the ack. There is no need to keep a count: there is only one delayed-procedure
|
|
// thread, so only one such proc can be outstanding at a time.
|
|
|
|
// This assert doesn't work: if a proc is orphaned, then AckPending is TRUE.
|
|
// Until that thread finishes, every time another thread calls CancelDelayedAck
|
|
// it will come here.
|
|
//
|
|
// ASSERT( AckOrphaned == FALSE );
|
|
|
|
AckOrphaned = TRUE;
|
|
}
|
|
|
|
--AckPending;
|
|
CurrentCall->DecrementRefCount();
|
|
}
|
|
else
|
|
{
|
|
if (Cancelled)
|
|
{
|
|
ASSERT( 0 && "delayed ack queued but AckPending == 0" );
|
|
}
|
|
else
|
|
{
|
|
// The proc was not queued, or already ran to completion
|
|
//
|
|
}
|
|
MutexClear();
|
|
}
|
|
}
|
|
|
|
|
|
DG_CCALL::DG_CCALL(
|
|
IN PDG_CCONNECTION a_Connection,
|
|
OUT RPC_STATUS * pStatus
|
|
) : State (CallInit),
|
|
Connection (a_Connection),
|
|
InterfacePointer (0),
|
|
DelayedSendPending (0),
|
|
CancelPending (FALSE),
|
|
|
|
#pragma warning(disable:4355)
|
|
TransmitTimer (DelayedSendProc, this),
|
|
#pragma warning(default:4355)
|
|
|
|
DG_PACKET_ENGINE( DG_REQUEST,
|
|
DG_PACKET::AllocatePacket(a_Connection->TransportInterface->ExpectedPduSize),
|
|
pStatus )
|
|
{
|
|
|
|
Previous = Next = 0;
|
|
|
|
ObjectType = DG_CCALL_TYPE;
|
|
pAsync = 0;
|
|
|
|
InterlockedIncrement(&ClientCallCount);
|
|
|
|
if (*pStatus != RPC_S_OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CancelEventId = 1;
|
|
|
|
ReadConnectionInfo(a_Connection, 0);
|
|
|
|
pSavedPacket->Header.ActivityHint = 0xffff;
|
|
|
|
LogEvent(SU_CCALL, EV_CREATE, this, a_Connection);
|
|
}
|
|
|
|
DG_CCALL::~DG_CCALL()
|
|
{
|
|
LogEvent(SU_CCALL, EV_DELETE, this, Connection);
|
|
InterlockedDecrement(&ClientCallCount);
|
|
}
|
|
|
|
inline RPC_STATUS
|
|
DG_CCALL::GetInitialBuffer(
|
|
IN OUT RPC_MESSAGE * Message,
|
|
IN UUID *MyObjectUuid
|
|
)
|
|
{
|
|
AsyncStatus = RPC_S_ASYNC_CALL_PENDING;
|
|
|
|
Message->Handle = (RPC_BINDING_HANDLE) this;
|
|
|
|
Next = DG_CCALL_NOT_ACTIVE;
|
|
|
|
if (MyObjectUuid)
|
|
{
|
|
UuidSpecified = 1;
|
|
RpcpMemoryCopy(&ObjectUuid, MyObjectUuid, sizeof(UUID));
|
|
}
|
|
else if (Connection->BindingHandle->InqIfNullObjectUuid() == 0)
|
|
{
|
|
UuidSpecified = 1;
|
|
RpcpMemoryCopy(&ObjectUuid,
|
|
Connection->BindingHandle->InqPointerAtObjectUuid(),
|
|
sizeof(UUID));
|
|
}
|
|
else
|
|
{
|
|
UuidSpecified = 0;
|
|
}
|
|
|
|
if (GetTickCount() - Connection->TimeStamp > (3 * 60 * 1000))
|
|
{
|
|
Connection->PossiblyRunDown = TRUE;
|
|
}
|
|
|
|
RPC_STATUS Status = GetBuffer(Message);
|
|
|
|
if (Status)
|
|
{
|
|
Connection->MutexRequest();
|
|
DecrementRefCount();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
long
|
|
DG_CCALL::DecrementRefCount()
|
|
{
|
|
long Count = DecrementRefCountAndKeepMutex();
|
|
|
|
if (Count > 0)
|
|
{
|
|
Connection->MutexClear();
|
|
}
|
|
|
|
return Count;
|
|
}
|
|
|
|
long
|
|
DG_CCALL::DecrementRefCountAndKeepMutex()
|
|
{
|
|
Connection->Mutex.VerifyOwned();
|
|
|
|
--ReferenceCount;
|
|
|
|
LogEvent(SU_CCALL, EV_DEC, this, 0, ReferenceCount);
|
|
|
|
if (ReferenceCount == 0)
|
|
{
|
|
ASSERT( !DelayedSendPending );
|
|
|
|
if (SourceEndpoint)
|
|
{
|
|
EndpointManager->ReleaseEndpoint(SourceEndpoint);
|
|
SourceEndpoint = 0;
|
|
}
|
|
|
|
TimeStamp = GetTickCount();
|
|
|
|
// ASSERT( !LastReceiveBuffer );
|
|
|
|
// CheckForLeakedPackets();
|
|
|
|
SetState(CallInit);
|
|
|
|
Connection->EndCall(this);
|
|
Connection->AddCallToCache(this);
|
|
|
|
return Connection->DecrementRefCountAndKeepMutex();
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCONNECTION::TransferCallsToNewConnection(
|
|
PDG_CCALL FirstCall,
|
|
PDG_CCONNECTION NewConnection
|
|
)
|
|
{
|
|
PDG_CCALL Call;
|
|
PDG_CCALL NextCall;
|
|
|
|
int Count;
|
|
|
|
for (Call = FirstCall; Call; Call = NextCall)
|
|
{
|
|
LogError(SU_CCONN, EV_TRANSFER, this, Call);
|
|
|
|
NextCall = Call->Next;
|
|
|
|
EndCall(Call);
|
|
Count = DecrementRefCountAndKeepMutex();
|
|
|
|
NewConnection->BeginCall(Call);
|
|
NewConnection->IncrementRefCount();
|
|
|
|
Call->SwitchConnection(NewConnection);
|
|
}
|
|
|
|
//
|
|
// AllocateConnection() incremented the refcount so now we have one ref too many.
|
|
//
|
|
NewConnection->DecrementRefCountAndKeepMutex();
|
|
|
|
// <this> may have been deleted, I'm not sure.
|
|
|
|
if (Count)
|
|
{
|
|
MutexClear();
|
|
}
|
|
|
|
NewConnection->fAutoReconnect = TRUE;
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
void
|
|
DG_CASSOCIATION::DeleteIdleConnections(
|
|
long CurrentTime
|
|
)
|
|
{
|
|
DictionaryCursor cursor;
|
|
|
|
if (CurrentTime - LastScavengeTime < IDLE_CCONNECTION_SWEEP_INTERVAL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (fLoneBindingHandle)
|
|
{
|
|
//
|
|
// This was created by a binding that wanted exclusive use of the association.
|
|
// The way the cluster guys are using it, retiring connections would only be
|
|
// an intrusion.
|
|
//
|
|
return;
|
|
}
|
|
|
|
long ContextHandles = ActiveAssociations->GetContextHandleCount(this);
|
|
|
|
MutexRequest();
|
|
|
|
if (CurrentTime - LastScavengeTime < IDLE_CCONNECTION_SWEEP_INTERVAL )
|
|
{
|
|
MutexClear();
|
|
return;
|
|
}
|
|
|
|
InactiveConnections.Reset(cursor);
|
|
|
|
DG_CCONNECTION * Head = 0;
|
|
DG_CCONNECTION * Node = InactiveConnections.Next(cursor);
|
|
|
|
//
|
|
// We should never see a context handle on an association with no connections.
|
|
// We preserve one connection on the association so that we can send keep-alives.
|
|
//
|
|
if (ContextHandles > 0 && ActiveConnections.Size() == 0)
|
|
{
|
|
ASSERT( Node );
|
|
|
|
Node = InactiveConnections.Next(cursor);
|
|
}
|
|
|
|
while (Node)
|
|
{
|
|
if (CurrentTime - Node->TimeStamp > IDLE_CCONNECTION_LIFETIME)
|
|
{
|
|
InactiveConnections.Delete(Node->AssociationKey);
|
|
|
|
Node->Next = Head;
|
|
Head = Node;
|
|
}
|
|
|
|
Node = InactiveConnections.Next(cursor);
|
|
}
|
|
|
|
LastScavengeTime = CurrentTime;
|
|
|
|
MutexClear();
|
|
|
|
while (Head)
|
|
{
|
|
Node = Head->Next;
|
|
|
|
Head->CancelDelayedAck();
|
|
delete Head;
|
|
|
|
Head = Node;
|
|
}
|
|
}
|
|
|
|
|
|
DG_CCALL *
|
|
DG_CCONNECTION::FindIdleCalls(
|
|
long CurrentTime
|
|
)
|
|
{
|
|
Mutex.VerifyOwned();
|
|
|
|
if (CurrentTime - LastScavengeTime < IDLE_CCALL_SWEEP_INTERVAL )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
LastScavengeTime = CurrentTime;
|
|
|
|
|
|
DG_CCALL * Node;
|
|
|
|
for (Node = CachedCalls; Node; Node = Node->Next)
|
|
{
|
|
if (CurrentTime - Node->TimeStamp > IDLE_CCALL_LIFETIME )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Node)
|
|
{
|
|
DG_CCALL * Next = Node->Next;
|
|
Node->Next = 0;
|
|
Node = Next;
|
|
}
|
|
|
|
return Node;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::GetBuffer(
|
|
IN OUT PRPC_MESSAGE Message,
|
|
IN UUID *ObjectUuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is called to actually allocate memory for an rpc call.
|
|
|
|
Arguments:
|
|
|
|
Message - The RPC_MESSAGE structure associated with this call.
|
|
ObjectUuid - Ignored
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OUT_OF_MEMORY
|
|
RPC_S_OK
|
|
|
|
--*/
|
|
{
|
|
LogEvent(SU_CCALL, EV_PROC, this, IntToPtr(Message->BufferLength), 'G' + (('B' + (('u' + ('f' << 8)) << 8)) << 8));
|
|
|
|
RPC_STATUS Status = CommonGetBuffer(Message);
|
|
|
|
LogEvent(SU_CCALL, EV_BUFFER_OUT, this, Message->Buffer, Message->BufferLength);
|
|
if (Status)
|
|
{
|
|
LogError(SU_CCALL, EV_STATUS, this, 0, Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
void
|
|
DG_CCALL::FreeBuffer(
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called by stubs in order to free a marshalling buffer.
|
|
|
|
Arguments:
|
|
|
|
Message - The RPC_MESSAGE structure associated with this call.
|
|
|
|
Return Value:
|
|
|
|
<none>
|
|
|
|
--*/
|
|
{
|
|
FreePipeBuffer(Message);
|
|
|
|
Connection->MutexRequest();
|
|
DecrementRefCount();
|
|
}
|
|
|
|
void
|
|
DG_CCALL::FreePipeBuffer (
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by stubs to free a buffer used for marshalling pipe data.
|
|
|
|
Arguments:
|
|
|
|
Message - description of the buffer
|
|
|
|
Return Value:
|
|
|
|
z none
|
|
|
|
--*/
|
|
|
|
{
|
|
LogEvent(SU_CCALL, EV_PROC, this, Message->Buffer, 'F' + (('B' + (('u' + ('f' << 8)) << 8)) << 8));
|
|
|
|
CommonFreeBuffer(Message);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::ReallocPipeBuffer (
|
|
IN PRPC_MESSAGE Message,
|
|
IN unsigned int NewSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by stubs to change the size of a pipe buffer. If possible, the
|
|
buffer will be reallocated in place; otherwise, we will allocate a new
|
|
buffer and duplicate the existing data.
|
|
|
|
Arguments:
|
|
|
|
Message - (on entry) describes the existing buffer
|
|
(on exit) describes the new buffer
|
|
|
|
NewSize - new requested buffer size
|
|
|
|
Return Value:
|
|
|
|
mainly RPC_S_OK for success or RPC_S_OUT_OF_MEMORY for failure
|
|
|
|
--*/
|
|
|
|
{
|
|
LogEvent(SU_CCALL, EV_PROC, this, Message->Buffer, 'R' + (('B' + (('u' + ('f' << 8)) << 8)) << 8));
|
|
|
|
RPC_STATUS Status = CommonReallocBuffer(Message, NewSize);
|
|
|
|
LogEvent(SU_CCALL, EV_BUFFER_OUT, this, Message->Buffer, Message->BufferLength);
|
|
if (Status)
|
|
{
|
|
LogError(SU_CCALL, EV_STATUS, this, 0, Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
void
|
|
DG_CCALL::BuildNcaPacketHeader(
|
|
PNCA_PACKET_HEADER Header,
|
|
PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given an input RPC_MESSAGE, builds a nca packet header.
|
|
|
|
Arguments:
|
|
|
|
pNcaPacketHeader - Where to build the new packet header.
|
|
|
|
Message - The original RPC_MESSAGE.
|
|
|
|
Return Value:
|
|
|
|
<none>
|
|
|
|
--*/
|
|
{
|
|
PRPC_CLIENT_INTERFACE pCli = (PRPC_CLIENT_INTERFACE) (Message->RpcInterfaceInformation);
|
|
RPC_UUID * pUuid = (RPC_UUID *) (&(pCli->InterfaceId.SyntaxGUID));
|
|
|
|
Header->InterfaceId = *pUuid;
|
|
|
|
if (UuidSpecified)
|
|
{
|
|
RpcpMemoryCopy(&(Header->ObjectId), &ObjectUuid, sizeof(UUID));
|
|
}
|
|
else
|
|
{
|
|
RpcpMemorySet(&Header->ObjectId, 0, sizeof(UUID));
|
|
}
|
|
|
|
Header->InterfaceVersion.MajorVersion = pCli->InterfaceId.SyntaxVersion.MajorVersion;
|
|
Header->InterfaceVersion.MinorVersion = pCli->InterfaceId.SyntaxVersion.MinorVersion;
|
|
|
|
Header->SequenceNumber = SequenceNumber;
|
|
Header->OperationNumber = (unsigned short) Message->ProcNum;
|
|
Header->ServerBootTime = Connection->Association->ServerBootTime;
|
|
|
|
Header->InterfaceHint = 0xffff;
|
|
Header->PacketType = DG_REQUEST;
|
|
Header->PacketFlags = (unsigned char) RpcToPacketFlagsArray[Message->RpcFlags & RPC_NCA_PACKET_FLAGS];
|
|
Header->PacketFlags2 = 0;
|
|
}
|
|
|
|
|
|
inline RPC_STATUS
|
|
DG_CCONNECTION::UpdateServerAddress(
|
|
IN DG_PACKET * Packet,
|
|
IN DG_TRANSPORT_ADDRESS Address
|
|
)
|
|
{
|
|
ServerResponded = TRUE;
|
|
|
|
Association->CurrentPduSize = CurrentPduSize;
|
|
Association->RemoteWindowSize = RemoteWindowSize;
|
|
|
|
return Association->UpdateAssociationWithAddress( Packet, Address );
|
|
}
|
|
|
|
|
|
void
|
|
DG_CCALL::SendAck(
|
|
)
|
|
{
|
|
LogEvent(SU_CCALL, EV_ACK, this);
|
|
|
|
pSavedPacket->Header.PacketType = DG_ACK;
|
|
pSavedPacket->SetPacketBodyLen(0);
|
|
pSavedPacket->SetFragmentNumber(ReceiveFragmentBase);
|
|
|
|
SetSerialNumber(&pSavedPacket->Header, SendSerialNumber);
|
|
|
|
Connection->SealAndSendPacket(SourceEndpoint, 0, &pSavedPacket->Header, 0);
|
|
|
|
if (FALSE == SourceEndpoint->Async)
|
|
{
|
|
SourceEndpoint->Flags |= PENALTY_BOX;
|
|
}
|
|
}
|
|
|
|
inline RPC_STATUS
|
|
DG_CCALL::SendPing(
|
|
)
|
|
{
|
|
pSavedPacket->Header.PacketType = DG_PING;
|
|
pSavedPacket->Header.PacketFlags &= DG_PF_IDEMPOTENT;
|
|
pSavedPacket->SetPacketBodyLen(0);
|
|
|
|
AddSerialNumber(&pSavedPacket->Header);
|
|
|
|
unsigned Frag = (pSavedPacket->Header.PacketType << 16) | pSavedPacket->GetFragmentNumber();
|
|
LogEvent(SU_CCALL, EV_PKT_OUT, this, 0, Frag);
|
|
|
|
RPC_STATUS Status = Connection->SealAndSendPacket(SourceEndpoint, 0, &pSavedPacket->Header, 0);
|
|
|
|
++SendSerialNumber;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
PDG_CCONNECTION
|
|
MapGenericHandleToConnection(
|
|
handle_t Handle,
|
|
UUID * Uuid
|
|
)
|
|
{
|
|
PDG_CCONNECTION Connection;
|
|
|
|
ASSERT( Handle );
|
|
|
|
if (PMESSAGE_OBJECT(Handle)->Type(DG_CCONNECTION_TYPE))
|
|
{
|
|
Connection = PDG_CCONNECTION(Handle);
|
|
|
|
Connection->MutexRequest();
|
|
}
|
|
else
|
|
{
|
|
ASSERT( PMESSAGE_OBJECT(Handle)->Type(DG_CALLBACK_TYPE) );
|
|
|
|
Connection = PDG_CLIENT_CALLBACK(Handle)->Connection;
|
|
if (Connection)
|
|
{
|
|
Connection->MutexRequest();
|
|
}
|
|
else
|
|
{
|
|
Connection = ClientConnections->Lookup( (RPC_UUID *) Uuid );
|
|
PDG_CLIENT_CALLBACK(Handle)->Connection = Connection;
|
|
}
|
|
}
|
|
|
|
return Connection;
|
|
}
|
|
|
|
#define CCC_SEQUENCE 0x0001
|
|
#define CCC_CAS 0x0002
|
|
#define CCC_AUTH 0x0004
|
|
#define CCC_ASYNC_OK 0x0008
|
|
#define CCC_AUTH_MORE 0x0010
|
|
|
|
void
|
|
ConvCore(
|
|
DWORD Bits,
|
|
PRPC_ASYNC_STATE AsyncHandle,
|
|
handle_t Handle,
|
|
UUID * Uuid,
|
|
unsigned long ServerBootTime,
|
|
byte * InData,
|
|
long InLength,
|
|
long OutMaxLength,
|
|
unsigned long * SequenceNumber,
|
|
UUID * pCASUuid,
|
|
byte * OutData,
|
|
long * pOutLength,
|
|
error_status_t *Status
|
|
)
|
|
{
|
|
if (pOutLength)
|
|
{
|
|
*pOutLength = 0;
|
|
}
|
|
|
|
PDG_CCONNECTION Connection = MapGenericHandleToConnection(Handle, Uuid);
|
|
|
|
LogEvent(SU_CCONN, EV_CALLBACK, Connection, 0, Bits);
|
|
|
|
if (!Connection)
|
|
{
|
|
*Status = NCA_STATUS_BAD_ACTID;
|
|
|
|
RpcAsyncCompleteCall(AsyncHandle, 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// See if this activity id has a call in progress.
|
|
//
|
|
if (Connection->ActivityNode.CompareUuid(Uuid) != 0)
|
|
{
|
|
Connection->MutexClear();
|
|
*Status = NCA_STATUS_BAD_ACTID;
|
|
RpcAsyncCompleteCall(AsyncHandle, 0);
|
|
return;
|
|
}
|
|
|
|
*Status = RPC_S_OK;
|
|
|
|
if (Connection->Association->ServerBootTime == 0)
|
|
{
|
|
//
|
|
// the server is responding to our first call.
|
|
//
|
|
Connection->Association->ServerBootTime = ServerBootTime;
|
|
}
|
|
else if (Connection->Association->ServerBootTime != ServerBootTime)
|
|
{
|
|
//
|
|
// The server crashed.
|
|
//
|
|
Connection->MutexClear();
|
|
*Status = NCA_STATUS_YOU_CRASHED;
|
|
RpcAsyncCompleteCall(AsyncHandle, 0);
|
|
return;
|
|
}
|
|
|
|
if (Bits & CCC_ASYNC_OK)
|
|
{
|
|
Connection->EnableOverlappedCalls();
|
|
}
|
|
|
|
if (Bits & CCC_SEQUENCE)
|
|
{
|
|
*SequenceNumber = Connection->GetSequenceNumber();
|
|
}
|
|
|
|
if (Bits & CCC_CAS)
|
|
{
|
|
ASSERT( ActiveAssociations->fCasUuidReady );
|
|
|
|
*pCASUuid = ActiveAssociations->CasUuid;
|
|
}
|
|
|
|
if (Bits & CCC_AUTH)
|
|
{
|
|
if (Connection->PossiblyRunDown)
|
|
{
|
|
Connection->PossiblyRunDown = FALSE;
|
|
Connection->fSecurePacketReceived = FALSE;
|
|
}
|
|
|
|
*Status = MapToNcaStatusCode(
|
|
Connection->DealWithAuthCallback(
|
|
InData,
|
|
InLength,
|
|
OutData,
|
|
OutMaxLength,
|
|
pOutLength
|
|
)
|
|
);
|
|
}
|
|
|
|
if (Bits & CCC_AUTH_MORE)
|
|
{
|
|
*Status = MapToNcaStatusCode(
|
|
Connection->DealWithAuthMore(
|
|
InLength,
|
|
OutData,
|
|
OutMaxLength,
|
|
pOutLength
|
|
)
|
|
);
|
|
}
|
|
|
|
if (RPC_S_OK == *Status)
|
|
{
|
|
Connection->CallbackCompleted = TRUE;
|
|
}
|
|
|
|
Connection->MutexClear();
|
|
RpcAsyncCompleteCall(AsyncHandle, 0);
|
|
}
|
|
|
|
void
|
|
conv_are_you_there(
|
|
PRPC_ASYNC_STATE AsyncHandle,
|
|
handle_t Handle,
|
|
UUID * Uuid,
|
|
unsigned long ServerBootTime,
|
|
error_status_t *Status
|
|
)
|
|
{
|
|
ConvCore(0, // bits
|
|
AsyncHandle,
|
|
Handle,
|
|
Uuid,
|
|
ServerBootTime,
|
|
0, // in auth data
|
|
0, // in auth data length
|
|
0, // out auth data max length
|
|
0, // sequence number
|
|
0, // CAS UUID
|
|
0, // out auth data
|
|
0, // out auth data length
|
|
Status
|
|
);
|
|
}
|
|
|
|
|
|
void
|
|
conv_who_are_you(
|
|
PRPC_ASYNC_STATE AsyncHandle,
|
|
IN handle_t Handle,
|
|
IN UUID * Uuid,
|
|
IN unsigned long ServerBootTime,
|
|
OUT unsigned long * SequenceNumber,
|
|
OUT error_status_t * Status
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the conv_who_are_you callback routine that the server will
|
|
call to check if it crashed.
|
|
|
|
Arguments:
|
|
|
|
pUuid - Activity Uuid.
|
|
|
|
ServerBootTime - The server's record of its boot time.
|
|
|
|
SequenceNumber - We return our record of our sequence number.
|
|
|
|
Status - 0 if we think things are ok, else an NCA error code
|
|
|
|
Return Value:
|
|
|
|
<none>
|
|
--*/
|
|
|
|
{
|
|
ConvCore(CCC_SEQUENCE,
|
|
AsyncHandle,
|
|
Handle,
|
|
Uuid,
|
|
ServerBootTime,
|
|
0, // in auth data
|
|
0, // in auth data length
|
|
0, // out auth data max length
|
|
SequenceNumber,
|
|
0, // CAS UUID
|
|
0, // out auth data
|
|
0, // out auth data length
|
|
Status
|
|
);
|
|
}
|
|
|
|
|
|
void
|
|
conv_who_are_you2(
|
|
PRPC_ASYNC_STATE AsyncHandle,
|
|
IN handle_t Handle,
|
|
IN UUID * Uuid,
|
|
IN unsigned long ServerBootTime,
|
|
OUT unsigned long * SequenceNumber,
|
|
OUT UUID * pCASUuid,
|
|
OUT error_status_t * Status
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the conv_who_are_you callback routine that the server will
|
|
call to check if it crashed.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
<none>
|
|
--*/
|
|
|
|
{
|
|
ConvCore(CCC_SEQUENCE | CCC_CAS,
|
|
AsyncHandle,
|
|
Handle,
|
|
Uuid,
|
|
ServerBootTime,
|
|
0, // in auth data
|
|
0, // in auth data length
|
|
0, // out auth data max length
|
|
SequenceNumber,
|
|
pCASUuid,
|
|
0, // out auth data
|
|
0, // out auth data length
|
|
Status
|
|
);
|
|
}
|
|
|
|
|
|
void
|
|
conv_who_are_you_auth(
|
|
PRPC_ASYNC_STATE AsyncHandle,
|
|
handle_t Handle,
|
|
UUID * Uuid,
|
|
unsigned long ServerBootTime,
|
|
byte * InData,
|
|
long InLength,
|
|
long OutMaxLength,
|
|
unsigned long * SequenceNumber,
|
|
UUID * pCASUuid,
|
|
byte * OutData,
|
|
long * pOutLength,
|
|
error_status_t *Status
|
|
)
|
|
{
|
|
ConvCore(CCC_SEQUENCE | CCC_CAS | CCC_AUTH,
|
|
AsyncHandle,
|
|
Handle,
|
|
Uuid,
|
|
ServerBootTime,
|
|
InData,
|
|
InLength,
|
|
OutMaxLength,
|
|
SequenceNumber,
|
|
pCASUuid,
|
|
OutData,
|
|
pOutLength,
|
|
Status
|
|
);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
conv_who_are_you_auth_more(
|
|
PRPC_ASYNC_STATE AsyncHandle,
|
|
handle_t Handle,
|
|
UUID * Uuid,
|
|
unsigned long ServerBootTime,
|
|
long Index,
|
|
long OutMaxLength,
|
|
byte * OutData,
|
|
long * pOutLength,
|
|
error_status_t *Status
|
|
)
|
|
{
|
|
ConvCore(CCC_AUTH_MORE,
|
|
AsyncHandle,
|
|
Handle,
|
|
Uuid,
|
|
ServerBootTime,
|
|
0,
|
|
Index,
|
|
OutMaxLength,
|
|
0,
|
|
0,
|
|
OutData,
|
|
pOutLength,
|
|
Status
|
|
);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
ms_conv_are_you_there(
|
|
PRPC_ASYNC_STATE AsyncHandle,
|
|
handle_t Handle,
|
|
UUID * Uuid,
|
|
unsigned long ServerBootTime,
|
|
error_status_t *Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the conv_who_are_you callback routine that the server will
|
|
call to check if it crashed.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
<none>
|
|
--*/
|
|
|
|
{
|
|
ConvCore(CCC_ASYNC_OK,
|
|
AsyncHandle,
|
|
Handle,
|
|
Uuid,
|
|
ServerBootTime,
|
|
0, // in auth data
|
|
0, // in auth data length
|
|
0, // out auth data max length
|
|
0, // sequence number
|
|
0, // CAS UUID
|
|
0, // out auth data
|
|
0, // out auth data length
|
|
Status
|
|
);
|
|
}
|
|
|
|
|
|
void
|
|
ms_conv_who_are_you2(
|
|
PRPC_ASYNC_STATE AsyncHandle,
|
|
IN handle_t Handle,
|
|
IN UUID * Uuid,
|
|
IN unsigned long ServerBootTime,
|
|
OUT unsigned long * SequenceNumber,
|
|
OUT UUID * pCASUuid,
|
|
OUT error_status_t * Status
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the conv_who_are_you callback routine that the server will
|
|
call to check if it crashed.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
<none>
|
|
--*/
|
|
|
|
{
|
|
ConvCore(CCC_ASYNC_OK | CCC_SEQUENCE | CCC_CAS,
|
|
AsyncHandle,
|
|
Handle,
|
|
Uuid,
|
|
ServerBootTime,
|
|
0, // in auth data
|
|
0, // in auth data length
|
|
0, // out auth data max length
|
|
SequenceNumber,
|
|
pCASUuid,
|
|
0, // out auth data
|
|
0, // out auth data length
|
|
Status
|
|
);
|
|
}
|
|
|
|
|
|
void
|
|
ms_conv_who_are_you_auth(
|
|
PRPC_ASYNC_STATE AsyncHandle,
|
|
handle_t Handle,
|
|
UUID * Uuid,
|
|
unsigned long ServerBootTime,
|
|
byte * InData,
|
|
long InLength,
|
|
long OutMaxLength,
|
|
unsigned long * SequenceNumber,
|
|
UUID * pCASUuid,
|
|
byte * OutData,
|
|
long * pOutLength,
|
|
error_status_t *Status
|
|
)
|
|
{
|
|
ConvCore(CCC_ASYNC_OK | CCC_SEQUENCE | CCC_CAS | CCC_AUTH,
|
|
AsyncHandle,
|
|
Handle,
|
|
Uuid,
|
|
ServerBootTime,
|
|
InData,
|
|
InLength,
|
|
OutMaxLength,
|
|
SequenceNumber,
|
|
pCASUuid,
|
|
OutData,
|
|
pOutLength,
|
|
Status
|
|
);
|
|
}
|
|
|
|
|
|
void
|
|
DG_CCONNECTION::EnableOverlappedCalls()
|
|
{
|
|
Association->fServerSupportsAsync = TRUE;
|
|
|
|
if (FALSE == fServerSupportsAsync)
|
|
{
|
|
fServerSupportsAsync = TRUE;
|
|
|
|
MutexRequest();
|
|
MaybeTransmitNextCall();
|
|
MutexClear();
|
|
}
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::GetEndpoint(
|
|
DWORD EndpointFlags
|
|
)
|
|
{
|
|
SourceEndpoint = EndpointManager->RequestEndpoint(
|
|
Connection->TransportInterface,
|
|
(pAsync) ? TRUE : FALSE,
|
|
EndpointFlags
|
|
);
|
|
|
|
if (!SourceEndpoint)
|
|
{
|
|
return RPC_S_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
if (pSavedPacket->MaxDataLength < SourceEndpoint->Stats.PreferredPduSize)
|
|
{
|
|
PDG_PACKET NewPacket = DG_PACKET::AllocatePacket(SourceEndpoint->Stats.PreferredPduSize);
|
|
if (!NewPacket)
|
|
{
|
|
EndpointManager->ReleaseEndpoint(SourceEndpoint);
|
|
SourceEndpoint = 0;
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
NewPacket->Header = pSavedPacket->Header;
|
|
|
|
FreePacket(pSavedPacket);
|
|
pSavedPacket = NewPacket;
|
|
}
|
|
|
|
//
|
|
// If there is a chance that the endpoint has queued ICMP rejects, drain them.
|
|
//
|
|
if (FALSE == SourceEndpoint->Async &&
|
|
(SourceEndpoint->Flags & PENALTY_BOX))
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
do
|
|
{
|
|
PDG_PACKET Packet = 0;
|
|
DG_TRANSPORT_ADDRESS ReceiveAddress = 0;
|
|
unsigned Length = 0;
|
|
void * Buffer = 0;
|
|
|
|
Status = Connection->TransportInterface->SyncReceive(
|
|
&SourceEndpoint->TransportEndpoint,
|
|
&ReceiveAddress,
|
|
&Length,
|
|
&Buffer,
|
|
0
|
|
);
|
|
|
|
LogEvent( SU_CCALL, EV_PKT_IN, Connection, Buffer, Status);
|
|
|
|
if (Buffer)
|
|
{
|
|
Packet = DG_PACKET::FromPacketHeader(Buffer);
|
|
Packet->DataLength = Length;
|
|
|
|
FreePacket(Packet);
|
|
}
|
|
|
|
if (Status == RPC_P_PORT_DOWN)
|
|
{
|
|
Status = 0;
|
|
}
|
|
|
|
}
|
|
while ( !Status );
|
|
}
|
|
|
|
SourceEndpoint->Flags &= ~(PENALTY_BOX);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::BeforeSendReceive(
|
|
PRPC_MESSAGE Message
|
|
)
|
|
{
|
|
DWORD EndpointFlags;
|
|
RPC_STATUS Status;
|
|
|
|
NotificationIssued = -1;
|
|
|
|
ASSERT( 0 == (Connection->BindingHandle->EndpointFlags & PORT_FOR_MAYBE_CALLS));
|
|
|
|
EndpointFlags = Connection->BindingHandle->EndpointFlags;
|
|
|
|
if (Message->RpcFlags & RPC_NCA_FLAGS_MAYBE)
|
|
{
|
|
if (FALSE == Connection->Association->TransportInterface->IsMessageTransport)
|
|
{
|
|
EndpointFlags |= PORT_FOR_MAYBE_CALLS;
|
|
}
|
|
}
|
|
|
|
Status = GetEndpoint(EndpointFlags);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
Status = Connection->BeginCall(this);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
EndpointManager->ReleaseEndpoint(SourceEndpoint);
|
|
SourceEndpoint = 0;
|
|
return Status;
|
|
}
|
|
|
|
InterfacePointer = (PRPC_CLIENT_INTERFACE) Message->RpcInterfaceInformation;
|
|
|
|
UnansweredRequestCount = 0;
|
|
|
|
NewCall();
|
|
|
|
//
|
|
// Set transport specific options for this binding handle (if any).
|
|
// NOTE: These options are from RpcBindingSetOption().
|
|
//
|
|
if ( (Connection->BindingHandle->pvTransportOptions)
|
|
&& (Connection->Association->TransportInterface->ImplementOptions) )
|
|
{
|
|
Status = Connection->Association->TransportInterface->ImplementOptions(
|
|
SourceEndpoint->TransportEndpoint,
|
|
Connection->BindingHandle->pvTransportOptions);
|
|
}
|
|
|
|
//
|
|
// Fill in common fields of the send packet.
|
|
//
|
|
BuildNcaPacketHeader(&pSavedPacket->Header, Message);
|
|
|
|
BasePacketFlags = pSavedPacket->Header.PacketFlags;
|
|
|
|
SetState(CallQuiescent);
|
|
|
|
ForceAck = FALSE;
|
|
AllArgsSent = FALSE;
|
|
StaticArgsSent = FALSE;
|
|
|
|
ASSERT( !DelayedSendPending );
|
|
|
|
//#ifdef NTENV
|
|
|
|
if (!pAsync)
|
|
{
|
|
Status = RegisterForCancels(this);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
Connection->EndCall(this);
|
|
|
|
EndpointManager->ReleaseEndpoint(SourceEndpoint);
|
|
SourceEndpoint = 0;
|
|
|
|
return Status;
|
|
}
|
|
}
|
|
//#endif
|
|
|
|
//
|
|
// If this is a call on the "conv" interface, we should set it also.
|
|
//
|
|
PRPC_CLIENT_INTERFACE pCli = (PRPC_CLIENT_INTERFACE) (Message->RpcInterfaceInformation);
|
|
RPC_UUID * pUuid = (RPC_UUID *) (&(pCli->InterfaceId.SyntaxGUID));
|
|
|
|
if (0 == pUuid->MatchUuid((RPC_UUID *) &((PRPC_SERVER_INTERFACE) conv_ServerIfHandle)->InterfaceId.SyntaxGUID ))
|
|
{
|
|
BasePacketFlags2 = DG_PF2_UNRELATED;
|
|
if (Previous)
|
|
{
|
|
Previous->ForceAck = TRUE;
|
|
}
|
|
}
|
|
|
|
unsigned TimeoutLevel = Connection->BindingHandle->InqComTimeout();
|
|
|
|
if (Message->RpcFlags & RPC_NCA_FLAGS_BROADCAST)
|
|
{
|
|
ReceiveTimeout = 3000;
|
|
TimeoutLimit = 1000 * (TimeoutLevel+1)/2;
|
|
}
|
|
else if (TimeoutLevel == RPC_C_BINDING_INFINITE_TIMEOUT)
|
|
{
|
|
ReceiveTimeout = 5000;
|
|
TimeoutLimit = 0x7fffffff;
|
|
}
|
|
else if (Connection->TransportInterface->IsMessageTransport)
|
|
{
|
|
ReceiveTimeout = 5000;
|
|
TimeoutLimit = 300000 + 10000 * ( 1 << TimeoutLevel );
|
|
}
|
|
else
|
|
{
|
|
ReceiveTimeout = 250 + 250 * (TimeoutLevel+1)/2;
|
|
TimeoutLimit = 1000 * ( 1 << TimeoutLevel );
|
|
}
|
|
|
|
LastReceiveTime = GetTickCount();
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::AfterSendReceive(
|
|
PRPC_MESSAGE Message,
|
|
RPC_STATUS Status
|
|
)
|
|
{
|
|
DG_BINDING_HANDLE * OldBinding = 0;
|
|
|
|
Connection->Mutex.VerifyOwned();
|
|
|
|
PDG_CASSOCIATION Association = Connection->Association;
|
|
|
|
CancelDelayedSend();
|
|
|
|
//#ifdef NTENV
|
|
|
|
if (!pAsync)
|
|
{
|
|
EVAL_AND_ASSERT(RPC_S_OK == UnregisterForCancels());
|
|
}
|
|
|
|
//#endif
|
|
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
ASSERT( !Buffer );
|
|
|
|
Connection->PossiblyRunDown = FALSE;
|
|
|
|
if (0 == (Message->RpcFlags & RPC_NCA_FLAGS_MAYBE))
|
|
{
|
|
Association->ClearErrorFlag();
|
|
}
|
|
|
|
// NOTE: No ACK for [message] calls.
|
|
if ( (ForceAck) && !(Message->RpcFlags & RPCFLG_MESSAGE) )
|
|
{
|
|
SendAck();
|
|
}
|
|
else if (Next)
|
|
{
|
|
//
|
|
// Don't ACK because we will transmit the next queued call momentarily.
|
|
//
|
|
}
|
|
else if (0 == (BufferFlags & RPC_NCA_FLAGS_IDEMPOTENT) ||
|
|
Message->BufferLength > MaxFragmentSize)
|
|
{
|
|
//
|
|
// NOTE: We don't need an ACK for [message] calls...
|
|
//
|
|
if ( !(Message->RpcFlags & RPCFLG_MESSAGE) &&
|
|
!(Message->RpcFlags & RPC_NCA_FLAGS_MAYBE) )
|
|
{
|
|
++ReferenceCount;
|
|
LogEvent(SU_CCALL, EV_INC, this, 0, ReferenceCount);
|
|
Connection->PostDelayedAck();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FALSE == SourceEndpoint->Async)
|
|
{
|
|
SourceEndpoint->Flags |= PENALTY_BOX;
|
|
}
|
|
|
|
Status = MapErrorCode(Status);
|
|
|
|
CleanupSendWindow();
|
|
CleanupReceiveWindow();
|
|
|
|
if (RPC_S_SERVER_UNAVAILABLE == Status ||
|
|
RPC_S_UNKNOWN_IF == Status ||
|
|
RPC_S_CALL_FAILED == Status ||
|
|
RPC_S_CALL_FAILED_DNE == Status ||
|
|
RPC_S_COMM_FAILURE == Status ||
|
|
RPC_S_CALL_CANCELLED == Status ||
|
|
RPC_S_PROTOCOL_ERROR == Status
|
|
)
|
|
{
|
|
Connection->fError = TRUE;
|
|
Association->SetErrorFlag();
|
|
OldBinding = Connection->BindingHandle;
|
|
OldBinding->IncrementRefCount();
|
|
}
|
|
}
|
|
|
|
SetState(CallComplete);
|
|
|
|
pAsync = 0;
|
|
|
|
long CurrentTime = GetTickCount();
|
|
DG_CCALL * IdleCalls = Connection->FindIdleCalls(CurrentTime);
|
|
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
Connection->MutexClear();
|
|
|
|
//
|
|
// Record that this interface is valid for this association.
|
|
//
|
|
Association->AddInterface(Message->RpcInterfaceInformation, &pSavedPacket->Header.ObjectId);
|
|
}
|
|
else
|
|
{
|
|
RPC_UUID Object;
|
|
|
|
Object.CopyUuid(&pSavedPacket->Header.ObjectId);
|
|
Association->IncrementRefCount();
|
|
|
|
//
|
|
// If the call is dying, we need to free the current buffer here
|
|
// instead of letting NDR do it on the usual schedule.
|
|
//
|
|
if (0 == DecrementRefCount())
|
|
{
|
|
FreePipeBuffer(Message);
|
|
Message->Handle = 0;
|
|
}
|
|
|
|
Association->RemoveInterface(Message->RpcInterfaceInformation, &Object);
|
|
Association->DecrementRefCount();
|
|
}
|
|
|
|
if (OldBinding)
|
|
{
|
|
OldBinding->DisassociateFromServer();
|
|
OldBinding->DecrementRefCount();
|
|
}
|
|
|
|
while (IdleCalls)
|
|
{
|
|
DG_CCALL * Next = IdleCalls->Next;
|
|
|
|
delete IdleCalls;
|
|
|
|
IdleCalls = Next;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::MapErrorCode(
|
|
RPC_STATUS Status
|
|
)
|
|
{
|
|
LogError(SU_CCALL, EV_STATUS, this, 0, Status);
|
|
|
|
//
|
|
// Map security errors to access-denied.
|
|
//
|
|
if (0x80090000UL == (Status & 0xffff0000UL))
|
|
{
|
|
#ifdef DEBUGRPC
|
|
if (Status != SEC_E_NO_IMPERSONATION &&
|
|
Status != SEC_E_UNSUPPORTED_FUNCTION )
|
|
{
|
|
PrintToDebugger("RPC DG: mapping security error %lx to access-denied\n", Status);
|
|
}
|
|
#endif
|
|
Status = RPC_S_SEC_PKG_ERROR;
|
|
}
|
|
|
|
//
|
|
// We have to return CALL_FAILED if all the [in] static parms have
|
|
// been sent, even if they weren't acknowledged.
|
|
//
|
|
if (RPC_P_HOST_DOWN == Status ||
|
|
RPC_P_PORT_DOWN == Status ||
|
|
RPC_P_SEND_FAILED == Status ||
|
|
RPC_P_RECEIVE_FAILED == Status ||
|
|
RPC_P_TIMEOUT == Status )
|
|
{
|
|
if (Connection->CallbackCompleted &&
|
|
!(BufferFlags & RPC_NCA_FLAGS_IDEMPOTENT) &&
|
|
StaticArgsSent)
|
|
{
|
|
Status = RPC_S_CALL_FAILED;
|
|
}
|
|
else if (Connection->ServerResponded)
|
|
{
|
|
Status = RPC_S_CALL_FAILED_DNE;
|
|
}
|
|
else
|
|
{
|
|
Status = RPC_S_SERVER_UNAVAILABLE;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::SendReceive(
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
{
|
|
LogEvent(SU_CCALL, EV_PROC, this, Message->Buffer, 0x52646e53);
|
|
|
|
RPC_STATUS Status;
|
|
|
|
ASSERT( !(Message->RpcFlags & RPC_BUFFER_ASYNC) && !pAsync);
|
|
|
|
Connection->MutexRequest();
|
|
|
|
Status = BeforeSendReceive(Message);
|
|
if (Status)
|
|
{
|
|
FreeBuffer(Message);
|
|
|
|
Connection->MutexClear();
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// [maybe], [maybe, broadcast] and [message] calls.
|
|
//
|
|
if ( (Message->RpcFlags & RPC_NCA_FLAGS_MAYBE)
|
|
|| (Message->RpcFlags & RPCFLG_MESSAGE) )
|
|
{
|
|
Message->RpcFlags |= RPC_NCA_FLAGS_MAYBE;
|
|
|
|
Status = MaybeSendReceive(Message);
|
|
|
|
return AfterSendReceive(Message, Status);
|
|
}
|
|
|
|
if (Message->RpcFlags & RPC_NCA_FLAGS_BROADCAST)
|
|
{
|
|
if (Message->BufferLength > SourceEndpoint->Stats.PreferredPduSize)
|
|
{
|
|
FreeBuffer(Message);
|
|
Connection->MutexClear();
|
|
|
|
return RPC_S_SERVER_UNAVAILABLE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send a single burst of packets.
|
|
// An asynchronous call will return to the caller; an ordinary call
|
|
// will loop until the call is complete.
|
|
//
|
|
SetFragmentLengths();
|
|
|
|
SetState(CallSendReceive);
|
|
|
|
Status = PushBuffer(Message);
|
|
if (Status)
|
|
{
|
|
LogEvent(SU_CCALL, EV_BUFFER_OUT, this, Message->Buffer, Message->BufferLength);
|
|
LogError(SU_CCALL, EV_STATUS, this, 0, Status);
|
|
|
|
FreePipeBuffer(Message);
|
|
|
|
return AfterSendReceive(Message, Status);
|
|
}
|
|
|
|
ASSERT( !Message->Buffer );
|
|
ASSERT( !Message->BufferLength );
|
|
|
|
while (RPC_S_OK == Status && FALSE == fReceivedAllFragments)
|
|
{
|
|
Status = ReceiveSinglePacket();
|
|
}
|
|
|
|
if (Status)
|
|
{
|
|
LogError(SU_CCALL, EV_STATUS, this, 0, Status);
|
|
}
|
|
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
Status = AssembleBufferFromPackets(Message, this);
|
|
}
|
|
|
|
//
|
|
// Depending upon circumstances, AfterSendReceive() may cause the ccall,
|
|
// the association, and/or the binding handle to be freed.
|
|
//
|
|
|
|
SetState(CallQuiescent);
|
|
|
|
return AfterSendReceive(Message, Status);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::AttemptAutoReconnect()
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
ASSERT( State == CallSend || State == CallSendReceive ||
|
|
(State == CallReceive && !fRetransmitted && SendWindowBase == 0
|
|
) );
|
|
|
|
/*
|
|
|
|
If the call's association has the error flag set, look for a follow-up
|
|
association, otherwise create a follow-up association.
|
|
|
|
link the binding handle to the new association, and ask it for a connection
|
|
to be associated with the existing connection's thread ID. Move this call
|
|
and successors to the new connection, send some packets, and return
|
|
to the packet loop.
|
|
|
|
*/
|
|
PDG_CCONNECTION OldConnection = Connection;
|
|
PDG_CCONNECTION NewConnection;
|
|
|
|
//
|
|
// To avoid a deadlock, we must release the connection mutex before
|
|
// taking the binding mutex. The fBusy flag prevents another thread
|
|
// from using it.
|
|
//
|
|
OldConnection->fError = TRUE;
|
|
OldConnection->fBusy = TRUE;
|
|
OldConnection->MutexClear();
|
|
|
|
NewConnection = OldConnection->BindingHandle->GetReplacementConnection(OldConnection, InterfacePointer);
|
|
if (!NewConnection)
|
|
{
|
|
OldConnection->MutexRequest();
|
|
return RPC_S_CALL_FAILED_DNE;
|
|
}
|
|
|
|
//
|
|
// We now own NewConnection's mutex. Transfer calls to be retried.
|
|
//
|
|
OldConnection->MutexRequest();
|
|
Status = OldConnection->TransferCallsToNewConnection(this, NewConnection);
|
|
|
|
ASSERT( !Status );
|
|
|
|
if (FALSE == SourceEndpoint->Async)
|
|
{
|
|
//
|
|
// Get a fresh endpoint to avoid a race with any ICMP rejects.
|
|
// If that fails, leave the old endpoint in place and bail out of the call.
|
|
//
|
|
DG_ENDPOINT * OldEndpoint = SourceEndpoint;
|
|
|
|
SourceEndpoint = 0;
|
|
|
|
Status = GetEndpoint(OldEndpoint->Flags);
|
|
|
|
OldEndpoint->Flags |= PENALTY_BOX;
|
|
|
|
if (Status)
|
|
{
|
|
SourceEndpoint = OldEndpoint;
|
|
return Status;
|
|
}
|
|
|
|
EndpointManager->ReleaseEndpoint(OldEndpoint);
|
|
}
|
|
|
|
//
|
|
// Attempt the call again.
|
|
//
|
|
LastReceiveTime = GetTickCount();
|
|
|
|
Status = SendSomeFragments();
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::ReceiveSinglePacket()
|
|
{
|
|
RPC_STATUS Status;
|
|
PDG_PACKET Packet = 0;
|
|
DG_TRANSPORT_ADDRESS ReceiveAddress = 0;
|
|
|
|
Connection->MutexClear();
|
|
|
|
unsigned Length = 0;
|
|
void * Buffer = 0;
|
|
|
|
Status = Connection->TransportInterface->SyncReceive(
|
|
&SourceEndpoint->TransportEndpoint,
|
|
&ReceiveAddress,
|
|
&Length,
|
|
&Buffer,
|
|
ReceiveTimeout
|
|
);
|
|
|
|
if (Buffer)
|
|
{
|
|
Packet = DG_PACKET::FromPacketHeader(Buffer);
|
|
Packet->DataLength = Length;
|
|
}
|
|
|
|
Connection->MutexRequest();
|
|
|
|
if (Status == RPC_P_HOST_DOWN)
|
|
{
|
|
RpcpErrorAddRecord( EEInfoGCRuntime,
|
|
Status,
|
|
EEInfoDLDG_CCALL__ReceiveSinglePacket10
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// If the transport tells us the server is not present (ICMP reject)
|
|
// then we can try auto-reconnect - as long as there is no possibility
|
|
// that the server crashed while executing our stub.
|
|
//
|
|
if (Status == RPC_P_PORT_DOWN)
|
|
{
|
|
RpcpErrorAddRecord( EEInfoGCRuntime,
|
|
Status,
|
|
EEInfoDLDG_CCALL__ReceiveSinglePacket20,
|
|
(ULONG) Connection->fAutoReconnect,
|
|
(ULONG) BufferFlags
|
|
);
|
|
|
|
if (!Connection->fAutoReconnect &&
|
|
(!fRetransmitted || (BufferFlags & RPC_NCA_FLAGS_IDEMPOTENT)))
|
|
{
|
|
Status = AttemptAutoReconnect();
|
|
return Status;
|
|
}
|
|
|
|
ASSERT( !Packet && !ReceiveAddress );
|
|
return Status;
|
|
}
|
|
|
|
if (Status == RPC_P_OVERSIZE_PACKET)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
PrintToDebugger("RPC DG: packet is too large\n");
|
|
#endif
|
|
Packet->Flags |= DG_PF_PARTIAL;
|
|
Status = RPC_S_OK;
|
|
}
|
|
else
|
|
{
|
|
ASSERT( !Packet || Packet->DataLength <= Packet->MaxDataLength );
|
|
}
|
|
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
LogEvent(SU_CCALL, EV_PKT_IN, this, (void *) 0, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|
|
|
do
|
|
{
|
|
//
|
|
// Request packets are special.
|
|
//
|
|
if (Packet->Header.PacketType == DG_REQUEST)
|
|
{
|
|
Status = StandardPacketChecks(Packet);
|
|
if (Status)
|
|
{
|
|
LogError(SU_CCALL, EV_PKT_IN, this, (void *) 1, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|
FreePacket(Packet);
|
|
break;
|
|
}
|
|
|
|
if (Packet->Header.AuthProto != 0)
|
|
{
|
|
LogError(SU_CCALL, EV_PKT_IN, this, (void *) 2, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|
FreePacket(Packet);
|
|
break;
|
|
}
|
|
|
|
if (Packet->Flags & DG_PF_PARTIAL)
|
|
{
|
|
LogError(SU_CCALL, EV_PKT_IN, this, (void *) 3, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|
FreePacket(Packet);
|
|
break;
|
|
}
|
|
|
|
LogEvent(SU_CCALL, EV_PKT_IN, this, (void *) 4, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|
|
|
Connection->ServerResponded = TRUE;
|
|
Status = DealWithRequest(Packet, ReceiveAddress);
|
|
if (Status)
|
|
{
|
|
//
|
|
// Make sure that the call times out in a reasonable time period.
|
|
//
|
|
if (long(GetTickCount()) - LastReceiveTime > TimeoutLimit)
|
|
{
|
|
LogError(SU_CCALL, EV_STATUS, this, (void *) 7, RPC_P_TIMEOUT);
|
|
|
|
SendQuit();
|
|
return RPC_P_TIMEOUT;
|
|
}
|
|
|
|
}
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
Status = StandardPacketChecks(Packet);
|
|
if (Status)
|
|
{
|
|
LogError(SU_CCALL, EV_PKT_IN, this, (void *) 5, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|
FreePacket(Packet);
|
|
|
|
if (Status == NCA_STATUS_VERSION_MISMATCH)
|
|
{
|
|
Status = 0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (Packet->Header.SequenceNumber != SequenceNumber ||
|
|
Connection->ActivityNode.Uuid.MatchUuid(&Packet->Header.ActivityId))
|
|
{
|
|
LogEvent(SU_CCALL, EV_PKT_IN, this, (void *) 6, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|
FreePacket(Packet);
|
|
break;
|
|
}
|
|
|
|
Status = Connection->UpdateServerAddress(Packet, ReceiveAddress);
|
|
if (Status)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = DispatchPacket(Packet);
|
|
}
|
|
while ( 0 );
|
|
}
|
|
else
|
|
{
|
|
ASSERT( !Packet && !ReceiveAddress );
|
|
|
|
if (SequenceNumber <= Connection->CurrentSequenceNumber())
|
|
{
|
|
if (!TimeoutCount)
|
|
{
|
|
ReceiveTimeout = 500;
|
|
}
|
|
|
|
++TimeoutCount;
|
|
|
|
//
|
|
// Shorten the burst length.
|
|
//
|
|
SendBurstLength = (1+SendBurstLength)/2;
|
|
|
|
if (Status == RPC_P_TIMEOUT)
|
|
{
|
|
LogEvent(SU_CCALL, EV_STATUS, this, 0, Status);
|
|
|
|
IncreaseReceiveTimeout();
|
|
}
|
|
else
|
|
{
|
|
RpcpErrorAddRecord( EEInfoGCRuntime,
|
|
Status,
|
|
EEInfoDLDG_CCALL__ReceiveSinglePacket30
|
|
);
|
|
|
|
LogError(SU_CCALL, EV_STATUS, this, 0, Status);
|
|
|
|
//
|
|
// Perhaps it's a transient error. Wait a moment and try again.
|
|
//
|
|
#ifdef DEBUGRPC
|
|
if (Status != RPC_S_OUT_OF_RESOURCES &&
|
|
Status != RPC_S_OUT_OF_MEMORY &&
|
|
Status != RPC_P_RECEIVE_FAILED )
|
|
{
|
|
DbgPrint("RPC: d/g receive status %x\n"
|
|
"Please send the error code to jroberts, and hit 'g'",
|
|
Status
|
|
);
|
|
|
|
RpcpBreakPoint();
|
|
}
|
|
#endif
|
|
|
|
Sleep(500);
|
|
}
|
|
}
|
|
|
|
Status = DealWithTimeout();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::AsyncSend(
|
|
PRPC_MESSAGE Message
|
|
)
|
|
{
|
|
if (AsyncStatus != RPC_S_OK &&
|
|
AsyncStatus != RPC_S_ASYNC_CALL_PENDING )
|
|
{
|
|
Connection->MutexRequest();
|
|
|
|
return AfterSendReceive(Message, AsyncStatus);
|
|
}
|
|
|
|
return Send(Message);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::Send(
|
|
PRPC_MESSAGE Message
|
|
)
|
|
{
|
|
if (Message->RpcFlags & RPC_BUFFER_ASYNC)
|
|
{
|
|
LogEvent(SU_CCALL, EV_PROC, this, Message->Buffer, 'A' + (('S' + (('n' + ('d' << 8)) << 8)) << 8));
|
|
}
|
|
else
|
|
{
|
|
LogEvent(SU_CCALL, EV_PROC, this, Message->Buffer, 'S' + (('e' + (('n' + ('d' << 8)) << 8)) << 8));
|
|
}
|
|
|
|
LogEvent(SU_CCALL, EV_BUFFER_IN, this, Message->Buffer, Message->BufferLength);
|
|
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
Connection->MutexRequest();
|
|
|
|
//
|
|
// See DG_CCALL::CancelDelayedSend for details.
|
|
//
|
|
while (State == CallCancellingSend)
|
|
{
|
|
Connection->MutexClear();
|
|
Sleep(1);
|
|
Connection->MutexRequest();
|
|
}
|
|
|
|
if (State == CallInit)
|
|
{
|
|
Status = BeforeSendReceive(Message);
|
|
if (RPC_S_OK != Status)
|
|
{
|
|
Connection->MutexClear();
|
|
FreeBuffer(Message);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
SetFragmentLengths();
|
|
|
|
if ((Message->RpcFlags & RPC_BUFFER_PARTIAL) &&
|
|
Message->BufferLength < (ULONG) MaxFragmentSize * SendWindowSize )
|
|
{
|
|
Status = RPC_S_SEND_INCOMPLETE;
|
|
|
|
Connection->IncrementRefCount();
|
|
if (pAsync)
|
|
{
|
|
IssueNotification( RpcSendComplete );
|
|
}
|
|
|
|
Connection->DecrementRefCount();
|
|
|
|
LogEvent(SU_CCALL, EV_STATUS, this, 0, Status);
|
|
return Status;
|
|
}
|
|
|
|
SetState(CallSend);
|
|
|
|
Status = PushBuffer(Message);
|
|
if (Status)
|
|
{
|
|
FreePipeBuffer(Message);
|
|
Status = AfterSendReceive(Message, Status);
|
|
|
|
LogEvent(SU_CCALL, EV_BUFFER_OUT, this, Message->Buffer, Message->BufferLength);
|
|
LogError(SU_CCALL, EV_STATUS, this, 0, Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
if (pAsync)
|
|
{
|
|
Connection->MutexClear();
|
|
}
|
|
else
|
|
{
|
|
while (RPC_S_OK == Status && !IsBufferAcknowledged())
|
|
{
|
|
Status = ReceiveSinglePacket();
|
|
}
|
|
|
|
SetState(CallQuiescent);
|
|
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
Connection->MutexClear();
|
|
}
|
|
else
|
|
{
|
|
Status = AfterSendReceive(Message, Status);
|
|
}
|
|
}
|
|
|
|
if (!Status && Message->BufferLength)
|
|
{
|
|
Status = RPC_S_SEND_INCOMPLETE;
|
|
}
|
|
|
|
LogEvent(SU_CCALL, EV_BUFFER_OUT, this, Message->Buffer, Message->BufferLength);
|
|
LogEvent(SU_CCALL, EV_STATUS, this, 0, Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::MaybeSendReceive(
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends a [maybe], [broadcast, maybe] or [message] call.
|
|
|
|
Arguments:
|
|
|
|
Message - Message to be sent.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
<error from Transport>
|
|
|
|
--*/
|
|
|
|
{
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
//
|
|
// Make sure this fits into a single packet.
|
|
//
|
|
if ( !(Message->RpcFlags & RPCFLG_MESSAGE)
|
|
&& (Message->BufferLength > MaxFragmentSize) )
|
|
{
|
|
FreePipeBuffer(Message);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
//
|
|
// [maybe] calls are implicitly idempotent.
|
|
//
|
|
Message->RpcFlags |= RPC_NCA_FLAGS_IDEMPOTENT;
|
|
|
|
//
|
|
// Build the request packet.
|
|
//
|
|
PDG_PACKET Packet = DG_PACKET::FromStubData(Message->Buffer);
|
|
PNCA_PACKET_HEADER Header = &Packet->Header;
|
|
|
|
*Header = pSavedPacket->Header;
|
|
|
|
BuildNcaPacketHeader(Header, Message);
|
|
|
|
Header->SetPacketBodyLen (Message->BufferLength);
|
|
Header->SetFragmentNumber(0);
|
|
Header->AuthProto = 0;
|
|
Header->ServerBootTime = 0;
|
|
|
|
AddSerialNumber(Header);
|
|
|
|
//
|
|
// Send the packet.
|
|
//
|
|
LogEvent(SU_CCALL, EV_PKT_OUT, this, 0, 0);
|
|
|
|
Status = SendSecurePacket(SourceEndpoint,
|
|
Connection->Association->InqServerAddress(),
|
|
Header,
|
|
0,
|
|
0
|
|
);
|
|
|
|
FreePipeBuffer(Message);
|
|
Message->BufferLength = 0;
|
|
|
|
if (Message->RpcFlags & RPCFLG_MESSAGE)
|
|
{
|
|
return Status;
|
|
}
|
|
else
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::AsyncReceive(
|
|
PRPC_MESSAGE Message,
|
|
unsigned MinimumSize
|
|
)
|
|
{
|
|
LogEvent(SU_CCALL, EV_PROC, this, IntToPtr(MinimumSize), 0x76635241);
|
|
LogEvent(SU_CCALL, EV_BUFFER_IN, this, Message->Buffer, Message->BufferLength);
|
|
|
|
ASSERT( pAsync && (Message->RpcFlags & RPC_BUFFER_ASYNC) );
|
|
|
|
Connection->MutexRequest();
|
|
|
|
if (State == CallSend)
|
|
{
|
|
Connection->MutexClear();
|
|
return RPC_S_ASYNC_CALL_PENDING;
|
|
}
|
|
|
|
//
|
|
// See DG_CCALL::CancelDelayedSend for details.
|
|
//
|
|
while (State == CallCancellingSend)
|
|
{
|
|
Connection->MutexClear();
|
|
Sleep(1);
|
|
Connection->MutexRequest();
|
|
}
|
|
|
|
if (AsyncStatus != RPC_S_OK &&
|
|
AsyncStatus != RPC_S_ASYNC_CALL_PENDING )
|
|
{
|
|
return AfterSendReceive(Message, AsyncStatus);
|
|
}
|
|
|
|
if (!fReceivedAllFragments &&
|
|
!(ConsecutiveDataBytes >= MinimumSize && (Message->RpcFlags & RPC_BUFFER_PARTIAL)))
|
|
{
|
|
if (Message->RpcFlags & RPC_BUFFER_NONOTIFY)
|
|
{
|
|
// just checking
|
|
}
|
|
else
|
|
{
|
|
SetState(CallReceive);
|
|
|
|
PipeReceiveSize = MinimumSize;
|
|
}
|
|
|
|
Connection->MutexClear();
|
|
|
|
LogEvent(SU_CCALL, EV_STATUS, this, 0, RPC_S_ASYNC_CALL_PENDING);
|
|
|
|
return RPC_S_ASYNC_CALL_PENDING;
|
|
}
|
|
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
Status = AssembleBufferFromPackets(Message, this);
|
|
|
|
LogEvent(SU_CCALL, EV_STATUS, this, 0, Status);
|
|
LogEvent(SU_CCALL, EV_BUFFER_OUT, this, Message->Buffer, Message->BufferLength);
|
|
|
|
if (0 == (Message->RpcFlags & RPC_BUFFER_PARTIAL) ||
|
|
(Message->RpcFlags & RPC_BUFFER_COMPLETE) ||
|
|
RPC_S_OK != Status )
|
|
{
|
|
Status = AfterSendReceive(Message, Status);
|
|
}
|
|
else
|
|
{
|
|
Connection->MutexClear();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::Receive(
|
|
PRPC_MESSAGE Message,
|
|
unsigned MinimumSize
|
|
)
|
|
{
|
|
LogEvent(SU_CCALL, EV_PROC, this, IntToPtr(MinimumSize), 0x76636552);
|
|
LogEvent(SU_CCALL, EV_BUFFER_IN, this, Message->Buffer, Message->BufferLength);
|
|
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
Connection->MutexRequest();
|
|
|
|
SetState(CallReceive);
|
|
|
|
while (RPC_S_OK == Status &&
|
|
!fReceivedAllFragments &&
|
|
!(ConsecutiveDataBytes >= MinimumSize && (Message->RpcFlags & RPC_BUFFER_PARTIAL)))
|
|
{
|
|
Status = ReceiveSinglePacket();
|
|
}
|
|
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
Status = AssembleBufferFromPackets(Message, this);
|
|
}
|
|
|
|
LogEvent(SU_CCALL, EV_STATUS, this, 0, RPC_S_OK);
|
|
LogEvent(SU_CCALL, EV_BUFFER_OUT, this, Message->Buffer, Message->BufferLength);
|
|
|
|
if (0 == (Message->RpcFlags & RPC_BUFFER_PARTIAL) ||
|
|
(Message->RpcFlags & RPC_BUFFER_COMPLETE) ||
|
|
RPC_S_OK != Status )
|
|
{
|
|
Status = AfterSendReceive(Message, Status);
|
|
}
|
|
else
|
|
{
|
|
Connection->MutexClear();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DG_CCALL::IssueNotification (
|
|
IN RPC_ASYNC_EVENT Event
|
|
)
|
|
{
|
|
Connection->Mutex.VerifyOwned();
|
|
|
|
LogEvent(SU_CCALL, EV_NOTIFY, this, (void *) Event, AsyncStatus);
|
|
|
|
if (State == CallInit ||
|
|
State == CallComplete ||
|
|
State == CallCancellingSend )
|
|
{
|
|
#ifdef DEBUGRPC
|
|
DbgPrint("RPC: redundant notification on ccall %lx\n", this);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
if (Event == RpcCallComplete)
|
|
{
|
|
SetState(CallComplete);
|
|
}
|
|
else
|
|
{
|
|
SetState(CallQuiescent);
|
|
}
|
|
|
|
if (pAsync->NotificationType == RpcNotificationTypeApc)
|
|
{
|
|
IncrementRefCount();
|
|
}
|
|
|
|
int i;
|
|
for (i=1; i < 3; ++i)
|
|
{
|
|
if (CALL::IssueNotification(Event))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
Sleep(200);
|
|
}
|
|
|
|
DecrementRefCountAndKeepMutex();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
DG_CCALL::FreeAPCInfo (
|
|
IN RPC_APC_INFO *pAPCInfo
|
|
)
|
|
{
|
|
LogEvent(SU_CCALL, EV_APC, this);
|
|
|
|
Connection->MutexRequest();
|
|
|
|
CALL::FreeAPCInfo(pAPCInfo);
|
|
|
|
DecrementRefCount();
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::CancelAsyncCall (
|
|
IN BOOL fAbort
|
|
)
|
|
{
|
|
Connection->MutexRequest();
|
|
|
|
if (State == CallInit ||
|
|
State == CallComplete ||
|
|
State == CallCancellingSend )
|
|
{
|
|
Connection->MutexClear();
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
RpcpErrorAddRecord( EEInfoGCRuntime,
|
|
RPC_S_CALL_CANCELLED,
|
|
EEInfoDLDG_CCALL__CancelAsyncCall10,
|
|
(ULONG) fAbort
|
|
);
|
|
|
|
SendQuit();
|
|
|
|
Connection->IncrementRefCount();
|
|
|
|
if (fAbort)
|
|
{
|
|
CancelDelayedSend();
|
|
CleanupReceiveWindow();
|
|
|
|
AsyncStatus = RPC_S_CALL_CANCELLED;
|
|
IssueNotification( RpcCallComplete );
|
|
}
|
|
else
|
|
{
|
|
CancelPending = TRUE;
|
|
}
|
|
|
|
Connection->DecrementRefCount();
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::DealWithRequest(
|
|
IN PDG_PACKET Packet,
|
|
IN DG_TRANSPORT_ADDRESS ReceiveAddress
|
|
)
|
|
{
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
RPC_MESSAGE CallbackMessage;
|
|
PNCA_PACKET_HEADER OriginalHeader;
|
|
DG_CLIENT_CALLBACK Callback;
|
|
|
|
//
|
|
// Save the server data rep for challenge response processing.
|
|
//
|
|
Connection->Association->ServerDataRep = 0x00ffffff & (*(unsigned long *) (Packet->Header.DataRep));
|
|
|
|
Callback.LocalEndpoint = SourceEndpoint;
|
|
Callback.Connection = Connection;
|
|
Callback.RemoteAddress = ReceiveAddress;
|
|
Callback.Request = Packet;
|
|
|
|
return DispatchCallbackRequest(&Callback);
|
|
}
|
|
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::DealWithFack(
|
|
PDG_PACKET pPacket
|
|
)
|
|
{
|
|
BOOL Updated;
|
|
RPC_STATUS Status;
|
|
|
|
SendBurstLength += 1;
|
|
|
|
Status = UpdateSendWindow(pPacket, &Updated);
|
|
|
|
if (Updated)
|
|
{
|
|
Connection->UpdateAssociation();
|
|
}
|
|
|
|
FreePacket(pPacket);
|
|
|
|
if (Status != RPC_P_PORT_DOWN &&
|
|
Status != RPC_P_HOST_DOWN)
|
|
{
|
|
Status = 0;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::DealWithResponse(
|
|
PDG_PACKET pPacket
|
|
)
|
|
{
|
|
#ifdef DBG
|
|
if (!Connection->TransportInterface->IsMessageTransport)
|
|
{
|
|
ASSERT( !(pPacket->GetPacketBodyLen() % 8) ||
|
|
!(pPacket->Header.PacketFlags & DG_PF_FRAG) ||
|
|
(pPacket->Header.PacketFlags & DG_PF_LAST_FRAG) );
|
|
}
|
|
#endif
|
|
|
|
Connection->Association->LastReceiveTime = LastReceiveTime;
|
|
|
|
//
|
|
// The first response is implicitly a FACK for the final request packet.
|
|
//
|
|
MarkAllPacketsReceived();
|
|
|
|
//
|
|
// Add packet to received list, and send a fack if necessary.
|
|
//
|
|
if (FALSE == UpdateReceiveWindow(pPacket))
|
|
{
|
|
FreePacket(pPacket);
|
|
}
|
|
|
|
Connection->MaybeTransmitNextCall();
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::DealWithWorking(
|
|
PDG_PACKET pPacket
|
|
)
|
|
{
|
|
Connection->Association->LastReceiveTime = LastReceiveTime;
|
|
|
|
//
|
|
// WORKING is implicitly a FACK for the final request packet.
|
|
//
|
|
MarkAllPacketsReceived();
|
|
|
|
Connection->MaybeTransmitNextCall();
|
|
|
|
//
|
|
// Reduce server load by increasing the timeout during long calls.
|
|
//
|
|
IncreaseReceiveTimeout();
|
|
|
|
FreePacket(pPacket);
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
void
|
|
DG_CCALL::IncreaseReceiveTimeout()
|
|
{
|
|
if (Connection->TransportInterface->IsMessageTransport)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ReceiveTimeout *= 2;
|
|
if (ReceiveTimeout > 16000)
|
|
{
|
|
ReceiveTimeout = 16000;
|
|
}
|
|
|
|
if (long(GetTickCount()) - LastReceiveTime + ReceiveTimeout > TimeoutLimit)
|
|
{
|
|
ReceiveTimeout = 1 + TimeoutLimit - (GetTickCount() - LastReceiveTime);
|
|
if (ReceiveTimeout < 0)
|
|
{
|
|
ReceiveTimeout = 0;
|
|
}
|
|
}
|
|
|
|
long CancelTimeout = ThreadGetRpcCancelTimeout();
|
|
if (CancelTimeout != RPC_C_CANCEL_INFINITE_TIMEOUT)
|
|
{
|
|
CancelTimeout *= 1000;
|
|
|
|
if (CancelTimeout < 2000)
|
|
{
|
|
CancelTimeout = 2000;
|
|
}
|
|
|
|
if (ReceiveTimeout > CancelTimeout)
|
|
{
|
|
ReceiveTimeout = CancelTimeout;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::DealWithNocall(
|
|
PDG_PACKET pPacket
|
|
)
|
|
{
|
|
BOOL Used;
|
|
BOOL Updated;
|
|
RPC_STATUS Status;
|
|
|
|
if (pPacket->GetPacketBodyLen() == 0)
|
|
{
|
|
//
|
|
// Don't trust the FragmentNumber field.
|
|
//
|
|
pPacket->SetFragmentNumber(0xffff);
|
|
}
|
|
|
|
Status = UpdateSendWindow(pPacket, &Updated);
|
|
|
|
if (Updated)
|
|
{
|
|
Connection->UpdateAssociation();
|
|
}
|
|
|
|
FreePacket(pPacket);
|
|
|
|
//
|
|
// Ordinarily a NOCALL means the request wasn't received, and we should
|
|
// fail the call after several in a row. But a NOCALL with window size 0
|
|
// means the request was received and queued, and we want to back off as
|
|
// for a WORKING packet.
|
|
//
|
|
if (SendWindowSize != 0)
|
|
{
|
|
++UnansweredRequestCount;
|
|
}
|
|
|
|
// IncreaseReceiveTimeout();
|
|
|
|
if (UnansweredRequestCount > 4)
|
|
{
|
|
SendQuit();
|
|
return RPC_P_PORT_DOWN;
|
|
}
|
|
|
|
if (Status != RPC_P_PORT_DOWN &&
|
|
Status != RPC_P_HOST_DOWN)
|
|
{
|
|
Status = 0;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::ProcessFaultOrRejectData(
|
|
PDG_PACKET Packet
|
|
)
|
|
{
|
|
RPC_STATUS Status = RPC_S_CALL_FAILED;
|
|
|
|
//
|
|
// Read the standard OSF error code.
|
|
//
|
|
if (Packet->GetPacketBodyLen() >= sizeof(unsigned long))
|
|
{
|
|
unsigned long Error = * (unsigned long *) Packet->Header.Data;
|
|
|
|
if (NeedsByteSwap(&Packet->Header))
|
|
{
|
|
Error = RpcpByteSwapLong(Error);
|
|
}
|
|
Status = MapFromNcaStatusCode(Error);
|
|
|
|
LogEvent( SU_CCALL, EV_STATUS, this, 0, Error);
|
|
}
|
|
|
|
//
|
|
// Read the extended error info, if present.
|
|
//
|
|
if (Packet->GetPacketBodyLen() > sizeof(EXTENDED_FAULT_BODY))
|
|
{
|
|
EXTENDED_FAULT_BODY * body = (EXTENDED_FAULT_BODY *) Packet->Header.Data;
|
|
|
|
if (body->Magic == DG_EE_MAGIC_VALUE)
|
|
{
|
|
ExtendedErrorInfo *EEInfo;
|
|
|
|
UnpickleEEInfoFromBuffer( body->EeInfo,
|
|
Packet->GetPacketBodyLen() - sizeof(EXTENDED_FAULT_BODY)
|
|
);
|
|
|
|
EEInfo = RpcpGetEEInfo();
|
|
if (EEInfo && pAsync)
|
|
{
|
|
ASSERT(this->EEInfo == NULL);
|
|
|
|
// move the eeinfo to the call. Even though it is possible
|
|
// that the call will be completed on this thread, it is
|
|
// still ok, as we will move it back during completion
|
|
this->EEInfo = EEInfo;
|
|
RpcpClearEEInfo();
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::DealWithFault(
|
|
PDG_PACKET pPacket
|
|
)
|
|
{
|
|
RPC_STATUS Status = ProcessFaultOrRejectData(pPacket);
|
|
|
|
FreePacket(pPacket);
|
|
|
|
SendAck();
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::DealWithReject(
|
|
PDG_PACKET pPacket
|
|
)
|
|
{
|
|
RPC_STATUS Status = ProcessFaultOrRejectData(pPacket);
|
|
|
|
FreePacket(pPacket);
|
|
|
|
if (!fRetransmitted || (BufferFlags & RPC_NCA_FLAGS_IDEMPOTENT))
|
|
{
|
|
if (Status == NCA_STATUS_WRONG_BOOT_TIME ||
|
|
(!Connection->fAutoReconnect && Status == RPC_S_CALL_FAILED_DNE))
|
|
{
|
|
Status = AttemptAutoReconnect();
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::SendQuit(
|
|
)
|
|
{
|
|
QUIT_BODY_0 * pBody = (QUIT_BODY_0 *) pSavedPacket->Header.Data;
|
|
|
|
pSavedPacket->Header.PacketType = DG_QUIT;
|
|
pSavedPacket->Header.PacketFlags &= DG_PF_IDEMPOTENT;
|
|
pSavedPacket->SetPacketBodyLen(sizeof(QUIT_BODY_0));
|
|
|
|
AddSerialNumber(&pSavedPacket->Header);
|
|
|
|
pBody->Version = 0;
|
|
pBody->EventId = CancelEventId;
|
|
|
|
unsigned Frag = (pSavedPacket->Header.PacketType << 16) | pSavedPacket->GetFragmentNumber();
|
|
LogEvent(SU_CCALL, EV_PKT_OUT, this, 0, Frag);
|
|
|
|
return Connection->SealAndSendPacket(SourceEndpoint, 0, &pSavedPacket->Header, 0);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::DealWithQuack(
|
|
PDG_PACKET pPacket
|
|
)
|
|
{
|
|
if (FALSE == CancelPending)
|
|
{
|
|
FreePacket(pPacket);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
QUACK_BODY_0 * pBody = (QUACK_BODY_0 *) pPacket->Header.Data;
|
|
|
|
if (0 == pPacket->GetPacketBodyLen())
|
|
{
|
|
//
|
|
// The server orphaned a call. I hope it is the current one.
|
|
//
|
|
goto ok;
|
|
}
|
|
|
|
//
|
|
// The ver 0 quack packet contains two ulongs and a uchar; I'd like to
|
|
// test for sizeof(quack body) but C++ likes to pad structure sizes
|
|
// to 0 mod 4. Hence the explicit test for length < 9.
|
|
//
|
|
if (pPacket->GetPacketBodyLen() < 9 ||
|
|
pBody->Version != 0)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
PrintToDebugger("RPC DG: unknown QUACK format: version 0x%lx, length 0x%hx\n",
|
|
pBody->Version, pPacket->GetPacketBodyLen()
|
|
);
|
|
#endif
|
|
FreePacket(pPacket);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
if (pBody->EventId != CancelEventId)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
PrintToDebugger("RPC DG: ignoring unknown QUACK event id 0x%lx\n",
|
|
pBody->EventId
|
|
);
|
|
#endif
|
|
FreePacket(pPacket);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
ok:
|
|
|
|
CancelPending = FALSE;
|
|
FreePacket(pPacket);
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCONNECTION::SealAndSendPacket(
|
|
IN DG_ENDPOINT * SourceEndpoint,
|
|
IN DG_TRANSPORT_ADDRESS UnusedRemoteAddress,
|
|
IN UNALIGNED NCA_PACKET_HEADER * Header,
|
|
IN unsigned long DataOffset
|
|
)
|
|
/*
|
|
|
|
- NT 3.5 did not support secure datagram RPC.
|
|
|
|
- NT 3.51 and NT 4.0 servers will probably fail to decrypt a multifragment
|
|
request if some fragments are encrypted with one context and others use a
|
|
different context, since they decrypt all the packets using the last active
|
|
context instead of looking at each packet's 'ksno' field.
|
|
|
|
- NT 3.51 and NT 4.0 servers dispose of stale contexts only when the activity
|
|
is deleted.
|
|
|
|
- NT 3.51 and NT 4.0 clients use only one set of security parameters
|
|
(provider, level, principal name) per connection. The only time the ksno
|
|
changes is on NT 4.0 when a Kerberos context expires and must be renewed.
|
|
|
|
- NT 3.51 and NT 4.0 clients do not notice if ksno rises above 0xff. If it
|
|
does, the next call will fail because the server sees only the lowest 8 bits
|
|
and mistakenly reuses another context.
|
|
|
|
- NT 3.51 clients do not notice whether the thread is impersonating, so they
|
|
can mistakenly reuse a connection if all info except the username is identical.
|
|
|
|
- NT 3.51 clients mistakenly send [maybe] calls with an auth trailer, if the
|
|
underlying connection is secure. OSF and NT 4.0 force [maybe] calls insecure.
|
|
|
|
*/
|
|
|
|
{
|
|
ASSERT( 0 == ActivityNode.CompareUuid(&Header->ActivityId) );
|
|
|
|
retry_packet:
|
|
|
|
RPC_STATUS Status;
|
|
Status = SendSecurePacket(SourceEndpoint,
|
|
Association->InqServerAddress(),
|
|
Header,
|
|
DataOffset,
|
|
ActiveSecurityContext
|
|
);
|
|
|
|
if (Status == SEC_E_CONTEXT_EXPIRED)
|
|
{
|
|
Status = InitializeSecurityContext();
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
goto retry_packet;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCONNECTION::InitializeSecurityContext(
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus = RPC_S_OK;
|
|
|
|
delete ActiveSecurityContext;
|
|
|
|
if (SecurityContextId > 0xff)
|
|
{
|
|
ActiveSecurityContext = 0;
|
|
return RPC_S_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
ActiveSecurityContext = new SECURITY_CONTEXT(&AuthInfo, SecurityContextId, TRUE, &RpcStatus);
|
|
if (0 == ActiveSecurityContext)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (RpcStatus)
|
|
{
|
|
delete ActiveSecurityContext;
|
|
ActiveSecurityContext = 0;
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
++SecurityContextId;
|
|
|
|
SECURITY_BUFFER_DESCRIPTOR BufferDescriptorIn;
|
|
DCE_INIT_SECURITY_INFO InitSecurityInfo;
|
|
SECURITY_BUFFER SecurityBuffersIn[1];
|
|
|
|
BufferDescriptorIn.ulVersion = 0;
|
|
BufferDescriptorIn.cBuffers = 1;
|
|
BufferDescriptorIn.pBuffers = SecurityBuffersIn;
|
|
|
|
SecurityBuffersIn[0].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
|
|
SecurityBuffersIn[0].pvBuffer = &InitSecurityInfo;
|
|
SecurityBuffersIn[0].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO);
|
|
|
|
InitSecurityInfo.DceSecurityInfo.SendSequenceNumber = 0;
|
|
InitSecurityInfo.DceSecurityInfo.ReceiveSequenceNumber = ActiveSecurityContext->AuthContextId;
|
|
RpcpMemoryCopy(&InitSecurityInfo.DceSecurityInfo.AssociationUuid, &ActivityNode.Uuid, sizeof(UUID));
|
|
|
|
InitSecurityInfo.AuthorizationService = AuthInfo.AuthorizationService;
|
|
InitSecurityInfo.PacketType = ~0;
|
|
|
|
RpcStatus = ActiveSecurityContext->InitializeFirstTime(
|
|
AuthInfo.Credentials,
|
|
AuthInfo.ServerPrincipalName,
|
|
AuthInfo.AuthenticationLevel,
|
|
&BufferDescriptorIn
|
|
);
|
|
|
|
LogEvent(SU_CCONN, EV_SEC_INIT1, this, IntToPtr(RpcStatus), I_RpcGetExtendedError());
|
|
|
|
return RpcStatus;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCONNECTION::DealWithAuthCallback(
|
|
IN void * InToken,
|
|
IN long InTokenLength,
|
|
OUT void * OutToken,
|
|
OUT long MaxOutTokenLength,
|
|
OUT long * OutTokenLength
|
|
)
|
|
{
|
|
SECURITY_BUFFER_DESCRIPTOR BufferDescriptorIn;
|
|
SECURITY_BUFFER_DESCRIPTOR BufferDescriptorOut;
|
|
DCE_INIT_SECURITY_INFO InitSecurityInfo;
|
|
SECURITY_BUFFER SecurityBuffersIn [2];
|
|
SECURITY_BUFFER SecurityBuffersOut[2];
|
|
RPC_STATUS Status;
|
|
|
|
InitSecurityInfo.DceSecurityInfo.SendSequenceNumber = 0;
|
|
InitSecurityInfo.DceSecurityInfo.ReceiveSequenceNumber = ActiveSecurityContext->AuthContextId;
|
|
ActivityNode.QueryUuid( &InitSecurityInfo.DceSecurityInfo.AssociationUuid );
|
|
|
|
InitSecurityInfo.AuthorizationService = AuthInfo.AuthorizationService;
|
|
InitSecurityInfo.PacketType = ~0;
|
|
|
|
BufferDescriptorIn.ulVersion = 0;
|
|
BufferDescriptorIn.cBuffers = 2;
|
|
BufferDescriptorIn.pBuffers = SecurityBuffersIn;
|
|
|
|
SecurityBuffersIn[0].BufferType = SECBUFFER_TOKEN;
|
|
SecurityBuffersIn[0].pvBuffer = InToken;
|
|
SecurityBuffersIn[0].cbBuffer = InTokenLength;
|
|
|
|
SecurityBuffersIn[1].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
|
|
SecurityBuffersIn[1].pvBuffer = &InitSecurityInfo;
|
|
SecurityBuffersIn[1].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO);
|
|
|
|
BufferDescriptorOut.ulVersion = 0;
|
|
BufferDescriptorOut.cBuffers = 1;
|
|
BufferDescriptorOut.pBuffers = SecurityBuffersOut;
|
|
|
|
SecurityBuffersOut[0].BufferType = SECBUFFER_TOKEN;
|
|
SecurityBuffersOut[0].pvBuffer = OutToken;
|
|
SecurityBuffersOut[0].cbBuffer = MaxOutTokenLength;
|
|
|
|
Status = ActiveSecurityContext->InitializeThirdLeg(
|
|
AuthInfo.Credentials,
|
|
Association->ServerDataRep,
|
|
&BufferDescriptorIn,
|
|
&BufferDescriptorOut
|
|
);
|
|
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
LogError(SU_CCONN, EV_SEC_INIT3, this, IntToPtr(Status), I_RpcGetExtendedError());
|
|
|
|
*OutTokenLength = 0;
|
|
return (Status);
|
|
}
|
|
else
|
|
{
|
|
LogEvent(SU_CCONN, EV_SEC_INIT3, this, IntToPtr(Status), I_RpcGetExtendedError());
|
|
|
|
*OutTokenLength = SecurityBuffersOut[0].cbBuffer;
|
|
}
|
|
|
|
//
|
|
// If the result buffer spans multiple packets, return the first and store
|
|
// the complete buffer in the connection for conv_who_are_you_auth_more().
|
|
//
|
|
if (!CurrentCall)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
long MaxData = CurrentCall->SourceEndpoint->Stats.MaxPduSize - sizeof(NCA_PACKET_HEADER) - 0x28;
|
|
|
|
if (*OutTokenLength > MaxData)
|
|
{
|
|
if (SecurityBuffer)
|
|
{
|
|
delete SecurityBuffer;
|
|
}
|
|
|
|
SecurityBuffer = new unsigned char[ *OutTokenLength ];
|
|
if (!SecurityBuffer)
|
|
{
|
|
*OutTokenLength = 0;
|
|
return NCA_STATUS_REMOTE_OUT_OF_MEMORY;
|
|
}
|
|
|
|
memcpy( SecurityBuffer, SecurityBuffersOut[0].pvBuffer, *OutTokenLength );
|
|
|
|
SecurityBufferLength = *OutTokenLength;
|
|
|
|
*OutTokenLength = MaxData;
|
|
return NCA_STATUS_PARTIAL_CREDENTIALS;
|
|
}
|
|
|
|
return (Status);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCONNECTION::DealWithAuthMore(
|
|
IN long Index,
|
|
OUT void * OutToken,
|
|
OUT long MaxOutTokenLength,
|
|
OUT long * OutTokenLength
|
|
)
|
|
{
|
|
if (0 == SecurityBuffer)
|
|
{
|
|
*OutTokenLength = 0;
|
|
return 0;
|
|
}
|
|
|
|
if (SecurityBufferLength <= Index)
|
|
{
|
|
*OutTokenLength = 0;
|
|
return 0;
|
|
}
|
|
|
|
if (!CurrentCall)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
long MaxData = CurrentCall->SourceEndpoint->Stats.MaxPduSize - sizeof(NCA_PACKET_HEADER) - 0x28;
|
|
|
|
*OutTokenLength = SecurityBufferLength - Index;
|
|
if (*OutTokenLength > MaxData)
|
|
{
|
|
*OutTokenLength = MaxData;
|
|
memcpy(OutToken, SecurityBuffer + Index, *OutTokenLength);
|
|
return NCA_STATUS_PARTIAL_CREDENTIALS;
|
|
}
|
|
|
|
memcpy(OutToken, SecurityBuffer + Index, *OutTokenLength);
|
|
return 0;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCONNECTION::VerifyPacket(
|
|
DG_PACKET * Packet
|
|
)
|
|
{
|
|
if (AuthInfo.AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE ||
|
|
(Packet->Flags & DG_PF_PARTIAL))
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
if (!Packet->Header.AuthProto)
|
|
{
|
|
if (!fSecurePacketReceived && Packet->Header.PacketType != DG_RESPONSE)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
if (Packet->Header.PacketType == DG_REJECT ||
|
|
Packet->Header.PacketType == DG_NOCALL)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
return RPC_S_ACCESS_DENIED;
|
|
}
|
|
|
|
RPC_STATUS Status;
|
|
PDG_SECURITY_TRAILER Verifier = (PDG_SECURITY_TRAILER) (Packet->Header.Data + Packet->GetPacketBodyLen());
|
|
|
|
if (Verifier->key_vers_num == ActiveSecurityContext->AuthContextId)
|
|
{
|
|
Status = VerifySecurePacket(Packet, ActiveSecurityContext);
|
|
}
|
|
// else if (Verifier->key_vers_num == ActiveSecurityContext->AuthContextId - 1)
|
|
// {
|
|
// Status = VerifySecurePacket(Packet, PreviousSecurityContext);
|
|
// }
|
|
else
|
|
{
|
|
Status = RPC_P_CONTEXT_EXPIRED;
|
|
}
|
|
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
fSecurePacketReceived = TRUE;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
inline
|
|
int
|
|
DG_CCONNECTION::IsSupportedAuthInfo(
|
|
IN const CLIENT_AUTH_INFO * ClientAuthInfo
|
|
)
|
|
{
|
|
return( AuthInfo.IsSupportedAuthInfo(ClientAuthInfo) );
|
|
}
|
|
|
|
|
|
int
|
|
DG_CASSOCIATION::CompareWithBinding(
|
|
IN PDG_BINDING_HANDLE Binding
|
|
)
|
|
{
|
|
BOOL Ignored;
|
|
|
|
ASSERT( !InvalidHandle(DG_CASSOCIATION_TYPE) );
|
|
|
|
CLAIM_MUTEX lock( Mutex );
|
|
|
|
if (0 != pDceBinding->Compare(Binding->pDceBinding,
|
|
&Ignored // fOnlyEndpointDifferent
|
|
)
|
|
)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::Cancel(
|
|
void * ThreadHandle
|
|
)
|
|
{
|
|
LogError( SU_CCALL, EV_PROC, this, 0, 'C' + (('a' + (('n' + (' ' << 8) )<< 8) )<< 8));
|
|
InterlockedIncrement(&Cancelled);
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
unsigned
|
|
DG_CCALL::TestCancel()
|
|
{
|
|
LogEvent( SU_CCALL, EV_PROC, this, IntToPtr(Cancelled), 'T' + (('s' + (('t' + ('C' << 8)) << 8)) << 8));
|
|
|
|
if (!Cancelled)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return InterlockedExchange(&Cancelled, 0);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::SendSomeFragments(
|
|
)
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
if (0 == FirstUnsentFragment)
|
|
{
|
|
if (SequenceNumber > Connection->CurrentSequenceNumber())
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
//
|
|
// If we begin sending this call's request before a previous call
|
|
// is complete, then we should add the DG_PF2_UNRELATED bit to this call.
|
|
//
|
|
if (Connection->Association->fServerSupportsAsync)
|
|
{
|
|
ASSERT( Connection->Association->fServerSupportsAsync );
|
|
|
|
PDG_CCALL node = Previous;
|
|
|
|
while (node && node->fReceivedAllFragments)
|
|
{
|
|
node = node->Previous;
|
|
}
|
|
|
|
if (node)
|
|
{
|
|
BasePacketFlags2 = DG_PF2_UNRELATED;
|
|
Previous->ForceAck = TRUE;
|
|
}
|
|
}
|
|
|
|
// Note that Previous may not be the call immediately prior to this one,
|
|
// since the prior call may have completed already and been removed
|
|
// from the list.
|
|
}
|
|
|
|
LogEvent(SU_CCALL, EV_PROC, this, (void *) 1, 0x656d6f53);
|
|
|
|
Status = DG_PACKET_ENGINE::SendSomeFragments();
|
|
|
|
if (IsBufferSent())
|
|
{
|
|
//
|
|
// The first buffer contains all the static args; it's simpler
|
|
// to set the flags multiple times than to set it only for the
|
|
// first buffer.
|
|
//
|
|
StaticArgsSent = TRUE;
|
|
|
|
if (0 == (BufferFlags & RPC_BUFFER_PARTIAL))
|
|
{
|
|
AllArgsSent = TRUE;
|
|
}
|
|
}
|
|
|
|
if (pAsync && AsyncStatus == RPC_S_ASYNC_CALL_PENDING)
|
|
{
|
|
PostDelayedSend();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DG_CCALL::CheckForCancelTimeout()
|
|
{
|
|
if (!CancelPending && TestCancel() > 0)
|
|
{
|
|
++CancelEventId;
|
|
CancelPending = TRUE;
|
|
CancelTime = GetTickCount();
|
|
}
|
|
|
|
if (CancelPending)
|
|
{
|
|
SendQuit();
|
|
|
|
if ((long(GetTickCount()) - CancelTime) / 1000 > ThreadGetRpcCancelTimeout() )
|
|
{
|
|
CancelPending = FALSE;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::DealWithTimeout()
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
#ifdef DEBUGRPC
|
|
Status = 0xbaadcccc;
|
|
#endif
|
|
|
|
LogEvent(SU_CCALL, EV_PROC, this, 0, 0x656d6f53);
|
|
|
|
ASSERT (State != CallComplete);
|
|
|
|
if (!CancelPending && TestCancel() > 0)
|
|
{
|
|
++CancelEventId;
|
|
CancelPending = TRUE;
|
|
CancelTime = GetTickCount();
|
|
}
|
|
|
|
if (CancelPending)
|
|
{
|
|
SendQuit();
|
|
|
|
if ((long(GetTickCount()) - CancelTime) / 1000 > ThreadGetRpcCancelTimeout() )
|
|
{
|
|
RpcpErrorAddRecord( EEInfoGCRuntime,
|
|
RPC_S_CALL_CANCELLED,
|
|
EEInfoDLDG_CCALL__DealWithTimeout10,
|
|
(ULONG) ThreadGetRpcCancelTimeout(),
|
|
(ULONG) (long(GetTickCount()) - CancelTime)
|
|
);
|
|
|
|
CancelPending = FALSE;
|
|
return RPC_S_CALL_CANCELLED;
|
|
}
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
if (long(GetTickCount()) - LastReceiveTime > TimeoutLimit)
|
|
{
|
|
LogError(SU_CCALL, EV_PROC, this, (void *) 2, 0x656d6f53);
|
|
|
|
RpcpErrorAddRecord( EEInfoGCRuntime,
|
|
RPC_P_TIMEOUT,
|
|
EEInfoDLDG_CCALL__DealWithTimeout20,
|
|
(ULONG) TimeoutLimit,
|
|
(ULONG) (long(GetTickCount()) - LastReceiveTime)
|
|
);
|
|
|
|
SendQuit();
|
|
return RPC_P_TIMEOUT;
|
|
}
|
|
|
|
LogEvent(SU_CCALL, EV_PROC, this, (void *) 4, 0x656d6f53);
|
|
|
|
int ConvComparisonResult = 1;
|
|
|
|
//
|
|
// The client for NT 3.5 (build 807) swallows requests for conv_who_are_you2.
|
|
// If the server pings the callback instead of retransmitting the request,
|
|
// the 807 client will give up before we ever try conv_who_are_you. Instead,
|
|
// retransmit the request packet for calls over the conv interface.
|
|
//
|
|
PRPC_SERVER_INTERFACE Conv = (PRPC_SERVER_INTERFACE) conv_ServerIfHandle;
|
|
if (FALSE == AllArgsSent ||
|
|
(BufferFlags & RPC_NCA_FLAGS_BROADCAST) ||
|
|
(0 == (ConvComparisonResult = RpcpMemoryCompare(&pSavedPacket->Header.InterfaceId,
|
|
&Conv->InterfaceId.SyntaxGUID,
|
|
sizeof(UUID)
|
|
))))
|
|
{
|
|
//
|
|
// Not all request packets have been transferred.
|
|
//
|
|
Status = SendSomeFragments();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Send a FACK if we have seen at least one response packet.
|
|
//
|
|
if (pReceivedPackets || ReceiveFragmentBase > 0)
|
|
{
|
|
Status = SendFackOrNocall(pReceivedPackets, DG_FACK);
|
|
}
|
|
else
|
|
{
|
|
Status = SendPing();
|
|
}
|
|
}
|
|
|
|
if (pAsync && AsyncStatus == RPC_S_ASYNC_CALL_PENDING)
|
|
{
|
|
PostDelayedSend();
|
|
}
|
|
|
|
if (Status != RPC_P_PORT_DOWN &&
|
|
Status != RPC_P_HOST_DOWN)
|
|
{
|
|
Status = 0;
|
|
}
|
|
else
|
|
{
|
|
RpcpErrorAddRecord( EEInfoGCRuntime,
|
|
Status,
|
|
EEInfoDLDG_CCALL__DealWithTimeout30
|
|
);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
INTERFACE_AND_OBJECT_LIST::Insert(
|
|
void __RPC_FAR * Interface,
|
|
RPC_UUID __RPC_FAR * Object
|
|
)
|
|
{
|
|
INTERFACE_AND_OBJECT * Current;
|
|
INTERFACE_AND_OBJECT * Prev;
|
|
unsigned Count;
|
|
|
|
for (Count = 0, Prev = 0, Current = Head;
|
|
Count < MAX_ELEMENTS, Current != NULL;
|
|
Count++, Prev = Current, Current = Current->Next)
|
|
{
|
|
if (Interface == Current->Interface &&
|
|
0 == Current->Object.MatchUuid(Object))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (Current)
|
|
{
|
|
//
|
|
// We have too many elements in the list. Reuse this one, the oldest.
|
|
//
|
|
if (Current == Head)
|
|
{
|
|
Head = Current->Next;
|
|
}
|
|
else
|
|
{
|
|
Prev->Next = Current->Next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Cache1Available)
|
|
{
|
|
Cache1Available = FALSE;
|
|
Current = &Cache1;
|
|
}
|
|
else if (Cache2Available)
|
|
{
|
|
Cache2Available = FALSE;
|
|
Current = &Cache2;
|
|
}
|
|
else
|
|
{
|
|
Current = new INTERFACE_AND_OBJECT;
|
|
if (!Current)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
Current->Update(Interface, Object);
|
|
Current->Next = Head;
|
|
Head = Current;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
INTERFACE_AND_OBJECT_LIST::Find(
|
|
void __RPC_FAR * Interface,
|
|
RPC_UUID __RPC_FAR * Object
|
|
)
|
|
{
|
|
INTERFACE_AND_OBJECT * Current;
|
|
INTERFACE_AND_OBJECT * Prev;
|
|
|
|
for (Current = Head; Current; Prev = Current, Current = Current->Next)
|
|
{
|
|
if (Interface == Current->Interface &&
|
|
0 == Current->Object.MatchUuid(Object))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Current)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (Current != Head)
|
|
{
|
|
Prev->Next = Current->Next;
|
|
Current->Next = Head;
|
|
Head = Current;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
INTERFACE_AND_OBJECT_LIST::Delete(
|
|
void __RPC_FAR * Interface,
|
|
RPC_UUID __RPC_FAR * Object
|
|
)
|
|
{
|
|
INTERFACE_AND_OBJECT * Current;
|
|
INTERFACE_AND_OBJECT * Prev;
|
|
|
|
for (Current = Head; Current; Prev = Current, Current = Current->Next)
|
|
{
|
|
if (Interface == Current->Interface &&
|
|
0 == Current->Object.MatchUuid(Object))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Current)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (Current == Head)
|
|
{
|
|
Head = Current->Next;
|
|
}
|
|
else
|
|
{
|
|
Prev->Next = Current->Next;
|
|
}
|
|
|
|
if (Current == &Cache1)
|
|
{
|
|
Cache1Available = TRUE;
|
|
}
|
|
else if (Current == &Cache2)
|
|
{
|
|
Cache2Available = TRUE;
|
|
}
|
|
else
|
|
{
|
|
delete Current;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
INTERFACE_AND_OBJECT_LIST::~INTERFACE_AND_OBJECT_LIST(
|
|
)
|
|
{
|
|
INTERFACE_AND_OBJECT * Next;
|
|
|
|
while (Head)
|
|
{
|
|
Next = Head->Next;
|
|
|
|
if (Head != &Cache1 && Head != &Cache2)
|
|
{
|
|
delete Head;
|
|
}
|
|
|
|
Head = Next;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef DEBUGRPC
|
|
|
|
void
|
|
DumpBuffer(
|
|
void FAR * Buffer,
|
|
unsigned Length
|
|
)
|
|
{
|
|
const BYTES_PER_LINE = 16;
|
|
|
|
unsigned char FAR *p = (unsigned char FAR *) Buffer;
|
|
|
|
//
|
|
// 3 chars per byte for hex display, plus an extra space every 4 bytes,
|
|
// plus a byte for the printable representation, plus the \0.
|
|
//
|
|
char Outbuf[BYTES_PER_LINE*3+BYTES_PER_LINE/4+BYTES_PER_LINE+1];
|
|
Outbuf[0] = 0;
|
|
Outbuf[sizeof(Outbuf)-1] = 0;
|
|
char * HexDigits = "0123456789abcdef";
|
|
|
|
unsigned Index;
|
|
for (unsigned Offset=0; Offset < Length; Offset++)
|
|
{
|
|
Index = Offset % BYTES_PER_LINE;
|
|
|
|
if (Index == 0)
|
|
{
|
|
DbgPrint(" %s\n", Outbuf);
|
|
memset(Outbuf, ' ', sizeof(Outbuf)-1);
|
|
}
|
|
|
|
Outbuf[Index*3+Index/4 ] = HexDigits[p[Offset] / 16];
|
|
Outbuf[Index*3+Index/4+1] = HexDigits[p[Offset] % 16];
|
|
Outbuf[BYTES_PER_LINE*3+BYTES_PER_LINE/4+Index] = iscntrl(p[Offset]) ? '.' : p[Offset];
|
|
}
|
|
|
|
DbgPrint(" %s\n", Outbuf);
|
|
}
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
|
RPC_STATUS
|
|
DispatchCallbackRequest(
|
|
DG_CLIENT_CALLBACK * CallbackObject
|
|
)
|
|
{
|
|
BOOL fAsyncCapable = FALSE;
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
RPC_MESSAGE CallbackMessage;
|
|
PNCA_PACKET_HEADER pHeader = &CallbackObject->Request->Header;
|
|
|
|
CallbackMessage.Buffer = 0;
|
|
|
|
if (pHeader->PacketFlags2 & DG_PF2_UNRELATED)
|
|
{
|
|
fAsyncCapable = TRUE;
|
|
}
|
|
|
|
//
|
|
// Allow only the internal "conv" interface as a callback.
|
|
//
|
|
if (0 != pHeader->InterfaceId.MatchUuid((RPC_UUID *) &((PRPC_SERVER_INTERFACE) conv_ServerIfHandle)->InterfaceId.SyntaxGUID ))
|
|
{
|
|
Status = RPC_S_UNKNOWN_IF;
|
|
}
|
|
else if (0 != (CallbackObject->Request->Header.PacketFlags & DG_PF_FRAG))
|
|
{
|
|
Status = RPC_S_CALL_FAILED_DNE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Dispatch to the callback stub.
|
|
// The client doesn't support Manager EPVs or nonidempotent callbacks.
|
|
//
|
|
RPC_STATUS ExceptionCode;
|
|
|
|
CallbackMessage.Handle = CallbackObject;
|
|
CallbackMessage.DataRepresentation = 0x00ffffff & (*(unsigned long *) &pHeader->DataRep);
|
|
CallbackMessage.Buffer = pHeader->Data;
|
|
CallbackMessage.BufferLength = pHeader->GetPacketBodyLen();
|
|
CallbackMessage.ProcNum = pHeader->OperationNumber;
|
|
|
|
CallbackMessage.ManagerEpv = 0;
|
|
CallbackMessage.ImportContext = 0;
|
|
CallbackMessage.TransferSyntax = 0;
|
|
CallbackMessage.RpcFlags = RPC_NCA_FLAGS_IDEMPOTENT;
|
|
|
|
CallbackMessage.RpcInterfaceInformation = conv_ServerIfHandle;
|
|
|
|
Status = DispatchCallback(((PRPC_SERVER_INTERFACE) CallbackMessage.RpcInterfaceInformation)->DispatchTable,
|
|
&CallbackMessage,
|
|
&ExceptionCode
|
|
);
|
|
if (Status)
|
|
{
|
|
if (Status == RPC_P_EXCEPTION_OCCURED)
|
|
{
|
|
Status = ExceptionCode;
|
|
}
|
|
}
|
|
else if (fAsyncCapable && CallbackObject->Connection)
|
|
{
|
|
CallbackObject->Connection->EnableOverlappedCalls();
|
|
}
|
|
|
|
LogEvent(SU_CCONN, EV_STATUS, CallbackObject->Connection, 0, Status);
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
LogError(SU_CCONN, EV_STATUS, CallbackObject->Connection, 0, Status);
|
|
|
|
InitErrorPacket(CallbackObject->Request, DG_REJECT, Status);
|
|
CallbackObject->SendPacket( pHeader );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
|
PDG_CCONNECTION
|
|
CLIENT_ACTIVITY_TABLE::Lookup(
|
|
RPC_UUID * Uuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
Packet
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
unsigned Hash = MakeHash(Uuid);
|
|
|
|
RequestHashMutex(Hash);
|
|
|
|
UUID_HASH_TABLE_NODE * Node = UUID_HASH_TABLE::Lookup(Uuid, Hash);
|
|
|
|
PDG_CCONNECTION Connection = 0;
|
|
|
|
if (Node)
|
|
{
|
|
Connection = DG_CCONNECTION::FromHashNode(Node);
|
|
|
|
Connection->MutexRequest();
|
|
ReleaseHashMutex(Hash);
|
|
}
|
|
else
|
|
{
|
|
ReleaseHashMutex(Hash);
|
|
}
|
|
|
|
return Connection;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
StandardPacketChecks(
|
|
DG_PACKET * pPacket
|
|
)
|
|
{
|
|
if (pPacket->Header.RpcVersion != DG_RPC_PROTOCOL_VERSION)
|
|
{
|
|
return NCA_STATUS_VERSION_MISMATCH;
|
|
}
|
|
|
|
ByteSwapPacketHeaderIfNecessary(pPacket);
|
|
|
|
if (0 == (pPacket->Flags & DG_PF_PARTIAL))
|
|
{
|
|
//
|
|
// Check for inconsistent packet length fields.
|
|
//
|
|
if (pPacket->DataLength < sizeof(NCA_PACKET_HEADER) ||
|
|
pPacket->DataLength - sizeof(NCA_PACKET_HEADER) < pPacket->GetPacketBodyLen())
|
|
{
|
|
return RPC_S_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
|
|
pPacket->DataLength -= sizeof(NCA_PACKET_HEADER);
|
|
|
|
//
|
|
// The X/Open standard does not give these fields a full byte.
|
|
//
|
|
pPacket->Header.RpcVersion &= 0x0F;
|
|
pPacket->Header.PacketType &= 0x1F;
|
|
|
|
//
|
|
// Fix up bogus OSF packets.
|
|
//
|
|
DeleteSpuriousAuthProto(pPacket);
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
|
void
|
|
ProcessDgClientPacket(
|
|
IN DWORD Status,
|
|
IN DG_TRANSPORT_ENDPOINT LocalEndpoint,
|
|
IN void * PacketHeader,
|
|
IN unsigned long PacketLength,
|
|
IN DatagramTransportPair *AddressPair
|
|
)
|
|
{
|
|
PDG_PACKET Packet = DG_PACKET::FromPacketHeader(PacketHeader);
|
|
|
|
Packet->DataLength = PacketLength;
|
|
|
|
#ifdef INTRODUCE_ERRORS
|
|
|
|
if (::ClientDropRate)
|
|
{
|
|
if ((GetRandomCounter() % 100) < ::ClientDropRate)
|
|
{
|
|
unsigned Frag = (Packet->Header.PacketType << 16) | Packet->Header.GetFragmentNumber();
|
|
unsigned Uuid = *(unsigned *) &Packet->Header.ActivityId;
|
|
unsigned Type = Packet->Header.PacketType;
|
|
|
|
LogError(SU_PACKET, EV_DROP, (void *) Uuid, (void *) Type, Frag);
|
|
|
|
Packet->Free();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (::ClientDelayRate)
|
|
{
|
|
if ((GetRandomCounter() % 100) < ::ClientDelayRate)
|
|
{
|
|
unsigned Frag = (Packet->Header.PacketType << 16) | Packet->Header.GetFragmentNumber();
|
|
unsigned Uuid = *(unsigned *) &Packet->Header.ActivityId;
|
|
unsigned Type = Packet->Header.PacketType;
|
|
|
|
LogError(SU_PACKET, EV_DELAY, (void *) Uuid, (void *) Type, Frag);
|
|
|
|
Sleep(::ClientDelayTime);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
if (Status == RPC_P_OVERSIZE_PACKET)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
PrintToDebugger("RPC DG: async packet is too large\n");
|
|
#endif
|
|
Packet->Flags |= DG_PF_PARTIAL;
|
|
Status = RPC_S_OK;
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
PrintToDebugger("RPC DG: async receive completed with status 0x%lx\n", Status);
|
|
RpcpBreakPoint();
|
|
#endif
|
|
Packet->Free(FALSE);
|
|
}
|
|
|
|
if (Packet->Header.PacketType == DG_REQUEST)
|
|
{
|
|
DG_CLIENT_CALLBACK CallbackObject;
|
|
|
|
CallbackObject.LocalEndpoint = DG_ENDPOINT::FromEndpoint( LocalEndpoint );
|
|
CallbackObject.Connection = 0;
|
|
CallbackObject.Request = Packet;
|
|
CallbackObject.RemoteAddress = AddressPair->RemoteAddress;
|
|
|
|
DispatchCallbackRequest(&CallbackObject);
|
|
return;
|
|
}
|
|
|
|
RPC_UUID ActivityId = Packet->Header.ActivityId;
|
|
|
|
if (NeedsByteSwap(&Packet->Header))
|
|
{
|
|
ByteSwapUuid(&ActivityId);
|
|
}
|
|
|
|
PDG_CCONNECTION Connection = ClientConnections->Lookup( &ActivityId );
|
|
|
|
if (Connection)
|
|
{
|
|
Connection->DispatchPacket(Packet, AddressPair->RemoteAddress);
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUGRPC
|
|
if (Packet->Header.PacketType != DG_QUACK)
|
|
{
|
|
PrintToDebugger("RPC DG: no connection found for async packet of type 0x%hx\n",
|
|
Packet->Header.PacketType);
|
|
}
|
|
#endif
|
|
Packet->Free(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
DG_CCONNECTION::DispatchPacket(
|
|
PDG_PACKET Packet,
|
|
DG_TRANSPORT_ADDRESS Address
|
|
)
|
|
{
|
|
Mutex.VerifyOwned();
|
|
|
|
PDG_CCALL Call;
|
|
|
|
if (RPC_S_OK != StandardPacketChecks(Packet))
|
|
{
|
|
Packet->Free();
|
|
return;
|
|
}
|
|
|
|
RPC_STATUS Status = UpdateServerAddress(Packet, Address);
|
|
if (Status)
|
|
{
|
|
MutexClear();
|
|
LogEvent(SU_CCONN, EV_PKT_IN, this, IntToPtr(Packet->Header.SequenceNumber), (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|
Packet->Free();
|
|
}
|
|
|
|
Call = ActiveCallHead;
|
|
while (Call && Call->GetSequenceNumber() < Packet->Header.SequenceNumber)
|
|
{
|
|
Call = Call->Next;
|
|
}
|
|
|
|
if (Call && Call->GetSequenceNumber() == Packet->Header.SequenceNumber)
|
|
{
|
|
Call->IncrementRefCount();
|
|
Call->DispatchPacket(Packet);
|
|
Call->DecrementRefCount();
|
|
}
|
|
else
|
|
{
|
|
MutexClear();
|
|
LogEvent(SU_CCONN, EV_PKT_IN, this, IntToPtr(Packet->Header.SequenceNumber), (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|
Packet->Free();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCALL::DispatchPacket(
|
|
IN DG_PACKET * Packet
|
|
)
|
|
{
|
|
BOOL ExtrasReference = FALSE;
|
|
RPC_STATUS Status;
|
|
PNCA_PACKET_HEADER pHeader = &Packet->Header;
|
|
|
|
LogEvent(SU_CCALL, EV_PKT_IN, this, (void *) 0, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|
|
|
if (State == CallCancellingSend ||
|
|
State == CallComplete )
|
|
{
|
|
FreePacket(Packet);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
CancelDelayedSend();
|
|
|
|
if (!CancelPending && TestCancel() > 0)
|
|
{
|
|
++CancelEventId;
|
|
CancelPending = TRUE;
|
|
CancelTime = GetTickCount();
|
|
}
|
|
|
|
if (CancelPending)
|
|
{
|
|
if ((long(GetTickCount()) - CancelTime) / 1000 > ThreadGetRpcCancelTimeout() )
|
|
{
|
|
RpcpErrorAddRecord( EEInfoGCRuntime,
|
|
RPC_S_CALL_CANCELLED,
|
|
EEInfoDLDG_CCALL__DispatchPacket10,
|
|
(ULONG) ThreadGetRpcCancelTimeout(),
|
|
(ULONG) (long(GetTickCount()) - CancelTime)
|
|
);
|
|
|
|
SendQuit();
|
|
CancelPending = FALSE;
|
|
FreePacket(Packet);
|
|
return RPC_S_CALL_CANCELLED;
|
|
}
|
|
}
|
|
|
|
if (Packet->Flags & DG_PF_PARTIAL)
|
|
{
|
|
SendFackOrNocall(Packet, DG_FACK);
|
|
|
|
FreePacket(Packet);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
// If the security context has expired, loop again - we will send
|
|
// a packet using the current security context, and the server
|
|
// will begin using that context to respond.
|
|
//
|
|
Status = Connection->VerifyPacket(Packet);
|
|
if (Status)
|
|
{
|
|
FreePacket(Packet);
|
|
|
|
if (Status == ERROR_SHUTDOWN_IN_PROGRESS)
|
|
{
|
|
return ERROR_SHUTDOWN_IN_PROGRESS;
|
|
}
|
|
|
|
if (pAsync && AsyncStatus == RPC_S_ASYNC_CALL_PENDING)
|
|
{
|
|
PostDelayedSend();
|
|
}
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
if (DG_NOCALL != pHeader->PacketType)
|
|
{
|
|
UnansweredRequestCount = 0;
|
|
TimeoutCount = 0;
|
|
Connection->fAutoReconnect = FALSE;
|
|
}
|
|
|
|
LastReceiveTime = GetTickCount();
|
|
|
|
//
|
|
// It's not clear when the hint is allowed to change, so let's
|
|
// always save it.
|
|
//
|
|
InterfaceHint = pHeader->InterfaceHint;
|
|
ActivityHint = pHeader->ActivityHint;
|
|
|
|
pSavedPacket->Header.InterfaceHint = pHeader->InterfaceHint;
|
|
pSavedPacket->Header.ActivityHint = pHeader->ActivityHint;
|
|
|
|
//
|
|
// Handle the packet.
|
|
//
|
|
switch (pHeader->PacketType)
|
|
{
|
|
case DG_RESPONSE: Status = DealWithResponse(Packet); break;
|
|
case DG_FACK: Status = DealWithFack (Packet); break;
|
|
case DG_WORKING: Status = DealWithWorking (Packet); break;
|
|
case DG_NOCALL: Status = DealWithNocall (Packet); break;
|
|
case DG_QUACK: Status = DealWithQuack (Packet); break;
|
|
case DG_FAULT: Status = DealWithFault (Packet); break;
|
|
case DG_REJECT: Status = DealWithReject (Packet); break;
|
|
case DG_PING: FreePacket (Packet); break;
|
|
default: Status = RPC_S_PROTOCOL_ERROR; break;
|
|
}
|
|
|
|
if (pAsync)
|
|
{
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
if (State == CallSend )
|
|
{
|
|
if (IsBufferAcknowledged())
|
|
{
|
|
SetState(CallQuiescent);
|
|
|
|
if (pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE)
|
|
{
|
|
IssueNotification( RpcSendComplete );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fReceivedAllFragments)
|
|
{
|
|
AsyncStatus = RPC_S_OK;
|
|
IssueNotification(RpcCallComplete);
|
|
}
|
|
else
|
|
{
|
|
if (State == CallReceive )
|
|
{
|
|
if (PipeReceiveSize && ConsecutiveDataBytes >= PipeReceiveSize)
|
|
{
|
|
IssueNotification( RpcReceiveComplete );
|
|
}
|
|
}
|
|
|
|
PostDelayedSend();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CancelDelayedSend();
|
|
|
|
AsyncStatus = MapErrorCode(Status);
|
|
IssueNotification(RpcCallComplete);
|
|
}
|
|
}
|
|
|
|
if (CancelPending)
|
|
{
|
|
SendQuit();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCONNECTION::BeginCall(
|
|
PDG_CCALL Call
|
|
)
|
|
{
|
|
//
|
|
// Add the call to the end of the active call list.
|
|
//
|
|
Call->SetSequenceNumber(LowestUnusedSequence++);
|
|
Call->Next = 0;
|
|
Call->Previous = ActiveCallTail;
|
|
|
|
if (ActiveCallHead)
|
|
{
|
|
ASSERT( Call->GetSequenceNumber() > ActiveCallTail->GetSequenceNumber() );
|
|
|
|
ActiveCallTail->Next = Call;
|
|
}
|
|
else
|
|
{
|
|
ActiveCallHead = Call;
|
|
}
|
|
|
|
ActiveCallTail = Call;
|
|
|
|
//
|
|
// Update CurrentCall if appropriate.
|
|
//
|
|
if (WillNextCallBeQueued() == RPC_S_ASYNC_CALL_PENDING)
|
|
{
|
|
LogEvent(SU_CCONN, EV_PUSH, this, Call, (ULONG_PTR) CurrentCall);
|
|
}
|
|
else
|
|
{
|
|
LogEvent(SU_CCONN, EV_PUSH, this, Call, 0);
|
|
if (CurrentCall)
|
|
{
|
|
CancelDelayedAck();
|
|
}
|
|
CurrentCall = Call;
|
|
}
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
void
|
|
DG_CCONNECTION::EndCall(
|
|
PDG_CCALL Call
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This fn removes a call from the active call list.
|
|
If the call posts a delayed ACK, this fn should not be called
|
|
until the delayed procedure runs or is cancelled.
|
|
|
|
Arguments:
|
|
|
|
Call - the call to remove
|
|
|
|
--*/
|
|
{
|
|
if (Call->Next == DG_CCALL_NOT_ACTIVE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Call->GetSequenceNumber() == LowestActiveSequence)
|
|
{
|
|
if (Call->Next)
|
|
{
|
|
LowestActiveSequence = Call->Next->GetSequenceNumber();
|
|
}
|
|
else
|
|
{
|
|
LowestActiveSequence = LowestUnusedSequence;
|
|
}
|
|
}
|
|
|
|
if (CurrentCall == Call)
|
|
{
|
|
CurrentCall = Call->Next;
|
|
|
|
//
|
|
// Queued async calls on a dead connection are cancelled, one at a time.
|
|
// As one is cleaned up, it will cancel the next, by induction.
|
|
//
|
|
if (this->fError)
|
|
{
|
|
if (CurrentCall && CurrentCall->InProgress())
|
|
{
|
|
CurrentCall->CancelAsyncCall( TRUE );
|
|
}
|
|
}
|
|
|
|
if (!CurrentCall)
|
|
{
|
|
CurrentCall = Call->Previous;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now remove the call from the active list.
|
|
//
|
|
if (Call->Previous)
|
|
{
|
|
Call->Previous->Next = Call->Next;
|
|
}
|
|
else
|
|
{
|
|
ActiveCallHead = Call->Next;
|
|
}
|
|
|
|
if (Call->Next)
|
|
{
|
|
Call->Next->Previous = Call->Previous;
|
|
}
|
|
else
|
|
{
|
|
ActiveCallTail = Call->Previous;
|
|
}
|
|
|
|
Call->Next = DG_CCALL_NOT_ACTIVE;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCONNECTION::MaybeTransmitNextCall()
|
|
{
|
|
RPC_STATUS Status = 0;
|
|
|
|
Mutex.VerifyOwned();
|
|
|
|
if (WillNextCallBeQueued() == RPC_S_ASYNC_CALL_PENDING)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
if (!CurrentCall || !CurrentCall->Next)
|
|
{
|
|
LogEvent(SU_CCONN, EV_POP, this, 0);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
PDG_CCALL Call = CurrentCall->Next;
|
|
|
|
LogEvent(SU_CCONN, EV_POP, this, Call);
|
|
|
|
ASSERT( Call->GetSequenceNumber() > CurrentCall->GetSequenceNumber() );
|
|
|
|
CancelDelayedAck();
|
|
|
|
CurrentCall = Call;
|
|
|
|
Status = Call->SendSomeFragments();
|
|
if (Status == RPC_P_HOST_DOWN ||
|
|
Status == RPC_P_PORT_DOWN)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CCONNECTION::WillNextCallBeQueued()
|
|
{
|
|
if (!CurrentCall)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
if (fServerSupportsAsync)
|
|
{
|
|
if (CurrentCall->AllArgsSent &&
|
|
CurrentCall->IsBufferAcknowledged())
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CurrentCall->fReceivedAllFragments)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
}
|
|
|
|
return RPC_S_ASYNC_CALL_PENDING;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CLIENT_CALLBACK::GetBuffer(
|
|
IN OUT PRPC_MESSAGE Message,
|
|
IN UUID *ObjectUuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is called to actually allocate memory for an rpc call.
|
|
|
|
Arguments:
|
|
|
|
Message - The RPC_MESSAGE structure associated with this call.
|
|
ObjectUuid - Ignored
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OUT_OF_MEMORY
|
|
RPC_S_OK
|
|
|
|
--*/
|
|
{
|
|
PDG_PACKET pPacket;
|
|
|
|
//
|
|
// Set up the message structure to point at this DG_CCALL.
|
|
//
|
|
Message->Handle = (RPC_BINDING_HANDLE)this;
|
|
|
|
if (Message->BufferLength < LocalEndpoint->Stats.PreferredPduSize)
|
|
{
|
|
Message->BufferLength = LocalEndpoint->Stats.PreferredPduSize;
|
|
}
|
|
|
|
pPacket = DG_PACKET::AllocatePacket(Message->BufferLength);
|
|
|
|
if (0 == pPacket)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Point the buffer at the appropriate place in the packet.
|
|
//
|
|
Message->Buffer = pPacket->Header.Data;
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
void
|
|
DG_CLIENT_CALLBACK::FreeBuffer(
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called by stubs in order to free a marshalling buffer.
|
|
|
|
Arguments:
|
|
|
|
Message - The RPC_MESSAGE structure associated with this call.
|
|
|
|
Return Value:
|
|
|
|
<none>
|
|
|
|
--*/
|
|
{
|
|
if (Message->Buffer)
|
|
{
|
|
PDG_PACKET Packet = DG_PACKET::FromStubData(Message->Buffer);
|
|
|
|
Packet->Free(FALSE);
|
|
if (Packet == Request)
|
|
{
|
|
Request = 0;
|
|
}
|
|
|
|
Message->Buffer = 0;
|
|
}
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_CLIENT_CALLBACK::SetAsyncHandle (
|
|
IN RPC_ASYNC_STATE * hAsync
|
|
)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
RPC_STATUS
|
|
DG_CLIENT_CALLBACK::AsyncSend(
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
{
|
|
return Send(Message);
|
|
}
|
|
|
|
RPC_STATUS
|
|
DG_CLIENT_CALLBACK::Send(
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
{
|
|
PNCA_PACKET_HEADER pHeader = CONTAINING_RECORD( Message->Buffer, NCA_PACKET_HEADER, Data );
|
|
DG_PACKET * Packet = CONTAINING_RECORD( Message->Buffer, DG_PACKET, Header.Data );
|
|
|
|
//
|
|
// The callback was successful, so the buffer has changed.
|
|
// Create the packet header, send the response, and free the
|
|
// request and response buffers.
|
|
//
|
|
|
|
*pHeader = Request->Header;
|
|
|
|
SetMyDataRep(pHeader);
|
|
|
|
pHeader->PacketType = DG_RESPONSE;
|
|
pHeader->PacketFlags = DG_PF_NO_FACK;
|
|
pHeader->AuthProto = 0;
|
|
pHeader->SetPacketBodyLen (Message->BufferLength);
|
|
pHeader->SetFragmentNumber(0);
|
|
|
|
if (Message->BufferLength + sizeof(NCA_PACKET_HEADER) > LocalEndpoint->Stats.MaxPduSize)
|
|
{
|
|
InitErrorPacket( Packet, DG_FAULT, NCA_STATUS_OUT_ARGS_TOO_BIG );
|
|
}
|
|
|
|
SendPacket( pHeader );
|
|
|
|
FreeBuffer(Message);
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
RPC_STATUS
|
|
DG_CLIENT_CALLBACK::SendReceive(
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
{
|
|
return RPC_S_CANNOT_SUPPORT;
|
|
}
|
|
|
|
RPC_STATUS
|
|
DG_CLIENT_CALLBACK::Receive(
|
|
IN OUT PRPC_MESSAGE Message,
|
|
IN unsigned MinimumSize
|
|
)
|
|
{
|
|
return RPC_S_CANNOT_SUPPORT;
|
|
}
|
|
|
|
|
|
ENDPOINT_MANAGER::ENDPOINT_MANAGER(
|
|
IN OUT RPC_STATUS * pStatus
|
|
) :
|
|
Mutex(pStatus)
|
|
{
|
|
unsigned u;
|
|
|
|
for (u=0; u < DG_TRANSPORT_COUNT; ++u)
|
|
{
|
|
AsyncEndpoints[u] = 0;
|
|
}
|
|
|
|
Endpoints = 0;
|
|
|
|
LastScavengeTime = GetTickCount();
|
|
}
|
|
|
|
|
|
DG_ENDPOINT *
|
|
ENDPOINT_MANAGER::RequestEndpoint(
|
|
IN RPC_DATAGRAM_TRANSPORT * TransportInterface,
|
|
IN BOOL Async,
|
|
IN DWORD Flags
|
|
)
|
|
{
|
|
ASSERT(0 == (Flags & PENALTY_BOX) );
|
|
|
|
if (Async)
|
|
{
|
|
unsigned u;
|
|
for (u=0; u < DG_TRANSPORT_COUNT && AsyncEndpoints[u]; ++u)
|
|
{
|
|
if (AsyncEndpoints[u]->TransportInterface == TransportInterface &&
|
|
AsyncEndpoints[u]->Flags == Flags)
|
|
{
|
|
LogEvent(SU_CENDPOINT, EV_START, AsyncEndpoints[u], 0, Async);
|
|
|
|
InterlockedIncrement(&AsyncEndpoints[u]->NumberOfCalls);
|
|
|
|
return AsyncEndpoints[u];
|
|
}
|
|
}
|
|
|
|
Mutex.Request();
|
|
|
|
for (; u < DG_TRANSPORT_COUNT && AsyncEndpoints[u]; ++u)
|
|
{
|
|
if (AsyncEndpoints[u]->TransportInterface == TransportInterface &&
|
|
AsyncEndpoints[u]->Flags == Flags)
|
|
{
|
|
Mutex.Clear();
|
|
LogEvent(SU_CENDPOINT, EV_START, AsyncEndpoints[u], 0, Async);
|
|
|
|
InterlockedIncrement(&AsyncEndpoints[u]->NumberOfCalls);
|
|
|
|
return AsyncEndpoints[u];
|
|
}
|
|
}
|
|
|
|
DG_ENDPOINT * Node;
|
|
|
|
Node = (DG_ENDPOINT *) new char[sizeof(DG_ENDPOINT) + TransportInterface->ClientEndpointSize];
|
|
|
|
LogEvent(SU_CENDPOINT, EV_CREATE, Node, TransportInterface, Async);
|
|
|
|
if (Node)
|
|
{
|
|
Node->TransportInterface = TransportInterface;
|
|
Node->Async = TRUE;
|
|
Node->Flags = Flags;
|
|
|
|
RPC_STATUS Status = TransportInterface->OpenEndpoint(&Node->TransportEndpoint, TRUE, Flags);
|
|
if (Status)
|
|
{
|
|
LogError(SU_CENDPOINT, EV_STATUS, Node, 0, Status);
|
|
|
|
Mutex.Clear();
|
|
delete Node;
|
|
return 0;
|
|
}
|
|
|
|
Status = TransportInterface->QueryEndpointStats(&Node->TransportEndpoint, &Node->Stats);
|
|
if (Status)
|
|
{
|
|
LogError(SU_CENDPOINT, EV_STATUS, Node, 0, Status);
|
|
Mutex.Clear();
|
|
|
|
Node->TransportInterface->Close(Node->TransportEndpoint);
|
|
delete Node;
|
|
return 0;
|
|
}
|
|
|
|
Node->NumberOfCalls = 1;
|
|
|
|
AsyncEndpoints[u] = Node;
|
|
|
|
Mutex.Clear();
|
|
|
|
return AsyncEndpoints[u];
|
|
}
|
|
|
|
LogEvent(SU_CENDPOINT, EV_STATUS, Node, 0, 0);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
Mutex.Request();
|
|
|
|
DG_ENDPOINT * Previous = 0;
|
|
DG_ENDPOINT * Node = Endpoints;
|
|
|
|
//
|
|
// Look for a node on the right transport and with the same flags.
|
|
// If the flags match except for the penalty box, use it only if
|
|
// the endpoint has been idle for the right length of time.
|
|
// The caller must drain any buffered packets on a penalty-box endpoint.
|
|
//
|
|
for ( ; Node; Previous=Node, Node=Node->Next )
|
|
{
|
|
if (Node->TransportInterface != TransportInterface)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (Node->Flags == Flags)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Node->Flags == (Flags | PENALTY_BOX) &&
|
|
GetTickCount() - Node->TimeStamp >= PENALTY_BOX_DURATION)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Node)
|
|
{
|
|
if (Previous)
|
|
{
|
|
Previous->Next = Node->Next;
|
|
}
|
|
else
|
|
{
|
|
Endpoints = Node->Next;
|
|
}
|
|
|
|
Node->NumberOfCalls = 1;
|
|
|
|
Mutex.Clear();
|
|
|
|
LogEvent(SU_CENDPOINT, EV_START, Node, 0, Async);
|
|
|
|
return Node;
|
|
}
|
|
else
|
|
{
|
|
Mutex.Clear();
|
|
|
|
Node = (DG_ENDPOINT *) new char[sizeof(DG_ENDPOINT) + TransportInterface->ClientEndpointSize];
|
|
|
|
LogEvent(SU_CENDPOINT, EV_CREATE, Node, TransportInterface, Async);
|
|
|
|
if (Node)
|
|
{
|
|
Node->TransportInterface = TransportInterface;
|
|
Node->Async = FALSE;
|
|
Node->Flags = Flags;
|
|
|
|
RPC_STATUS Status = TransportInterface->OpenEndpoint(&Node->TransportEndpoint, FALSE, Flags);
|
|
if (Status)
|
|
{
|
|
LogEvent(SU_CENDPOINT, EV_STATUS, Node, 0, Status);
|
|
|
|
delete Node;
|
|
return 0;
|
|
}
|
|
|
|
Status = TransportInterface->QueryEndpointStats(&Node->TransportEndpoint, &Node->Stats);
|
|
if (Status)
|
|
{
|
|
LogEvent(SU_CENDPOINT, EV_STATUS, Node, 0, Status);
|
|
|
|
Mutex.Clear();
|
|
|
|
Node->TransportInterface->Close(Node->TransportEndpoint);
|
|
delete Node;
|
|
return 0;
|
|
}
|
|
|
|
Node->NumberOfCalls = 1;
|
|
return Node;
|
|
}
|
|
|
|
LogEvent(SU_CENDPOINT, EV_STATUS, Node, 0, 0);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ENDPOINT_MANAGER::ReleaseEndpoint(
|
|
IN DG_ENDPOINT * Node
|
|
)
|
|
{
|
|
LogEvent(SU_CENDPOINT, EV_STOP, Node, 0, Node->Async);
|
|
|
|
if (Node->Async)
|
|
{
|
|
InterlockedDecrement(&Node->NumberOfCalls);
|
|
}
|
|
else
|
|
{
|
|
Node->TimeStamp = GetTickCount();
|
|
|
|
Mutex.Request();
|
|
|
|
Node->Next = Endpoints;
|
|
Endpoints = Node;
|
|
|
|
Mutex.Clear();
|
|
|
|
EnableGlobalScavenger();
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
ENDPOINT_MANAGER::DeleteIdleEndpoints(
|
|
long CurrentTime
|
|
)
|
|
{
|
|
boolean More = FALSE;
|
|
|
|
Mutex.Request();
|
|
|
|
DG_ENDPOINT * Node;
|
|
DG_ENDPOINT * Prev = 0;
|
|
|
|
for (Node = Endpoints; Node; Node = Node->Next)
|
|
{
|
|
ASSERT( Node->Async == FALSE );
|
|
|
|
if (CurrentTime - Node->TimeStamp > IDLE_ENDPOINT_LIFETIME )
|
|
{
|
|
break;
|
|
}
|
|
|
|
Prev = Node;
|
|
More = TRUE;
|
|
}
|
|
|
|
if (Prev)
|
|
{
|
|
Prev->Next = 0;
|
|
}
|
|
else
|
|
{
|
|
Endpoints = 0;
|
|
}
|
|
|
|
LastScavengeTime = CurrentTime;
|
|
|
|
Mutex.Clear();
|
|
|
|
while (Node)
|
|
{
|
|
DG_ENDPOINT * Next = Node->Next;
|
|
|
|
LogEvent(SU_CENDPOINT, EV_DELETE, Node, Node->TransportInterface, Node->Async);
|
|
|
|
Node->TransportInterface->Close(Node->TransportEndpoint);
|
|
delete Node;
|
|
|
|
Node = Next;
|
|
}
|
|
|
|
return More;
|
|
}
|
|
|
|
void
|
|
DG_CCALL::PostDelayedSend()
|
|
{
|
|
Connection->Mutex.VerifyOwned();
|
|
|
|
//
|
|
// Cancel the rpevious send, to make sure the pending-send count is accurate.
|
|
// Then post the new request.
|
|
//
|
|
CancelDelayedSend();
|
|
|
|
DelayedSendPending++;
|
|
DelayedProcedures->Add(&TransmitTimer, (GetTickCount() - LastReceiveTime) + ReceiveTimeout, TRUE);
|
|
|
|
LogEvent(SU_CCALL, EV_PROC, this, 0, 0x6e534450);
|
|
}
|
|
|
|
void
|
|
DG_CCALL::CancelDelayedSend()
|
|
{
|
|
Connection->Mutex.VerifyOwned();
|
|
|
|
LogEvent(SU_CCALL, EV_PROC, this, 0, 0x6e534443);
|
|
if (!DelayedSendPending)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (TRUE == DelayedProcedures->Cancel(&TransmitTimer))
|
|
{
|
|
DelayedSendPending--;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We just missed the activation of the delayed procedure.
|
|
// This thread must let go of the mutex in order for ExecuteDelayedSend
|
|
// to continue. It's important that no other thread change the call state
|
|
// during this process, since ExecuteDelayedSend checks for that state.
|
|
//
|
|
DG_CLIENT_STATE OldState = State;
|
|
|
|
SetState(CallCancellingSend);
|
|
|
|
Connection->MutexClear();
|
|
|
|
while (DelayedSendPending)
|
|
{
|
|
Sleep(1);
|
|
}
|
|
|
|
Connection->MutexRequest();
|
|
|
|
SetState(OldState);
|
|
}
|
|
|
|
void
|
|
DG_CCALL::ExecuteDelayedSend()
|
|
{
|
|
LogEvent(SU_CCALL, EV_PROC, this, 0, 0x6e534445);
|
|
|
|
Connection->MutexRequest();
|
|
|
|
DelayedSendPending--;
|
|
|
|
if (DelayedSendPending)
|
|
{
|
|
//
|
|
// Multiple sends are pending. Only one is helpful.
|
|
//
|
|
Connection->MutexClear();
|
|
return;
|
|
}
|
|
|
|
if (State == CallCancellingSend)
|
|
{
|
|
Connection->MutexClear();
|
|
return;
|
|
}
|
|
|
|
if (!TimeoutCount)
|
|
{
|
|
ReceiveTimeout = 500;
|
|
}
|
|
|
|
IncreaseReceiveTimeout();
|
|
|
|
RPC_STATUS Status = DealWithTimeout();
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
AsyncStatus = MapErrorCode(Status);
|
|
IssueNotification( RpcCallComplete );
|
|
}
|
|
|
|
Connection->MutexClear();
|
|
}
|
|
|
|
|
|
void
|
|
DelayedSendProc(
|
|
void * parm
|
|
)
|
|
{
|
|
PDG_CCALL(parm)->ExecuteDelayedSend();
|
|
}
|
|
|
|
|
|
DG_ASSOCIATION_TABLE::DG_ASSOCIATION_TABLE(
|
|
RPC_STATUS * pStatus
|
|
)
|
|
: Mutex( *pStatus )
|
|
{
|
|
Associations = InitialAssociations;
|
|
AssociationsLength = INITIAL_ARRAY_LENGTH;
|
|
|
|
long i;
|
|
for (i=0; i < AssociationsLength; ++i)
|
|
{
|
|
Associations[i].fBusy = FALSE;
|
|
Associations[i].Hint = ~0;
|
|
}
|
|
|
|
fCasUuidReady = FALSE;
|
|
|
|
PreviousFreedCount = MINIMUM_IDLE_ENTRIES;
|
|
}
|
|
|
|
|
|
|
|
|
|
RPC_STATUS
|
|
DG_ASSOCIATION_TABLE::Add(
|
|
DG_CASSOCIATION * Association
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds <Association> to the table.
|
|
|
|
Return Value:
|
|
|
|
zero if success
|
|
a Win32 error if failure
|
|
|
|
--*/
|
|
{
|
|
LONG i;
|
|
LONG OldAssociationsLength;
|
|
|
|
//
|
|
// Make a hint for the association.
|
|
//
|
|
HINT Hint = MakeHint( Association->pDceBinding );
|
|
|
|
//
|
|
// Look for an open space in the current block.
|
|
//
|
|
Mutex.LockExclusive();
|
|
|
|
//
|
|
// Make sure this process has a "client address space UUID". The callback
|
|
// for each connection will ask for it.
|
|
// We do this here because we are not supposed to do it in InitializeRpcProtocolDgClient.
|
|
// It would cause a perf hit even in code that uses only LRPCbindings.
|
|
//
|
|
if (!fCasUuidReady)
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
Status = UuidCreate(&CasUuid);
|
|
if (Status == RPC_S_UUID_LOCAL_ONLY)
|
|
{
|
|
Status = 0;
|
|
}
|
|
|
|
if (Status)
|
|
{
|
|
Mutex.UnlockExclusive();
|
|
|
|
LogEvent( SU_CASSOC, EV_STATUS, 0, 0, Status );
|
|
return Status;
|
|
}
|
|
|
|
fCasUuidReady = TRUE;
|
|
}
|
|
|
|
for (i = 0; i < AssociationsLength; ++i)
|
|
{
|
|
if (!Associations[i].fBusy)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == AssociationsLength)
|
|
{
|
|
//
|
|
// The current block is full; allocate an expanded block.
|
|
//
|
|
LONG NewAssociationsLength = 2*AssociationsLength;
|
|
NODE * NewAssociations = new NODE[ NewAssociationsLength ];
|
|
if (!NewAssociations)
|
|
{
|
|
Mutex.UnlockExclusive();
|
|
|
|
LogEvent( SU_CASSOC, EV_STATUS, 0, 0, RPC_S_OUT_OF_MEMORY );
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Initialize the used and unused sections of the new block.
|
|
//
|
|
RpcpMemoryCopy( NewAssociations, Associations, sizeof(NODE) * AssociationsLength );
|
|
|
|
RpcpMemorySet( &NewAssociations[ AssociationsLength ], 0, sizeof(NODE) * (NewAssociationsLength-AssociationsLength) );
|
|
|
|
//
|
|
// Replace the block.
|
|
//
|
|
if (Associations != InitialAssociations)
|
|
{
|
|
delete Associations;
|
|
}
|
|
|
|
i = AssociationsLength;
|
|
|
|
Associations = NewAssociations;
|
|
AssociationsLength = NewAssociationsLength;
|
|
}
|
|
|
|
Associations[i].fBusy = TRUE;
|
|
Associations[i].Hint = Hint;
|
|
Associations[i].Association = Association;
|
|
Associations[i].TimeStamp = GetTickCount();
|
|
Associations[i].ContextHandleCount = 0;
|
|
|
|
Association->InternalTableIndex = i;
|
|
|
|
Mutex.UnlockExclusive();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
DG_CASSOCIATION *
|
|
DG_ASSOCIATION_TABLE::Find(
|
|
DG_BINDING_HANDLE * Binding,
|
|
RPC_CLIENT_INTERFACE * Interface,
|
|
BOOL fContextHandle,
|
|
BOOL fPartial,
|
|
BOOL fAutoReconnect
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Locate an association that matches <Binding>'s DCE_BINDING.
|
|
If <fPartial> is true, the endpoint will not be compared, but the
|
|
association must have <Interface> in its list of successful interfaces
|
|
in order to match. <fContextHandle> is true if the binding handle is
|
|
embedded in a context handle.
|
|
|
|
Return Value:
|
|
|
|
the association, if an matching one was found
|
|
NULL otherwise
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Calculate the hint for a matching association.
|
|
//
|
|
HINT Hint = MakeHint( Binding->pDceBinding );
|
|
|
|
//
|
|
// For auto-reconnect, only recently-successful associations qualify.
|
|
// MinimumTime is after the failed call began, at least for most com-timeout
|
|
// settings.
|
|
//
|
|
long MinimumTime = GetTickCount() - (15 * 1000);
|
|
|
|
Mutex.LockShared();
|
|
|
|
long i;
|
|
for (i = 0; i < AssociationsLength; ++i)
|
|
{
|
|
if (Associations[i].Hint == Hint &&
|
|
Associations[i].fBusy)
|
|
{
|
|
DG_CASSOCIATION * Association = Associations[i].Association;
|
|
|
|
if (Association->ErrorFlag())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (Association->fLoneBindingHandle)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (fAutoReconnect &&
|
|
MinimumTime - Association->LastReceiveTime > 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (fPartial)
|
|
{
|
|
if (Association->ComparePartialBinding(Binding, Interface) == TRUE)
|
|
{
|
|
Association->IncrementBindingRefCount(fContextHandle);
|
|
Associations[i].TimeStamp = GetTickCount();
|
|
|
|
Mutex.UnlockShared();
|
|
return Association;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Association->CompareWithBinding(Binding) == 0)
|
|
{
|
|
Association->IncrementBindingRefCount(fContextHandle);
|
|
Associations[i].TimeStamp = GetTickCount();
|
|
|
|
Mutex.UnlockShared();
|
|
return Association;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Mutex.UnlockShared();
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
ContextHandleProc(
|
|
void * arg
|
|
)
|
|
{
|
|
ActiveAssociations->SendContextHandleKeepalives();
|
|
}
|
|
|
|
BOOL
|
|
DG_ASSOCIATION_TABLE::SendContextHandleKeepalives()
|
|
{
|
|
BOOL fContextHandles = FALSE;
|
|
long StartTime = GetTickCount();
|
|
|
|
Mutex.LockShared();
|
|
|
|
long i;
|
|
for (i=0; i < AssociationsLength; ++i)
|
|
{
|
|
if (!Associations[i].fBusy)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (Associations[i].ContextHandleCount > 0)
|
|
{
|
|
fContextHandles = TRUE;
|
|
|
|
if (StartTime - Associations[i].TimeStamp >= CXT_HANDLE_KEEPALIVE_INTERVAL)
|
|
{
|
|
//
|
|
// We have to avoid holding the shared lock when calling a procedure
|
|
// that may take the exclusive lock.
|
|
// DG_CASSOCIATION::DecrementRefCount may delete the association,
|
|
// and that would want the exclusive table lock.
|
|
//
|
|
Associations[i].Association->IncrementRefCount();
|
|
Mutex.UnlockShared();
|
|
|
|
if (Associations[i].Association->SendKeepAlive())
|
|
{
|
|
Associations[i].TimeStamp = GetTickCount();
|
|
}
|
|
|
|
Associations[i].Association->DecrementRefCount();
|
|
Mutex.LockShared();
|
|
}
|
|
}
|
|
}
|
|
|
|
Mutex.UnlockShared();
|
|
|
|
if (fContextHandles)
|
|
{
|
|
long FinishTime = GetTickCount();
|
|
|
|
if (FinishTime-StartTime >= CXT_HANDLE_SWEEP_INTERVAL)
|
|
{
|
|
//
|
|
// We are behind. Recursion or "goto start" would be cheaper,
|
|
// but would block other delayed procs that are ready to fire.
|
|
//
|
|
DelayedProcedures->Add(ContextHandleTimer, 0, TRUE);
|
|
}
|
|
else
|
|
{
|
|
DelayedProcedures->Add(ContextHandleTimer, CXT_HANDLE_SWEEP_INTERVAL-(FinishTime-StartTime), TRUE);
|
|
}
|
|
}
|
|
return fContextHandles;
|
|
}
|
|
|
|
|
|
void
|
|
DG_ASSOCIATION_TABLE::Delete(
|
|
DG_CASSOCIATION * Association
|
|
)
|
|
{
|
|
long Index = Association->InternalTableIndex;
|
|
|
|
if (Index == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ASSERT( Index >= 0 && Index < AssociationsLength );
|
|
ASSERT( Associations[Index].Association == Association );
|
|
|
|
Mutex.LockExclusive();
|
|
|
|
ASSERT( Associations[Index].fBusy );
|
|
|
|
Associations[Index].fBusy = FALSE;
|
|
Associations[Index].Hint = ~0;
|
|
|
|
Mutex.UnlockExclusive();
|
|
|
|
delete Association;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DG_ASSOCIATION_TABLE::DeleteIdleEntries(
|
|
long CurrentTime
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This deletes entries idle more than IDLE_CASSOCIATION_LIFETIME milliseconds.
|
|
|
|
The fn takes only the read lock until it finds an entry that needs deletion.
|
|
Then it takes the write lock, and keeps it until the table is scoured.
|
|
|
|
Return Value:
|
|
|
|
TRUE if items are left in the table at the end
|
|
FALSE if the table is empty at the end
|
|
|
|
--*/
|
|
{
|
|
BOOL fExclusive = FALSE;
|
|
BOOL fLeftovers = FALSE;
|
|
long i = 0;
|
|
DG_CASSOCIATION * Association;
|
|
|
|
//
|
|
// Initialize an array to hold removed associations.
|
|
//
|
|
long NextFreedEntry = 0;
|
|
long MaximumFreedEntries = PreviousFreedCount;
|
|
|
|
DG_CASSOCIATION ** FreedEntries = (DG_CASSOCIATION **) _alloca( sizeof(DG_CASSOCIATION *) * MaximumFreedEntries );
|
|
|
|
memset( FreedEntries, 0, sizeof(DG_CASSOCIATION *) * MaximumFreedEntries );
|
|
|
|
//
|
|
//
|
|
//
|
|
Mutex.LockShared();
|
|
|
|
for (i=0; i < AssociationsLength; ++i)
|
|
{
|
|
if (Associations[i].fBusy)
|
|
{
|
|
if (CurrentTime - Associations[i].TimeStamp > IDLE_CASSOCIATION_LIFETIME)
|
|
{
|
|
//
|
|
// We must claim the exclusive lock now to get an accurate answer.
|
|
//
|
|
if (!fExclusive)
|
|
{
|
|
fExclusive = TRUE;
|
|
Mutex.ConvertToExclusive();
|
|
}
|
|
|
|
if (Associations[i].fBusy &&
|
|
CurrentTime - Associations[i].TimeStamp > IDLE_CASSOCIATION_LIFETIME &&
|
|
Associations[i].Association->ReferenceCount.GetInteger() == 0)
|
|
{
|
|
//
|
|
// It's official; the association will be deleted.
|
|
// Add it to the deletion array and release its slot in Associations[]..
|
|
//
|
|
Associations[i].Association->InternalTableIndex = -1;
|
|
FreedEntries[ NextFreedEntry ] = Associations[i].Association;
|
|
|
|
Associations[i].fBusy = FALSE;
|
|
Associations[i].Hint = ~0;
|
|
|
|
++NextFreedEntry;
|
|
if (NextFreedEntry == MaximumFreedEntries)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fLeftovers = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fLeftovers = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fExclusive)
|
|
{
|
|
Mutex.UnlockExclusive();
|
|
}
|
|
else
|
|
{
|
|
Mutex.UnlockShared();
|
|
}
|
|
|
|
//
|
|
// Calculate the length of the array for the next pass.
|
|
//
|
|
if (NextFreedEntry == MaximumFreedEntries)
|
|
{
|
|
fLeftovers = TRUE;
|
|
PreviousFreedCount = MaximumFreedEntries * 2;
|
|
}
|
|
else if (NextFreedEntry < MINIMUM_IDLE_ENTRIES)
|
|
{
|
|
PreviousFreedCount = MINIMUM_IDLE_ENTRIES;
|
|
}
|
|
else
|
|
{
|
|
PreviousFreedCount = NextFreedEntry;
|
|
}
|
|
|
|
//
|
|
// Delete the removed associations.
|
|
//
|
|
--NextFreedEntry;
|
|
while (NextFreedEntry >= 0)
|
|
{
|
|
delete FreedEntries[ NextFreedEntry ];
|
|
--NextFreedEntry;
|
|
}
|
|
|
|
return fLeftovers;
|
|
}
|
|
|
|
|
|
DG_ASSOCIATION_TABLE::HINT
|
|
DG_ASSOCIATION_TABLE::MakeHint(
|
|
DCE_BINDING * pDceBinding
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Construct a cheap hint to scan the association table.
|
|
The hint should not depend on the endpoint, since we want
|
|
a partial binding's pDceBinding to match an association
|
|
that has a resolved endpoint.
|
|
|
|
Return Value:
|
|
|
|
The hint.
|
|
|
|
--*/
|
|
{
|
|
HINT Hint = 0;
|
|
RPC_CHAR * String;
|
|
|
|
String = pDceBinding->InqNetworkAddress();
|
|
|
|
while (*String)
|
|
{
|
|
Hint *= 37;
|
|
Hint += *String;
|
|
++String;
|
|
}
|
|
|
|
return Hint;
|
|
}
|
|
|
|
|
|
void
|
|
DG_ASSOCIATION_TABLE::IncrementContextHandleCount(
|
|
DG_CASSOCIATION * Association
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Increment the count of context handles associated with this index.
|
|
This is stired in the table rather than in the association
|
|
so that the count can be accessed even if the association is swapped out.
|
|
|
|
--*/
|
|
{
|
|
long Index = Association->InternalTableIndex;
|
|
|
|
if (Index == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Mutex.LockShared();
|
|
|
|
ASSERT( Index >= 0 && Index < AssociationsLength );
|
|
ASSERT( Associations[Index].Association == Association );
|
|
|
|
++Associations[Index].ContextHandleCount;
|
|
|
|
Mutex.UnlockShared();
|
|
}
|
|
|
|
|
|
void
|
|
DG_ASSOCIATION_TABLE::DecrementContextHandleCount(
|
|
DG_CASSOCIATION * Association
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrement the count of context handles associated with this index.
|
|
This is stired in the table rather than in the association
|
|
so that the count can be accessed even if the association is swapped out.
|
|
|
|
--*/
|
|
{
|
|
long Index = Association->InternalTableIndex;
|
|
|
|
if (Index == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Mutex.LockShared();
|
|
|
|
ASSERT( Index >= 0 && Index < AssociationsLength );
|
|
ASSERT( Associations[Index].Association == Association );
|
|
|
|
--Associations[Index].ContextHandleCount;
|
|
|
|
Mutex.UnlockShared();
|
|
}
|
|
|
|
|
|
long
|
|
DG_ASSOCIATION_TABLE::GetContextHandleCount(
|
|
DG_CASSOCIATION * Association
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrement the count of context handles associated with this index.
|
|
This is stired in the table rather than in the association
|
|
so that the count can be accessed even if the association is swapped out.
|
|
|
|
--*/
|
|
{
|
|
long Count;
|
|
long Index = Association->InternalTableIndex;
|
|
|
|
if (Index == -1)
|
|
{
|
|
return 0;
|
|
}
|
|
Mutex.LockShared();
|
|
|
|
ASSERT( Index >= 0 && Index < AssociationsLength );
|
|
ASSERT( Associations[Index].Association == Association );
|
|
|
|
Count = Associations[Index].ContextHandleCount;
|
|
|
|
Mutex.UnlockShared();
|
|
|
|
return Count;
|
|
}
|
|
|
|
|
|
void
|
|
DG_ASSOCIATION_TABLE::UpdateTimeStamp(
|
|
DG_CASSOCIATION * Association
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrement the count of context handles associated with this index.
|
|
This is stired in the table rather than in the association
|
|
so that the count can be accessed even if the association is swapped out.
|
|
|
|
--*/
|
|
{
|
|
long Index = Association->InternalTableIndex;
|
|
|
|
if (Index == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ASSERT( Index >= 0 && Index < AssociationsLength );
|
|
ASSERT( Associations[Index].Association == Association );
|
|
|
|
Associations[Index].TimeStamp = GetTickCount();
|
|
}
|
|
|
|
|
|
//
|
|
// test hook data.
|
|
//
|
|
//
|
|
// casting unsigned to/from void * is OK in this case.
|
|
//
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4312)
|
|
NEW_NAMED_SDICT2( TEST_HOOK, RPC_TEST_HOOK_FN_RAW, RPC_TEST_HOOK_ID );
|
|
#pragma warning(pop)
|
|
|
|
TEST_HOOK_DICT2 * pTestHookDict;
|
|
|
|
RPCRTAPI
|
|
DWORD
|
|
RPC_ENTRY
|
|
I_RpcSetTestHook(
|
|
RPC_TEST_HOOK_ID id,
|
|
RPC_TEST_HOOK_FN fn
|
|
)
|
|
{
|
|
InitializeIfNecessary();
|
|
|
|
GlobalMutexRequest();
|
|
|
|
if (0 == pTestHookDict)
|
|
{
|
|
pTestHookDict = new TEST_HOOK_DICT2;
|
|
if (!pTestHookDict)
|
|
{
|
|
GlobalMutexClear();
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
pTestHookDict->Delete( id );
|
|
pTestHookDict->Insert( id, fn );
|
|
|
|
GlobalMutexClear();
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ForceCallTestHook(
|
|
RPC_TEST_HOOK_ID id,
|
|
PVOID subject,
|
|
PVOID object
|
|
)
|
|
{
|
|
if (pTestHookDict)
|
|
{
|
|
RPC_TEST_HOOK_FN fn = pTestHookDict->Find( id );
|
|
|
|
if (fn)
|
|
{
|
|
(*fn)(id, subject, object);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
RPC_TEST_HOOK_FN
|
|
GetTestHook(
|
|
RPC_TEST_HOOK_ID id
|
|
)
|
|
{
|
|
return pTestHookDict->Find( id );
|
|
}
|