Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

5062 lines
116 KiB

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
dgclnt.cxx
Abstract:
This is the client side of datagram rpc.
Author:
Dave Steckler (davidst) 15-Dec-1992
Revision History:
--*/
#include <precomp.hxx>
#include <conv.h>
#include <epmap.h>
#include <dgpkt.hxx>
#include <spseal.h>
#include <dgclnt.hxx>
#if defined(__RPC_WIN16__)
//
// For FP_SEG macro.
//
#include <dos.h>
#endif
//-------------------------------------------------------------------
//
// Dictionary of "pointers" to servers/endpoints.
//
MUTEX * AssociationListMutex;
NEW_SDICT(DG_CASSOCIATION);
static DG_CASSOCIATION_DICT * pAssociationDict;
#if defined(MULTITHREADED)
#define CLIENT_SCAVENGER_INTERVAL (1*60*1000)
#define CASSOCIATION_CACHE_LIMIT (5*60*1000)
#define CCALL_CACHE_TIME (2*60*1000)
DELAYED_ACTION_TABLE * DelayedActions;
DELAYED_ACTION_NODE * GlobalScavengerTimer;
DELAYED_ACTION_NODE * ClientScavengerTimer;
#endif // NTENV || WIN96
#if defined(__RPC_WIN16__)
//
// (Windows only) Dictionary of ENDPOINT_MANAGERS
//
DG_ENDPOINT_MANAGER_DICT * EpmDict;
#endif
static UUID CASUuid;
static int CASUuidInitialized = 0;
BOOL ClientGlobalsInitialized = FALSE;
BOOL
DoLazyAckingIfPossible(
DG_CCALL * Call
);
#ifdef MULTITHREADED
void
SendAckProc(
PDG_CCALL pCall
)
{
pCall->SendAck();
}
#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 (TRUE == ClientGlobalsInitialized)
{
return 0;
}
RequestGlobalMutex();
if (FALSE == ClientGlobalsInitialized)
{
Status = DG_PACKET::Initialize();
if (Status != RPC_S_OK)
{
goto abend;
}
#ifdef MULTITHREADED
DelayedActions = new DELAYED_ACTION_TABLE(&Status);
if (!DelayedActions)
{
Status = RPC_S_OUT_OF_MEMORY;
}
if (Status != RPC_S_OK)
{
goto abend;
}
GlobalScavengerTimer = new DELAYED_ACTION_NODE(GlobalScavengerProc, 0);
if (!GlobalScavengerTimer)
{
goto abend;
}
ClientScavengerTimer = new DELAYED_ACTION_NODE(DG_CASSOCIATION::ScavengerProc, 0);
if (!ClientScavengerTimer)
{
goto abend;
}
#endif
pAssociationDict = new DG_CASSOCIATION_DICT;
if (pAssociationDict == 0)
{
goto abend;
}
AssociationListMutex = new MUTEX(&Status);
if (!AssociationListMutex)
{
Status = RPC_S_OUT_OF_MEMORY;
}
if (Status != RPC_S_OK)
{
goto abend;
}
#ifdef WIN
//Create A Dictionary Of Datagram Transport Ep Managers
//So That on each rpc process exit, via brute force we can
//force Endpoints to be closed/released
EpmDict = new DG_ENDPOINT_MANAGER_DICT;
if (EpmDict == 0)
{
goto abend;
}
#endif
ClientGlobalsInitialized = TRUE;
}
ClearGlobalMutex();
return 0;
//--------------------------------------------------------------------
abend:
#ifdef WIN
delete EpmDict;
#endif
delete AssociationListMutex;
delete pAssociationDict;
#ifdef MULTITHREADED
if (ClientScavengerTimer)
{
delete ClientScavengerTimer;
ClientScavengerTimer = 0;
}
if (GlobalScavengerTimer)
{
delete GlobalScavengerTimer;
GlobalScavengerTimer = 0;
}
delete DelayedActions;
DelayedActions = 0;
#endif
ClearGlobalMutex();
return 1;
}
BINDING_HANDLE *
DgCreateBindingHandle (
void
)
/*++
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;
}
inline
void
DG_CASSOCIATION::DecrementRefCount(
void
)
/*++
Routine Description:
Decrements the ref count to an association. If the ref count hits zero,
the association is marked for deletion.
Arguments:
<none>
Return Value:
<none>
--*/
{
ASSERT(Magic == MAGIC_ASSOC);
#ifdef MULTITHREADED
//
// Note that expiration time mut be loaded before refcount goes to zero
// in order to avoid a race condition.
//
ExpirationTime = CurrentTimeInMsec();
if (0 == ReferenceCount.Decrement())
{
DelayedActions->Add(ClientScavengerTimer, CLIENT_SCAVENGER_INTERVAL, FALSE);
}
#else
//
// We don't cache associations in non-threaded environments.
//
if (0 == ReferenceCount.Decrement())
{
pAssociationDict->Delete(DictionaryKey);
delete this;
}
#endif
}
void PAPI *
DG_CASSOCIATION::UpdateAssociationWithAddress(
void PAPI * NewAddress
)
{
void PAPI * OldAddress;
RPC_CHAR * NewEndpoint;
RPC_STATUS Status;
ASSERT(Magic == MAGIC_ASSOC);
NewEndpoint = new RPC_CHAR[pTransport->EndpointStringSize];
if (NewEndpoint != 0)
{
Status = pTransport->GetEndpoint(NewAddress, NewEndpoint);
}
Mutex.Request();
OldAddress = (void PAPI *)
InterlockedExchange((LONG *)&ServerAddress, (LONG) NewAddress);
if (NewEndpoint != 0)
{
pDceBinding->AddEndpoint(NewEndpoint);
}
Mutex.Clear();
return (OldAddress);
}
DG_BINDING_HANDLE::DG_BINDING_HANDLE(
IN OUT RPC_STATUS PAPI * Status
)
: BindingHandleMutex(Status),
pCAssociation(0),
ReferenceCount(1),
pDceBinding(0)
/*++
Routine Description:
The constructor for DG_BINDING_HANDLE. This object represents a
binding to a server. Most of the main "pointers" to the server/endpoint
are set up in DG_BINDING_HANDLE::PrepareBindingHandle.
Arguments:
Status
Return Value:
None, this is a constructor.
--*/
{
}
DG_BINDING_HANDLE::~DG_BINDING_HANDLE()
/*++
Routine Description:
Destructor for the DG_BINDING_HANDLE. Let the CASSOCIATION know we aren't
using it anymore; this may cause it to be deleted.
Arguments:
<none>
Return Value:
<none>
--*/
{
if (pCAssociation != 0)
{
pCAssociation->DecrementRefCount();
}
delete pDceBinding;
}
inline void
DG_BINDING_HANDLE::DecrementReferenceCount(
void
)
{
//
// We aren't really excluding other threads from entering the critical
// section, just waiting until the other threads have left it.
//
BindingHandleMutex.Request();
ReferenceCount--;
if (ReferenceCount == 0)
{
//
// Don't try to clear mutex...it was deleted along
// this the binding handle.
//
delete this;
}
else
{
BindingHandleMutex.Clear();
}
}
RPC_STATUS
DG_BINDING_HANDLE::GetBuffer (
IN OUT PRPC_MESSAGE Message
)
/*++
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:
--*/
{
PDG_CCALL pCCall;
RPC_STATUS Status;
unsigned long AssociationFlag = 0;
CLIENT_AUTH_INFO * AuthInfo;
if (Message->RpcFlags & RPC_NCA_FLAGS_MAYBE)
{
Message->RpcFlags |= RPC_NCA_FLAGS_IDEMPOTENT;
}
else if (Message->RpcFlags & RPC_NCA_FLAGS_BROADCAST)
{
Message->RpcFlags |= RPC_NCA_FLAGS_IDEMPOTENT;
}
BindingHandleMutex.Request();
ASSERT(pDceBinding != 0);
//
// Have we already determined the association for this binding handle?
//
if (pCAssociation == 0)
{
//
// Check to see if we need to resolve this endpoint.
//
Status = pDceBinding->ResolveEndpointIfNecessary(
(RPC_CLIENT_INTERFACE __RPC_FAR *)Message->RpcInterfaceInformation,
InqPointerAtObjectUuid(),
InquireEpLookupHandle(),
TRUE, //UseEpMapper Ep If Necessary
InqComTimeout()
);
if ( (Status != RPC_S_OK) && (Status != RPC_P_EPMAPPER_EP) )
{
BindingHandleMutex.Clear();
return Status;
}
if (Message->RpcFlags & RPC_NCA_FLAGS_BROADCAST)
{
AssociationFlag |= BROADCAST;
}
//
// Is there an association already pointing at the same place?
//
if ( Status == RPC_P_EPMAPPER_EP )
{
RequestGlobalMutex();
pAssociationDict->Reset();
while ( (pCAssociation = pAssociationDict->Next()) != 0 )
{
//
// Don't pick up an association whose last call failed.
//
if (pCAssociation->ErrorFlag())
{
continue;
}
if (TRUE == pCAssociation->ComparePartialBinding(
this,
Message->RpcInterfaceInformation))
{
pCAssociation->IncrementRefCount();
break;
}
}
ClearGlobalMutex();
if (!pCAssociation)
{
AssociationFlag |= UNRESOLVEDEP;
}
}
else
{
RequestGlobalMutex();
pAssociationDict->Reset();
while ( (pCAssociation = pAssociationDict->Next()) != 0 )
{
//
// Don't pick up an association whose last call failed.
//
if (pCAssociation->ErrorFlag())
{
continue;
}
if (pCAssociation->CompareWithBinding(this) == 0)
{
pCAssociation->IncrementRefCount();
break;
}
}
ClearGlobalMutex();
}
if (!pCAssociation)
{
Status = RPC_S_OK;
ASSERT(pDceBinding);
DCE_BINDING * NewDceBinding = pDceBinding->DuplicateDceBinding();
if (!NewDceBinding)
{
pCAssociation = 0;
BindingHandleMutex.Clear();
return RPC_S_OUT_OF_MEMORY;
}
pCAssociation = new DG_CASSOCIATION(
pTransport,
this,
AssociationFlag,
NewDceBinding,
&Status
);
if (pCAssociation == 0)
{
Status = RPC_S_OUT_OF_MEMORY;
}
if (Status != RPC_S_OK)
{
delete pCAssociation;
pCAssociation = 0;
BindingHandleMutex.Clear();
return Status;
}
//
// Other threads using this binding handle for the same interface
// will reuse this association instead of creating a new one.
//
pCAssociation->AddInterface(Message->RpcInterfaceInformation, InqPointerAtObjectUuid());
}
}
#ifdef NTENV
AuthInfo = InquireAuthInformation();
if ( (AuthInfo != 0) && (AuthInfo->AuthenticationLevel !=
RPC_C_AUTHN_LEVEL_NONE) )
{
if (AuthInfo->IdentityTracking == RPC_C_QOS_IDENTITY_DYNAMIC)
{
Status = ReAcquireCredentialsIfNecessary();
if (Status != RPC_S_OK)
{
BindingHandleMutex.Clear();
return (Status);
}
}
}
#endif
//
// Get a DG_CCALL to play with.
//
Status = pCAssociation->AllocateCCall(&pCCall, InquireAuthInformation());
if (Status != RPC_S_OK)
{
ASSERT(pCCall == 0);
return Status;
}
ReferenceCount++;
BindingHandleMutex.Clear();
pCCall->Initialize(this);
//
// Finally we can allocate the buffer.
//
Status = pCCall->GetBuffer(Message);
if (Status != RPC_S_OK)
{
//
// This RPC call is no longer using the DG_CCALL. This may cause
// the DG_CCALL to be deleted. There is still a reference to the
// association, for the binding handle.
//
pCAssociation->FreeCall(pCCall);
//
// This RPC call is no longer using the binding handle. This may cause
// the binding handle to be deleted, which may cause the association
// to be deleted.
//
DecrementReferenceCount();
}
return Status;
}
RPC_STATUS
DG_BINDING_HANDLE::BindingFree (
)
/*++
Routine Description:
Implements RpcBindingFree for dg binding handles.
Arguments:
<none>
Return Value:
RPC_S_OK
--*/
{
//
// Decrement the ref count. If the count has hit zero, this call will
// delete this.
//
DecrementReferenceCount();
return RPC_S_OK;
}
void
DG_BINDING_HANDLE::PrepareBindingHandle (
IN void * 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.
Here we search for the appropriate DG_CASSOCIATION and, if found, then
point at it. Otherwise create a new association and insert it in the
association dictionary.
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);
pDceBinding = DceBinding;
pTransport = (PDG_RPC_CLIENT_TRANSPORT_INFO)TransportInterface;
pCAssociation = 0;
RPC_CHAR * Endpoint = pDceBinding->InqEndpoint();
if (!Endpoint || Endpoint[0] == 0)
{
fDynamicEndpoint = TRUE;
}
else
{
fDynamicEndpoint = FALSE;
}
if (DefaultMaxDatagramLength)
{
ParmMaxDatagramLength = min(DefaultMaxDatagramLength, pTransport->MaxPduSize);
}
else
{
ParmMaxDatagramLength = pTransport->PreferredPduSize;
}
ParmBufferLength = pTransport->DefaultBufferLength;
}
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>
--*/
{
if (pCAssociation == 0)
{
*StringBinding = pDceBinding->StringBindingCompose(
InqPointerAtObjectUuid()
);
if (*StringBinding == 0)
{
return RPC_S_OUT_OF_MEMORY;
}
return RPC_S_OK;
}
else
{
return pCAssociation->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>
--*/
{
if ( pCAssociation == 0 )
{
return pDceBinding->ResolveEndpointIfNecessary(
RpcClientInterface,
InqPointerAtObjectUuid(),
InquireEpLookupHandle(),
FALSE,
InqComTimeout()
);
}
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;
--*/
{
BindingHandleMutex.Request();
DisassociateFromServer();
if (pDceBinding)
{
pDceBinding->MakePartiallyBound();
if (0 != *InquireEpLookupHandle())
{
EpFreeLookupHandle(*InquireEpLookupHandle());
*InquireEpLookupHandle() = 0;
}
BindingHandleMutex.Clear();
return RPC_S_OK;
}
else
{
BindingHandleMutex.Clear();
return RPC_S_OUT_OF_MEMORY;
}
}
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
--*/
{
UNUSED(MaintainContext);
RPC_STATUS Status = RPC_S_OK;
PDG_BINDING_HANDLE Binding;
DCE_BINDING * pNewDceBinding;
if (pDceBinding != 0)
{
pNewDceBinding = pDceBinding->DuplicateDceBinding();
if (!pNewDceBinding)
{
*DestinationBinding = 0;
return RPC_S_OUT_OF_MEMORY;
}
}
else
{
ASSERT(pCAssociation);
pNewDceBinding = 0;
}
Binding = new DG_BINDING_HANDLE(&Status);
if ( Binding == 0 || Status != RPC_S_OK)
{
delete pNewDceBinding;
*DestinationBinding = 0;
return RPC_S_OUT_OF_MEMORY;
}
BindingHandleMutex.Request();
RPC_UUID Uuid;
InquireObjectUuid(&Uuid);
Binding->SetObjectUuid(&Uuid);
CLIENT_AUTH_INFO * AuthInfo;
if ((AuthInfo = InquireAuthInformation()) != 0)
{
RPC_STATUS RpcStatus;
RpcStatus = Binding->SetAuthInformation(
AuthInfo->ServerPrincipalName,
AuthInfo->AuthenticationLevel,
AuthInfo->AuthenticationService,
AuthInfo->AuthIdentity,
AuthInfo->AuthorizationService,
#ifndef NTENV
AuthInfo->Credentials
#else
AuthInfo->Credentials,
AuthInfo->ImpersonationType,
AuthInfo->IdentityTracking,
AuthInfo->Capabilities
#endif
);
if (RpcStatus != RPC_S_OK)
{
ASSERT (RpcStatus == RPC_S_OUT_OF_MEMORY);
BindingHandleMutex.Clear();
delete Binding;
Binding = 0;
return RpcStatus;
}
}
Binding->pTransport = pTransport;
Binding->pDceBinding = pNewDceBinding;
Binding->pCAssociation = pCAssociation;
Binding->ParmBufferLength = ParmBufferLength;
Binding->ParmMaxDatagramLength = ParmMaxDatagramLength;
if (pCAssociation != 0)
{
pCAssociation->IncrementRefCount();
}
BindingHandleMutex.Clear();
*DestinationBinding = (BINDING_HANDLE *) Binding;
return RPC_S_OK;
}
void
DG_BINDING_HANDLE::DisassociateFromServer(
)
{
PDG_CASSOCIATION Association;
BindingHandleMutex.Request();
Association = pCAssociation;
pCAssociation = 0;
//
// BUGBUG this frees memory while holding the mutex - gross.
// We should modify DCE_BINDING::MakePartiallyBound to return the old
// endpoint so we can delete it outside the mutex.
//
if (fDynamicEndpoint)
{
pDceBinding->MakePartiallyBound();
}
BindingHandleMutex.Clear();
if (Association != 0)
{
Association->DecrementRefCount();
}
}
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);
}
return(AuthenticationLevel);
}
DG_CASSOCIATION::DG_CASSOCIATION(
IN PDG_RPC_CLIENT_TRANSPORT_INFO MyTransport,
IN PDG_BINDING_HANDLE MyBinding,
IN unsigned long MyAssociationFlag,
IN DCE_BINDING * NewDceBinding,
OUT RPC_STATUS __RPC_FAR * pStatus
)
: DG_ASSOCIATION ( MyTransport->BaselinePduSize, pStatus),
pTransport ( MyTransport ),
AssociationFlag ( MyAssociationFlag ),
Magic ( MAGIC_ASSOC ),
EndpointManager ( 0 ),
ServerAddress ( 0 ),
ServerBootTime ( 0 ),
pDceBinding ( NewDceBinding ),
DictionaryKey ( -1 ),
fErrorFlag (FALSE)
/*++
Routine Description:
This is the constructor for DG_CASSOCIATION.
Notice that the object is initialized so that the destructor can be called
even if the constructor bails out early.
Arguments:
pTransport - Transport that this association is active on.
pDceBinding - DCE_BINDING that we are associated with
pStatus - A return status.
RPC_S_OK
RPC_S_OUT_OF_MEMORY
<return from construction of DG_CCALL>
<return from UuidCreate>
<return from DG_CLIENT_TRANSPORT::RegisterCall>
Return Value:
<none>, this is a constructor
--*/
{
if (*pStatus != RPC_S_OK)
{
return;
}
//
// BUGBUG pay attention to binding packet size and buffer length hints
//
//
// Initialize some data.
//
//
if (0 == (ServerAddress = RpcpFarAllocate(MyTransport->AddressSize)))
{
*pStatus = RPC_S_OUT_OF_MEMORY;
return;
}
RpcpMemorySet(ServerAddress, 0, MyTransport->AddressSize);
EndpointManager = (PDG_ENDPOINT_MANAGER) MyTransport->EndpointManager;
EndpointManager->IncrementReferenceCount();
*pStatus = MyTransport->RegisterCall(
this, // cassociation
MyBinding->pDceBinding->InqNetworkAddress(), // server
MyBinding->pDceBinding->InqEndpoint(), // endpoint
(void PAPI * PAPI *)&ServerAddress // output addr obj
);
if (*pStatus != RPC_S_OK)
{
ASSERT( (*pStatus == RPC_S_OUT_OF_MEMORY) ||
(*pStatus == RPC_S_DUPLICATE_ENDPOINT) ||
(*pStatus == RPC_S_SERVER_UNAVAILABLE)
);
//
// Delete it here, because the destructor would try to deregister it
// before deleting it.
//
//
RpcpFarFree(ServerAddress);
ServerAddress = 0;
return;
}
//
// Insert this association in the association dictionary.
//
RequestGlobalMutex();
DictionaryKey = pAssociationDict->Insert(this);
if (DictionaryKey == -1)
{
ClearGlobalMutex();
*pStatus = RPC_S_OUT_OF_MEMORY;
return;
}
ClearGlobalMutex();
}
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>
--*/
{
PDG_PACKET pPacket;
DG_CCALL * Call;
ASSERT(Magic == MAGIC_ASSOC);
Magic = 0;
//
// Delete All Calls For This Association..
//
InactiveCallsDict.Reset();
while ( (Call = InactiveCallsDict.Next()) != 0 )
{
InactiveCallsDict.Delete(Call->AssociationKey);
delete Call;
}
//
// If the trans address is not 0, then deregister it.
//
if (ServerAddress != 0)
{
(void)pTransport->DeregisterCall(ServerAddress);
RpcpFarFree(ServerAddress);
}
delete pDceBinding;
EndpointManager->DecrementReferenceCount();
}
RPC_STATUS
DG_CASSOCIATION::AllocateCCall(
PDG_CCALL __RPC_FAR * ppCCall,
CLIENT_AUTH_INFO * ClientAuthInfo
)
/*++
Routine Description:
Allocates a new DG_CCALL object on this association.
Arguments:
ppCCall - where to store the pointer to the new DG_CCALL
ClientAuthInfo - the binding's auth info
Return Value:
RPC_S_OK
RPC_S_OUT_OF_MEMORY
--*/
{
PDG_CCALL pReturn = 0;
RPC_STATUS Status = RPC_S_OK;
ASSERT(Magic == MAGIC_ASSOC);
*ppCCall = 0;
Mutex.Request();
if (0 == pReturn)
{
InactiveCallsDict.Reset();
while ( (pReturn = InactiveCallsDict.Next()) != 0 )
{
if (pReturn->IsSupportedAuthInfo(ClientAuthInfo) == FALSE)
{
continue;
}
InactiveCallsDict.Delete(pReturn->AssociationKey);
break;
}
}
Mutex.Clear();
if (0 == pReturn)
{
pReturn = new DG_CCALL(this, pTransport->AddressSize, ClientAuthInfo, &Status);
}
if (0 == pReturn)
{
Status = RPC_S_OUT_OF_MEMORY;
}
if (RPC_S_OK == Status)
{
IncrementRefCount();
*ppCCall = pReturn;
}
else
{
delete pReturn;
}
return Status;
}
void
DG_CASSOCIATION::FreeCall(
IN DG_CCALL * Call
)
{
ASSERT(Magic == MAGIC_ASSOC);
#ifdef MULTITHREADED
Call->ExpirationTime = CurrentTimeInMsec();
DelayedActions->Add(ClientScavengerTimer, CLIENT_SCAVENGER_INTERVAL, FALSE);
#endif
Mutex.Request();
int Key = Call->AssociationKey = InactiveCallsDict.Insert(Call);
Mutex.Clear();
if (-1 == Key)
{
delete Call;
}
DecrementRefCount();
}
RPC_STATUS
DG_CASSOCIATION::ToStringBinding (
OUT RPC_CHAR __RPC_FAR * __RPC_FAR * StringBinding,
IN RPC_UUID __RPC_FAR * 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
--*/
{
*StringBinding = pDceBinding->StringBindingCompose(ObjectUuid);
if (*StringBinding == 0)
{
return RPC_S_OUT_OF_MEMORY;
}
return RPC_S_OK;
}
BOOL
OptionalStringsEqual(
RPC_CHAR __RPC_FAR * String1,
RPC_CHAR __RPC_FAR * 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 __RPC_FAR * 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 __RPC_FAR * String1;
RPC_CHAR __RPC_FAR * String2;
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);
CLAIM_MUTEX Lock(Mutex);
return InterfaceAndObjectDict.Find(InterfaceInformation, &Object);
}
BOOL
DG_CASSOCIATION::AddInterface(
void __RPC_FAR * InterfaceInformation,
RPC_UUID __RPC_FAR * 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 __RPC_FAR * InterfaceInformation,
RPC_UUID __RPC_FAR * 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_CCALL::DG_CCALL(
IN PDG_CASSOCIATION MyAssociation,
unsigned AddressSize,
CLIENT_AUTH_INFO * myAuthInfo,
OUT RPC_STATUS __RPC_FAR * pStatus
)
: pCAssociation(MyAssociation),
AssociationKey(-1),
CallbackAddress(0),
EndpointHandle(0),
CallbackCompleted(FALSE),
InsideCallback(FALSE),
UseSecurity(FALSE),
ActiveSecurityContext(0),
PreviousSecurityContext(0),
AckPending(FALSE),
DG_PACKET_ENGINE(MyAssociation->CurrentPduSize,
MyAssociation->pTransport->PreferredPduSize,
MyAssociation->pTransport->MaxPacketSize,
MyAssociation->RemoteWindowSize,
MyAssociation->pTransport->DefaultBufferLength,
pStatus
),
#pragma warning(disable:4355)
#ifdef MULTITHREADED
DelayedAckTimer((DELAYED_ACTION_FN) SendAckProc, this),
#endif
#pragma warning(default:4355)
CCONNECTION(myAuthInfo, pStatus),
ServerResponded(FALSE)
/*++
Routine Description:
This is the constructor for the DG_CCALL class. This object represents
a call being made from the client to the server. There can be many
DG_CCALLs active on a single association.
Arguments:
pCAssociation - pointer to the association that this call is being
made on.
pStatus - A return status.
Always RPC_S_OK
Return Value:
<none>, this is a constructor
--*/
{
CancelEventId = 1;
if (*pStatus)
{
return;
}
*pStatus = UuidCreate((UUID __RPC_FAR *)&ActivityUuid);
if (*pStatus == RPC_S_UUID_LOCAL_ONLY)
{
*pStatus = RPC_S_OK;
}
if (*pStatus)
{
return;
}
pSavedPacket->Header.ActivityId = ActivityUuid;
//
// Allocate a buffer for the source address of incoming packets.
// UDP addresses include a boolean that needs to be initialized to FALSE,
// so clear the buffer here. Think about the usage of UpdateServerAddress
// and DG_UDP_ADDRESS.ServerLookupFailed for the full story.
//
CallbackAddress = RpcpFarAllocate(AddressSize);
if (0 == CallbackAddress)
{
*pStatus = RPC_S_OUT_OF_MEMORY;
return;
}
RpcpMemorySet(CallbackAddress, 0, AddressSize);
if (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE)
{
UseSecurity = TRUE;
*pStatus = InitializeSecurityContext();
if (*pStatus == RPC_P_CONTINUE_NEEDED ||
*pStatus == RPC_P_COMPLETE_NEEDED ||
*pStatus == RPC_P_COMPLETE_AND_CONTINUE)
{
//
// BUGBUG set bits if necessary.
//
*pStatus = RPC_S_OK;
}
}
}
DG_CCALL::~DG_CCALL()
/*++
Routine Description:
Destructor for DG_CCALL. Does nothing at present.
Arguments:
<none>
Return Value:
none
--*/
{
#ifdef MULTITHREADED
if (TRUE == DelayedActions->Cancel(&DelayedAckTimer))
{
SendAck();
AckPending = FALSE;
}
#endif
if (CallbackAddress)
{
RpcpFarFree(CallbackAddress);
}
delete PreviousSecurityContext;
#ifdef MULTITHREADED
//
// If we cancelled the timer while the timer proc was running, we need
// to wait for the proc to terminate. SendAck() uses pSavedPacket, so
// it would crash if the DG_CCALL is freed before it completes.
//
while (AckPending)
{
Sleep(0);
}
#endif
delete ActiveSecurityContext;
FreeEndpoint();
}
RPC_STATUS
DG_CCALL::GetBuffer(
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
This method is called to actually allocate memory for an rpc call.
Arguments:
Message - The RPC_MESSAGE structure associated with this call.
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;
//
// For small buffers, it is fastest to take a packet from the cache.
//
if (Message->BufferLength <= MaxFragmentSize)
{
pPacket = AllocatePacket();
}
else
{
pPacket = DG_PACKET::AllocatePacket(
sizeof(NCA_PACKET_HEADER)
+ Align8(Message->BufferLength)
+ SecurityTrailerSize);
}
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_CCALL::FreeBuffer(
IN OUT PRPC_MESSAGE pMessage
)
/*++
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>
--*/
{
FreeInParms(pMessage);
if (FALSE == InsideCallback)
{
PDG_BINDING_HANDLE Handle = pBindingHandle;
//
// This RPC call is no longer using the DG_CCALL. This may cause
// the DG_CCALL to be deleted.
//
pCAssociation->FreeCall(this);
//
// This RPC call is no longer using the binding handle. This may cause
// the binding handle and the association to be deleted.
//
Handle->DecrementReferenceCount();
}
}
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:
none
--*/
{
FreeInParms(Message);
}
void
DG_CCALL::FreeInParms(
IN OUT PRPC_MESSAGE pMessage
)
/*++
Routine Description:
Free the call's [in] parameters.
Arguments:
pMessage - The RPC_MESSAGE structure associated with this call.
Return Value:
<none>
--*/
{
if (pMessage->Buffer)
{
PDG_PACKET Packet = DG_PACKET::ContainingRecord(pMessage->Buffer);
if (Packet->MaxDataLength == MaxPduSize)
{
FreePacket(Packet);
}
else
{
delete Packet;
}
pMessage->Buffer = 0;
}
}
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
--*/
{
if (Message->Buffer == LastReceiveBuffer &&
NewSize <= LastReceiveBufferLength)
{
Message->BufferLength = NewSize;
return RPC_S_OK;
}
RPC_STATUS Status;
RPC_MESSAGE NewMessage;
NewMessage.BufferLength = NewSize;
Status = GetBuffer(&NewMessage);
if (RPC_S_OK != Status)
{
return Status;
}
LastReceiveBuffer = NewMessage.Buffer;
LastReceiveBufferLength = NewMessage.BufferLength;
if (Message->BufferLength)
{
RpcpMemoryCopy(NewMessage.Buffer,
Message->Buffer,
Message->BufferLength
);
}
FreeInParms(Message);
Message->Buffer = NewMessage.Buffer;
Message->BufferLength = NewMessage.BufferLength;
return RPC_S_OK;
}
void
DG_CCALL::FreeEndpoint(
)
/*++
Routine Description:
The fn removes the assoication between <this> and the transport endpoint.
Return Value:
none
--*/
{
#ifdef MULTITHREADED
PENDPOINT Ep = (PENDPOINT) InterlockedExchange((PLONG) &EndpointHandle, 0);
#else
PENDPOINT Ep = (PENDPOINT) EndpointHandle;
EndpointHandle = 0;
#endif
if (Ep)
{
pCAssociation->EndpointManager->FreeEndpoint(Ep);
}
}
void
DG_CCALL::BuildNcaPacketHeader(
OUT PNCA_PACKET_HEADER pNcaPacketHeader,
IN OUT 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>
--*/
{
pNcaPacketHeader->PacketFlags = 0;
pNcaPacketHeader->PacketFlags2 = 0;
PRPC_CLIENT_INTERFACE pCli =
(PRPC_CLIENT_INTERFACE)(Message->RpcInterfaceInformation);
RPC_UUID __RPC_FAR *pUuid = (RPC_UUID __RPC_FAR *)
(&(pCli->InterfaceId.SyntaxGUID));
pNcaPacketHeader->InterfaceId = *pUuid;
pNcaPacketHeader->ObjectId = *(pBindingHandle->InqPointerAtObjectUuid());
pNcaPacketHeader->InterfaceVersion.MajorVersion =
pCli->InterfaceId.SyntaxVersion.MajorVersion;
pNcaPacketHeader->InterfaceVersion.MinorVersion =
pCli->InterfaceId.SyntaxVersion.MinorVersion;
pNcaPacketHeader->SequenceNumber = SequenceNumber;
pNcaPacketHeader->OperationNumber = Message->ProcNum;
pNcaPacketHeader->ServerBootTime = pCAssociation->ServerBootTime;
}
inline void
DG_CCALL::Initialize(
PDG_BINDING_HANDLE Binding
)
{
pBindingHandle = Binding;
#ifdef MULTITHREADED
//
// We are about to RPC to the server..no need to send a lazy ack.
//
if (TRUE == DelayedActions->Cancel(&DelayedAckTimer))
{
AckPending = FALSE;
}
//
// If we cancelled the timer while the timer proc was running, we need
// to wait for the proc to terminate.
//
while (AckPending)
{
Sleep(0);
}
#endif
}
void
DG_CCALL::SendAck(
)
{
pSavedPacket->Header.PacketType = DG_ACK;
pSavedPacket->Header.PacketBodyLen = 0;
SealAndSendPacket(&pSavedPacket->Header);
FreeEndpoint();
AckPending = FALSE;
}
inline RPC_STATUS
DG_CCALL::SendPing(
)
{
pSavedPacket->Header.PacketType = DG_PING;
pSavedPacket->Header.PacketFlags |= DG_PF_IDEMPOTENT;
pSavedPacket->Header.PacketBodyLen = 0;
AddSerialNumber(&pSavedPacket->Header);
RPC_STATUS Status = SealAndSendPacket(&pSavedPacket->Header);
++SendSerialNumber;
return Status;
}
void __RPC_FAR
conv_are_you_there(
handle_t Binding,
UUID __RPC_FAR *pUuid,
unsigned long ServerBootTime,
error_status_t __RPC_FAR *Status
)
{
PDG_CCALL pCall = (PDG_CCALL) Binding;
RPC_UUID __RPC_FAR * pRpcUuid=(RPC_UUID __RPC_FAR *)pUuid;
//
// See if this activity id has a call in progress.
//
if (pCall->ActivityUuid.MatchUuid(pRpcUuid) != 0)
{
//
// This is a really stale packet.
//
*Status = NCA_STATUS_BAD_ACTID;
return;
}
*Status = RPC_S_OK;
if (pCall->pCAssociation->ServerBootTime == 0)
{
//
// the server is responding to our first call.
//
pCall->pCAssociation->ServerBootTime = ServerBootTime;
pCall->pSavedPacket->Header.ServerBootTime = ServerBootTime;
}
else if (pCall->pCAssociation->ServerBootTime != ServerBootTime)
{
//
// The server crashed.
//
*Status = NCA_STATUS_YOU_CRASHED;
}
}
void __RPC_FAR
conv_who_are_you(
IN handle_t Binding,
IN UUID __RPC_FAR * pUuid,
IN unsigned long ServerBootTime,
OUT unsigned long __RPC_FAR * SequenceNumber,
OUT error_status_t __RPC_FAR * 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, DG_YOU_CRASHED if we think the
server crashed.
Return Value:
<none>
--*/
{
conv_are_you_there(Binding, pUuid, ServerBootTime, Status);
if (*Status != 0)
{
return;
}
PDG_CCALL pCall = (PDG_CCALL) Binding;
*SequenceNumber = pCall->SequenceNumber;
pCall->CallbackCompleted = TRUE;
}
void __RPC_FAR
conv_who_are_you2(
IN handle_t Binding,
IN UUID __RPC_FAR * pUuid,
IN unsigned long ServerBootTime,
OUT unsigned long __RPC_FAR * SequenceNumber,
OUT UUID __RPC_FAR * pCASUuid,
OUT error_status_t __RPC_FAR * 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>
--*/
{
conv_who_are_you(Binding, pUuid,ServerBootTime,SequenceNumber,Status);
if (CASUuidInitialized == 0)
{
RequestGlobalMutex();
if (CASUuidInitialized == 0)
{
UuidCreate(&CASUuid);
CASUuidInitialized = 1;
}
ClearGlobalMutex();
}
RpcpMemoryCopy(pCASUuid, &CASUuid, sizeof(UUID));
}
void __RPC_FAR
conv_who_are_you_auth(
handle_t Binding,
UUID __RPC_FAR *pUuid,
unsigned long ServerBootTime,
byte __RPC_FAR * InData,
long InLength,
long OutMaxLength,
unsigned long __RPC_FAR *SequenceNumber,
UUID __RPC_FAR * pCASUuid,
byte __RPC_FAR * OutData,
long __RPC_FAR * pOutLength,
error_status_t __RPC_FAR *Status
)
/*++
Routine Description:
Arguments:
Return Value:
Note:
InData and OutData must be pointers rather than arrays, because
if they are arrays Visual C++ 1.5 will treat them as 16-bits even if they
have the __RPC_FAR attribute! This is (expletive).
--*/
{
PDG_CCALL pCall = (PDG_CCALL) Binding;
conv_who_are_you2(Binding, pUuid,ServerBootTime,SequenceNumber,pCASUuid,Status);
if (*Status != 0)
{
*pOutLength = 0;
return;
}
*Status = MapToNcaStatusCode(
pCall->DealWithAuthCallback(
InData,
InLength,
OutData,
OutMaxLength,
pOutLength
)
);
}
DG_ENDPOINT_MANAGER::DG_ENDPOINT_MANAGER (
IN PDG_RPC_CLIENT_TRANSPORT_INFO Transport,
IN OUT RPC_STATUS PAPI * RpcStatus
) : EndpointManagerMutex(RpcStatus)
{
ReferenceCount = 0;
ClientTransport = Transport;
TransportEndpointSize = Transport->EndpointSize;
}
PENDPOINT
DG_ENDPOINT_MANAGER::AllocateEndpoint(
)
{
ENDPOINT * Endpoint;
RPC_STATUS Status;
EndpointManagerMutex.Request();
EndpointDictionary.Reset();
while ((Endpoint = EndpointDictionary.Next()) != 0)
{
if (Endpoint->Flag == INUSE)
{
continue;
}
#ifdef WIN
if (Endpoint->TaskId != GetCurrentTask())
{
continue;
}
#endif
Endpoint->Flag = INUSE;
EndpointManagerMutex.Clear();
return(Endpoint);
}
EndpointManagerMutex.Clear();
//All endpoints are in use ..Ask Transport to assign us one..
Endpoint = (ENDPOINT *) new char[ sizeof(ENDPOINT) + TransportEndpointSize ];
if (Endpoint != 0)
{
Endpoint->TransportEndpoint = (void __RPC_FAR *)(((char *)Endpoint)
+ sizeof(ENDPOINT));
Status = ClientTransport->AssignEndpoint(Endpoint->TransportEndpoint);
if (Status == RPC_S_OK)
{
Endpoint->Flag = INUSE;
}
#ifdef WIN
Endpoint->TaskId = GetCurrentTask();
#endif
}
if ((Endpoint == 0) || (Status != RPC_S_OK))
{
delete Endpoint;
return (0);
}
RequestGlobalMutex();
EndpointManagerMutex.Request();
if ( (Endpoint->DictKey = (EndpointDictionary.Insert(Endpoint))) == -1)
{
ClientTransport->FreeEndpoint(Endpoint->TransportEndpoint);
delete Endpoint;
Endpoint = 0;
}
EndpointManagerMutex.Clear();
ClearGlobalMutex();
return(Endpoint);
}
inline
void
DG_ENDPOINT_MANAGER::FreeEndpoint(
PENDPOINT Endpoint
)
{
Endpoint->Flag = NOTINUSE;
}
void
DG_ENDPOINT_MANAGER::IncrementReferenceCount (
void
)
{
EndpointManagerMutex.Request();
ReferenceCount++;
EndpointManagerMutex.Clear();
}
void
DG_ENDPOINT_MANAGER::DecrementReferenceCount(
void
)
{
ENDPOINT * Endpoint;
#if DBG
RequestGlobalMutex();
#endif
EndpointManagerMutex.Request();
ASSERT(ReferenceCount > 0);
ReferenceCount--;
//If the reference count is 0
//no active associations reference this EndpointManager,
//hence stop caching the the endpoints i.e. free them
if ( ReferenceCount == 0 )
{
EndpointDictionary.Reset();
while ((Endpoint = EndpointDictionary.Next()) != 0)
{
ClientTransport->FreeEndpoint(Endpoint->TransportEndpoint);
EndpointDictionary.Delete(Endpoint->DictKey);
delete Endpoint;
}
}
EndpointManagerMutex.Clear();
#if DBG
ClearGlobalMutex();
#endif
}
#ifdef WIN
void
DG_ENDPOINT_MANAGER::CleanupForThisTask(
void
)
{
unsigned int Task = GetCurrentTask();
ENDPOINT * Endpoint;
unsigned int ThisTaskUsedDg = 0;
EndpointManagerMutex.Request();
EndpointDictionary.Reset();
while ((Endpoint = EndpointDictionary.Next()) != 0)
{
if (Endpoint->TaskId == Task)
{
ClientTransport->FreeEndpoint(Endpoint->TransportEndpoint);
EndpointDictionary.Delete(Endpoint->DictKey);
delete Endpoint;
ThisTaskUsedDg = 1;
}
}
/*
Dont Do This.. Rpc Cannot Reload transports..
Do all Transport Specific Cleanups in DLLTermination!
//Do perprocess cleanup if necessary..
if ( (ThisTaskUsedDg != 0) && (ClientTransport->TransportUnload != 0) )
{
ClientTransport->TransportUnload();
}
*/
EndpointManagerMutex.Clear();
}
void
CleanupDgTransports(
)
{
PDG_ENDPOINT_MANAGER Epm;
EpmDict->Reset();
while ( (Epm = EpmDict->Next()) )
{
Epm->CleanupForThisTask();
}
}
#endif
RPC_STATUS
DG_CCALL::BeforeSendReceive(
PRPC_MESSAGE Message
)
{
RPC_STATUS Status;
#ifdef WIN
// Verify that a there isn't a call already in progress
Status = I_RpcWinCallInProgress();
if (Status != RPC_S_OK)
{
VALIDATE((Status, RPC_S_CALL_IN_PROGRESS, RPC_S_OUT_OF_MEMORY, 0));
return(Status);
}
#endif
//Get An Endpoint Assigned
if (EndpointHandle == 0)
{
EndpointHandle = pCAssociation->EndpointManager->AllocateEndpoint();
if (EndpointHandle == 0)
{
return (RPC_S_OUT_OF_MEMORY);
}
//
// BUGBUG re-enable when multiple packet sizes are implemented.
//
// Status = pCAssociation->pTransport->SetBufferLength(
// EndpointHandle->TransportEndpoint,
// TransportBufferLength
// );
// if (Status != RPC_S_OK)
// {
// return Status;
// }
Endpoint = EndpointHandle->TransportEndpoint;
}
#ifdef NTENV
//
// Let the world know that this thread is making a call.
//
if (RpcpThreadTestCancel())
{
return RPC_S_CALL_CANCELLED;
}
CancelPending = FALSE;
Status = RegisterForCancels(this);
if (Status != RPC_S_OK)
{
return Status;
}
#endif
#ifdef DOS
Status = pCAssociation->pTransport->BeginCall(Endpoint, this);
if (Status != RPC_S_OK)
{
return Status;
}
#endif
SendWindowSize = pCAssociation->RemoteWindowSize;
NextCallPduSize = pCAssociation->CurrentPduSize;
UnansweredRequestCount = 0;
WorkingCount = 0;
Timeout = 1;
//
// If the interface has changed from the previous call, then the i/f hint
// is invalid.
//
PRPC_CLIENT_INTERFACE pCli =
(PRPC_CLIENT_INTERFACE) (Message->RpcInterfaceInformation);
RPC_UUID __RPC_FAR *pUuid = (RPC_UUID __RPC_FAR *)
(&(pCli->InterfaceId.SyntaxGUID));
if (0 != RpcpMemoryCompare(&pSavedPacket->Header.InterfaceId, pUuid, sizeof(UUID)))
{
pSavedPacket->Header.InterfaceHint = 0xffff;
}
//
// Fill in common fields of the send packet.
//
BuildNcaPacketHeader(&pSavedPacket->Header, Message);
pSavedPacket->Header.PacketType = DG_REQUEST;
//
// Reset the DG_PACKET_ENGINE.
//
NewCall();
fNewCall = FALSE;
AllArgsSent = FALSE;
StaticArgsSent = FALSE;
return RPC_S_OK;
}
RPC_STATUS
DG_CCALL::AfterSendReceive(
PRPC_MESSAGE Message,
RPC_STATUS Status
)
{
#ifdef MULTITHREADED
if (pReceivedPackets && pReceivedPackets->Header.PacketFlags2 & DG_PF_CANCEL_PENDING)
{
RpcpThreadCancel(GetCurrentThread());
}
#endif
if (RPC_S_OK == Status)
{
if (0 == (Message->RpcFlags & RPC_NCA_FLAGS_MAYBE))
{
pCAssociation->ClearErrorFlag();
}
//
// Record that this interface is valid for this DG_CCALL.
//
pCAssociation->AddInterface(Message->RpcInterfaceInformation, &pSavedPacket->Header.ObjectId);
//
// Post a lazy ack request or send an ack packet, as appropriate.
// But don't ack a single-fragment idempotent response.
//
if (0 == (BufferFlags & RPC_NCA_FLAGS_IDEMPOTENT) || Message->BufferLength > MaxFragmentSize)
{
pSavedPacket->Header.FragmentNumber = ReceiveFragmentBase;
SetSerialNumber(&pSavedPacket->Header, SendSerialNumber);
#ifdef MULTITHREADED
AckPending = TRUE;
DelayedActions->Add(&DelayedAckTimer, TWO_SECS_IN_MSEC, TRUE);
#else
SendAck();
#endif
}
else
{
FreeEndpoint();
}
}
else
{
//
// 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_ABORT_CALL == Status ||
RPC_P_SEND_FAILED == Status ||
RPC_P_RECEIVE_FAILED == Status ||
RPC_P_TIMEOUT == Status )
{
if (CallbackCompleted &&
!(BufferFlags & RPC_NCA_FLAGS_IDEMPOTENT) &&
StaticArgsSent)
{
Status = RPC_S_CALL_FAILED;
}
else if (ServerResponded)
{
Status = RPC_S_CALL_FAILED_DNE;
}
else
{
Status = RPC_S_SERVER_UNAVAILABLE;
}
}
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_PROTOCOL_ERROR == Status
)
{
pCAssociation->SetErrorFlag();
pBindingHandle->DisassociateFromServer();
}
pCAssociation->RemoveInterface(Message->RpcInterfaceInformation, &pSavedPacket->Header.ObjectId);
}
#ifdef DOS
pCAssociation->pTransport->EndCall(Endpoint);
#endif
#ifdef NTENV
EVAL_AND_ASSERT(RPC_S_OK == UnregisterForCancels());
#endif
++SequenceNumber;
fNewCall = TRUE;
if (Status != RPC_S_OK)
{
PDG_BINDING_HANDLE Handle = pBindingHandle;
//
// This may cause the association to be deleted.
// This RPC call is no longer using the DG_CCALL.
//
pCAssociation->FreeCall(this);
//
// This RPC call is no longer using the binding handle. This may cause
// the binding handle to be deleted.
//
Handle->DecrementReferenceCount();
}
return Status;
}
RPC_STATUS
DG_CCALL::SendReceive(
IN OUT PRPC_MESSAGE Message
)
{
return SendReceiveRecur(Message, TRUE);
}
RPC_STATUS
DG_CCALL::SendReceiveRecur(
IN OUT PRPC_MESSAGE Message,
boolean AllowRetry
)
{
RPC_STATUS Status;
Status = BeforeSendReceive(Message);
if (Message->RpcFlags & RPC_NCA_FLAGS_BROADCAST)
{
if (Message->BufferLength > CurrentPduSize)
{
Status = RPC_S_SERVER_UNAVAILABLE;
}
}
if (RPC_S_OK != Status)
{
FreeInParms(Message);
PDG_BINDING_HANDLE Handle = pBindingHandle;
//
// This RPC call is no longer using the DG_CCALL. This may cause
// the DG_CCALL and the association to be deleted.
//
pCAssociation->FreeCall(this);
//
// This RPC call is no longer using the binding handle. This may cause
// the binding handle to be deleted.
//
Handle->DecrementReferenceCount();
return Status;
}
//
// [maybe] and [maybe, broadcast] calls.
//
if (Message->RpcFlags & RPC_NCA_FLAGS_MAYBE)
{
Status = MaybeSendReceive(Message);
//
// Depending upon circumstances, AfterSendReceive() may cause the ccall,
// the association, and/or the binding handle to be freed.
//
return AfterSendReceive(Message, Status);
}
//
// non-maybe calls.
//
Status = SetupSendWindow(Message);
ASSERT(Status == RPC_S_OK);
do
{
Status = SingleSendReceive();
}
while (RPC_S_OK == Status && FALSE == fReceivedAllFragments);
//
// If we can silently retry w/o violating protocol, do so.
//
if (AllowRetry)
{
if (Status == NCA_STATUS_WRONG_BOOT_TIME &&
(!fRetransmitted || (0 == (BufferFlags & RPC_NCA_FLAGS_IDEMPOTENT))))
{
goto retry_call;
}
}
//
// This is the path for successful calls and errors that won't
// benefit from a retry.
//
FreeInParms(Message);
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.
//
return AfterSendReceive(Message, Status);
retry_call:
//
// First we should disconnect the binding handle from the failing
// association, and clean up the old call. This will not delete
// the [in] buffer, but may delete <this>.
//
PDG_BINDING_HANDLE Handle = pBindingHandle;
AfterSendReceive(Message, RPC_S_CALL_FAILED_DNE);
//
// Now let's acquire a new buffer and association.
//
RPC_MESSAGE NewMessage = *Message;
Status = Handle->GetBuffer(&NewMessage);
if (RPC_S_OK != Status)
{
//
// Clean up after the original call attempt.
// Depending upon circumstances, AfterSendReceive() may cause the ccall,
// the association, and/or the binding handle to be freed.
//
FreeInParms(Message);
return RPC_S_CALL_FAILED_DNE;
}
RpcpMemoryCopy(NewMessage.Buffer, Message->Buffer, Message->BufferLength);
FreeInParms(Message);
//
// Attempt the call again using the new association.
//
*Message = NewMessage;
PDG_CCALL Call = (PDG_CCALL) Message->Handle;
return Call->SendReceiveRecur(Message, TRUE);
}
RPC_STATUS
DG_CCALL::Send(
PRPC_MESSAGE Message
)
{
return SendRecur(Message, TRUE);
}
RPC_STATUS
DG_CCALL::SendRecur(
PRPC_MESSAGE Message,
boolean AllowRetry
)
{
boolean FirstSend = fNewCall;
RPC_STATUS Status = RPC_S_OK;
if (FirstSend)
{
Status = BeforeSendReceive(Message);
if (RPC_S_OK != Status)
{
FreeInParms(Message);
PDG_BINDING_HANDLE Handle = pBindingHandle;
//
// This RPC call is no longer using the DG_CCALL.
// This may cause the association to be deleted.
//
pCAssociation->FreeCall(this);
//
// This RPC call is no longer using the binding handle. This may cause
// the binding handle to be deleted.
//
Handle->DecrementReferenceCount();
return Status;
}
}
Status = SetupSendWindow(Message);
if (Status)
{
ASSERT(Status == RPC_S_SEND_INCOMPLETE);
return Status;
}
unsigned FractionalPacket = Message->BufferLength - BufferLength;
while (RPC_S_OK == Status && !IsBufferAcknowledged())
{
ASSERT(SendWindowBase <= FirstUnsentFragment);
Status = SingleSendReceive();
}
if (FirstSend && AllowRetry)
{
if (Status == NCA_STATUS_WRONG_BOOT_TIME &&
(!fRetransmitted || (0 == (BufferFlags & RPC_NCA_FLAGS_IDEMPOTENT))))
{
goto retry_call;
}
}
if (Status == RPC_S_OK && FractionalPacket)
{
ASSERT(Buffer == Message->Buffer);
char __RPC_FAR * Temp = (char __RPC_FAR *) Buffer;
Message->BufferLength -= FirstUnsentOffset;
RpcpMemoryMove(Message->Buffer, Temp + FirstUnsentOffset, Message->BufferLength);
Status = RPC_S_SEND_INCOMPLETE;
}
ASSERT(SendWindowBase <= FirstUnsentFragment);
if (RPC_S_OK != Status &&
RPC_S_SEND_INCOMPLETE != Status )
{
FreeInParms(Message);
Status = AfterSendReceive(Message, Status);
}
return Status;
retry_call:
//
// First we should disconnect the binding handle from the failing
// association.
//
pCAssociation->SetErrorFlag();
pBindingHandle->DisassociateFromServer();
//
// Now let's acquire a new buffer and association.
//
RPC_MESSAGE NewMessage = *Message;
Status = pBindingHandle->GetBuffer(&NewMessage);
if (RPC_S_OK != Status)
{
//
// Clean up after the original call attempt.
// Depending upon circumstances, AfterSendReceive() may cause the ccall,
// the association, and/or the binding handle to be freed.
//
FreeInParms(Message);
return AfterSendReceive(Message, RPC_S_CALL_FAILED_DNE);
}
RpcpMemoryCopy(NewMessage.Buffer, Message->Buffer, Message->BufferLength);
//
// Clean up after the original call attempt.
// AfterSendReceive() may cause the ccall to be freed. The binding
// handle and the association have a ref from the call to GetBuffer
// so they are safe.
//
FreeInParms(Message);
AfterSendReceive(Message, RPC_S_CALL_FAILED_DNE);
//
// Make the new call.
//
*Message = NewMessage;
PDG_CCALL Call = (PDG_CCALL) Message->Handle;
return Call->SendRecur(Message, FALSE);
}
RPC_STATUS
DG_CCALL::MaybeSendReceive(
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
Sends a [maybe] or [broadcast, maybe] call.
Arguments:
Message - Message to be sent.
Return Value:
RPC_S_OK
<error from Transport>
--*/
{
//
// Make sure this fits into a single packet.
//
if (Message->BufferLength > MaxFragmentSize)
{
return RPC_S_OK;
}
//
// [maybe] calls are implicitly idempotent.
//
Message->RpcFlags |= RPC_NCA_FLAGS_IDEMPOTENT;
//
// Build the request packet.
//
PDG_PACKET Packet = DG_PACKET::ContainingRecord(Message->Buffer);
PNCA_PACKET_HEADER Header = &Packet->Header;
*Header = pSavedPacket->Header;
BuildNcaPacketHeader(Header, Message);
Header->PacketType = DG_REQUEST;
Header->PacketFlags = RpcToPacketFlagsArray[Message->RpcFlags & RPC_NCA_PACKET_FLAGS];
Header->PacketBodyLen = Message->BufferLength;
Header->FragmentNumber = 0;
Header->AuthProto = 0;
Header->ServerBootTime = 0;
AddSerialNumber(Header);
//
// Send the packet.
//
return pCAssociation->pTransport->Send(
Endpoint,
Header,
sizeof(NCA_PACKET_HEADER) + Header->PacketBodyLen,
Header->PacketFlags & DG_PF_BROADCAST ? TRUE : FALSE,
pCAssociation->ServerAddress
);
}
RPC_STATUS
DG_CCALL::Receive(
PRPC_MESSAGE Message,
unsigned MinimumSize
)
{
RPC_STATUS Status;
Buffer = 0;
do
{
ASSERT(SendWindowBase <= FirstUnsentFragment);
Status = SingleSendReceive();
if (fReceivedAllFragments)
{
break;
}
if (ConsecutiveDataBytes >= MinimumSize &&
(Message->RpcFlags & RPC_BUFFER_PARTIAL))
{
break;
}
}
while (RPC_S_OK == Status);
if (RPC_S_OK == Status)
{
Status = AssembleBufferFromPackets(Message, this);
}
ASSERT(SendWindowBase <= FirstUnsentFragment);
if (0 == (Message->RpcFlags & RPC_BUFFER_PARTIAL) ||
(Message->RpcFlags & RPC_BUFFER_COMPLETE) ||
RPC_S_OK != Status )
{
Status = AfterSendReceive(Message, Status);
}
return Status;
}
RPC_STATUS
DG_CCALL::SingleSendReceive(
)
/*++
Routine Description:
This fn runs one step of the general send something...receive something
loop that is common to SendReceive(), Send(), and Receive().
Arguments:
none
Return Value:
result of the call
--*/
{
RPC_STATUS Status;
PDG_PACKET pPacket;
PNCA_PACKET_HEADER pHeader;
Status = SendSomething();
if (RPC_S_SEND_INCOMPLETE == Status)
{
return Status;
}
if (Status != RPC_S_OK)
{
return Status;
}
//
// Look for a reply packet.
//
pPacket = AllocatePacket();
if (!pPacket)
{
return RPC_S_OUT_OF_MEMORY;
}
pHeader = &pPacket->Header;
ASSERT(pPacket->MaxDataLength >= MaxPduSize);
get_packet:
unsigned long LongLength = MaxPduSize;
Status = pCAssociation->pTransport->ReceivePacket(
Endpoint,
pHeader,
&LongLength,
Timeout,
CallbackAddress
);
pPacket->DataLength = (unsigned) LongLength;
if (Status == RPC_P_OVERSIZE_PACKET)
{
#ifdef DEBUGRPC
PrintToDebugger("RPC DG: packet is too large\n");
#endif
pPacket->Header.PacketFlags |= DG_PF_OVERSIZE_PACKET;
Status = RPC_S_OK;
}
else
{
ASSERT( pPacket->DataLength <= pPacket->MaxDataLength );
}
if (Status == RPC_S_OK)
{
TimeoutCount = 0;
if (pPacket->Header.RpcVersion != DG_RPC_PROTOCOL_VERSION)
{
goto get_packet;
}
ByteSwapPacketHeaderIfNecessary(pPacket);
if (0 == (pPacket->Header.PacketFlags & DG_PF_OVERSIZE_PACKET))
{
//
// Check for inconsistent packet length fields.
//
if (pPacket->DataLength < sizeof(NCA_PACKET_HEADER) ||
pPacket->DataLength - sizeof(NCA_PACKET_HEADER) < pPacket->Header.PacketBodyLen)
{
goto get_packet;
}
}
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);
//
// Request packets are special.
//
if (pHeader->PacketType == DG_REQUEST)
{
if (pPacket->Header.AuthProto != 0)
{
goto get_packet;
}
if (pPacket->Header.PacketFlags & DG_PF_OVERSIZE_PACKET)
{
goto get_packet;
}
ServerResponded = TRUE;
DealWithRequest(pPacket);
//
// DealWithRequest freed our packet, so get a new one.
//
pPacket = AllocatePacket();
if (!pPacket)
{
return RPC_S_OUT_OF_MEMORY;
}
pHeader = &pPacket->Header;
ASSERT(pPacket->MaxDataLength >= MaxPduSize);
goto get_packet;
}
//
// Make sure the packet is relevant to the current call.
//
if (pHeader->SequenceNumber != SequenceNumber ||
ActivityUuid.MatchUuid(&pHeader->ActivityId))
{
//
// Not our call; ignore the packet.
//
goto get_packet;
}
ServerResponded = TRUE;
//
// 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.
//
if (pPacket->Header.AuthProto != 0 &&
0 == (pPacket->Header.PacketFlags & DG_PF_OVERSIZE_PACKET))
{
Status = VerifyPacket(pPacket);
if (Status != RPC_S_OK)
{
if (Status == RPC_P_CONTEXT_EXPIRED)
{
FreePacket(pPacket);
return RPC_S_OK;
}
goto get_packet;
}
}
//
// Now we know the packet is valid.
//
//
// 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;
if (DG_NOCALL != pHeader->PacketType)
{
UnansweredRequestCount = 0;
}
//
// Handle the packet.
//
switch (pHeader->PacketType)
{
case DG_RESPONSE: Status = DealWithResponse(pPacket); break;
case DG_FACK: Status = DealWithFack (pPacket); break;
case DG_WORKING: Status = DealWithWorking (pPacket); break;
case DG_NOCALL: Status = DealWithNocall (pPacket); break;
case DG_QUACK: Status = DealWithQuack (pPacket); break;
case DG_FAULT: Status = DealWithFault (pPacket); break;
case DG_REJECT: Status = DealWithFault (pPacket); break;
case DG_PING: FreePacket(pPacket); break;
default: Status = RPC_S_PROTOCOL_ERROR; break;
}
}
else if (Status == RPC_P_ABORT_CALL)
{
return RPC_P_ABORT_CALL;
}
else
{
FreePacket(pPacket);
++TimeoutCount;
Timeout = 1;
//
// Shorten the burst length.
//
SendBurstLength = (1+SendBurstLength)/2;
if (Status != RPC_P_TIMEOUT)
{
//
// Perhaps it's a transient error. Wait a moment and try again.
//
ASSERT(Status == RPC_S_OUT_OF_RESOURCES ||
Status == RPC_S_OUT_OF_MEMORY ||
Status == RPC_P_RECEIVE_FAILED
);
#ifdef MULTITHREADED
Sleep(500);
#endif
}
Status = RPC_S_OK;
}
#ifdef MULTITHREADED
if (TestCancel() > 0)
{
++CancelEventId;
CancelPending = TRUE;
CancelTime = CurrentTimeInMsec();
}
if (RPC_S_OK == Status)
{
if (CancelPending)
{
if ((CurrentTimeInMsec() - CancelTime) / 1000 > ThreadGetRpcCancelTimeout() )
{
CancelPending = FALSE;
Status = RPC_S_CALL_CANCELLED;
}
}
}
#endif
return Status;
}
RPC_STATUS
DG_CCALL::DealWithRequest(
PDG_PACKET pPacket
)
{
RPC_STATUS Status = RPC_S_OK;
PNCA_PACKET_HEADER pHeader = &pPacket->Header;
RPC_MESSAGE CallbackMessage;
PNCA_PACKET_HEADER OriginalHeader;
//
// Save the server data rep for challenge response processing.
//
pCAssociation->ServerDataRep = 0x00ffffff & (*(unsigned long PAPI *) (pHeader->DataRep));
//
// 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 != (pPacket->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;
OriginalHeader = pHeader;
CallbackMessage.Handle = this;
CallbackMessage.DataRepresentation = 0x00ffffff & (*(unsigned long PAPI *) &pHeader->DataRep);
CallbackMessage.Buffer = pHeader->Data;
CallbackMessage.BufferLength = pHeader->PacketBodyLen;
CallbackMessage.ProcNum = pHeader->OperationNumber;
CallbackMessage.RpcInterfaceInformation = conv_ServerIfHandle;
CallbackMessage.ManagerEpv = 0;
CallbackMessage.ImportContext = 0;
CallbackMessage.TransferSyntax = 0;
CallbackMessage.RpcFlags = RPC_NCA_FLAGS_IDEMPOTENT;
InsideCallback = TRUE;
Status = DispatchCallback(((PRPC_SERVER_INTERFACE) conv_ServerIfHandle)->DispatchTable,
&CallbackMessage,
&ExceptionCode
);
if (Status)
{
if (Status == RPC_P_EXCEPTION_OCCURED)
{
Status = ExceptionCode;
}
}
//
// We can't send a fragmented callback response.
//
if (RPC_S_OK == Status && CallbackMessage.BufferLength > MaxFragmentSize)
{
Status = RPC_S_CALL_FAILED;
}
}
if (Status != RPC_S_OK)
{
//
// If DispatchCallback failed [as opposed to failing to lookup acticity
// or wrong boottime] because of an exception or wrong op num the
// buffer is still pointing to the incoming request buffer.
//
InitErrorPacket(pHeader, DG_REJECT, Status);
pCAssociation->pTransport->Send(
Endpoint,
pHeader,
sizeof(NCA_PACKET_HEADER) + pHeader->PacketBodyLen,
FALSE,
CallbackAddress
);
FreePacket(pPacket);
InsideCallback = FALSE;
return Status;
}
//
// The callback was successful, so the buffer has changed.
// Create the packet header, send the response, and free the
// request and response buffers.
//
pHeader = ((PNCA_PACKET_HEADER) CallbackMessage.Buffer)-1;
*pHeader = *OriginalHeader;
SetMyDataRep(pHeader);
pHeader->PacketType = DG_RESPONSE;
pHeader->PacketFlags = DG_PF_NO_FACK;
pHeader->FragmentNumber = 0;
pHeader->PacketBodyLen = CallbackMessage.BufferLength;
ASSERT(CallbackMessage.BufferLength <= MaxFragmentSize);
SealAndSendPacket(pHeader);
FreePacket(pPacket);
FreeBuffer(&CallbackMessage);
InsideCallback = FALSE;
return RPC_S_OK;
}
RPC_STATUS
DG_CCALL::DealWithFack(
PDG_PACKET pPacket
)
{
//
// If we were talking to the endpoint mapper endpoint, begin
// using the endpoint from which the server responded.
//
CallbackAddress = pCAssociation->UpdateServerAddress(CallbackAddress);
SendBurstLength += 1;
UpdateSendWindow(pPacket, ActiveSecurityContext, pCAssociation);
FreePacket(pPacket);
return RPC_S_OK;
}
RPC_STATUS
DG_CCALL::DealWithResponse(
PDG_PACKET pPacket
)
{
ASSERT( !(pPacket->Header.PacketBodyLen % 8) ||
!(pPacket->Header.PacketFlags & DG_PF_FRAG) ||
(pPacket->Header.PacketFlags & DG_PF_LAST_FRAG) );
ASSERT( FirstUnsentFragment > FinalSendFrag );
//
// If we were talking to the endpoint mapper endpoint, begin
// using the endpoint from which the server responded.
//
CallbackAddress = pCAssociation->UpdateServerAddress(CallbackAddress);
//
// The first response is implicitly a FACK for the final request packet.
//
MarkAllPacketsReceived();
//
// Add packet to received list, and send a fack if necessary.
//
UpdateReceiveWindow(pPacket);
return RPC_S_OK;
}
RPC_STATUS
DG_CCALL::DealWithWorking(
PDG_PACKET pPacket
)
{
ASSERT( FirstUnsentFragment > FinalSendFrag );
//
// WORKING is implicitly a FACK for the final request packet.
//
MarkAllPacketsReceived();
//
// If we were talking to the endpoint mapper endpoint, begin
// using the endpoint from which the server responded.
//
CallbackAddress = pCAssociation->UpdateServerAddress(CallbackAddress);
//
// Reduce server load by increasing the timeout during long calls.
//
++WorkingCount;
Timeout = (WorkingCount < 5) ? (1 << WorkingCount) : 32;
#ifdef MULTITHREADED
unsigned long CancelTimeout = ThreadGetRpcCancelTimeout();
if (CancelTimeout < 2)
{
CancelTimeout = 2;
}
if (Timeout > CancelTimeout)
{
Timeout = CancelTimeout;
}
#endif
FreePacket(pPacket);
return RPC_S_OK;
}
RPC_STATUS
DG_CCALL::DealWithNocall(
PDG_PACKET pPacket
)
{
BOOL Used;
//
// If we were talking to the endpoint mapper endpoint, begin
// using the endpoint from which the server responded.
//
CallbackAddress = pCAssociation->UpdateServerAddress(CallbackAddress);
if (pPacket->Header.PacketBodyLen == 0)
{
//
// Don't trust the FragmentNumber field.
//
pPacket->Header.FragmentNumber = 0xffff;
}
UpdateSendWindow(pPacket, ActiveSecurityContext, pCAssociation);
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;
}
else
{
++WorkingCount;
Timeout = (WorkingCount < 5) ? (1 << WorkingCount) : 32;
#ifdef MULTITHREADED
unsigned long CancelTimeout = ThreadGetRpcCancelTimeout();
if (CancelTimeout < 2)
{
CancelTimeout = 2;
}
if (Timeout > CancelTimeout)
{
Timeout = CancelTimeout;
}
#endif
}
if (UnansweredRequestCount > 6)
{
SendQuit();
return RPC_P_ABORT_CALL;
}
return RPC_S_OK;
}
RPC_STATUS
DG_CCALL::DealWithFault(
PDG_PACKET pPacket
)
{
//
// If we were talking to the endpoint mapper endpoint, begin
// using the endpoint from which the server responded.
//
CallbackAddress = pCAssociation->UpdateServerAddress(CallbackAddress);
RPC_STATUS Status;
unsigned long Error = * (unsigned long __RPC_FAR *) pPacket->Header.Data;
if (NeedsByteSwap(&pPacket->Header))
{
ByteSwapLong(Error);
}
Status = MapFromNcaStatusCode(Error);
FreePacket(pPacket);
return Status;
}
RPC_STATUS
DG_CCALL::SendQuit(
)
{
QUIT_BODY_0 __RPC_FAR * pBody = (QUIT_BODY_0 __RPC_FAR *) pSavedPacket->Header.Data;
pSavedPacket->Header.PacketType = DG_QUIT;
pSavedPacket->Header.PacketFlags |= DG_PF_IDEMPOTENT;
pSavedPacket->Header.PacketBodyLen = sizeof(QUIT_BODY_0);
AddSerialNumber(&pSavedPacket->Header);
pBody->Version = 0;
pBody->EventId = CancelEventId;
return SealAndSendPacket(&pSavedPacket->Header);
}
RPC_STATUS
DG_CCALL::DealWithQuack(
PDG_PACKET pPacket
)
{
#ifndef MULTITHREADED
FreePacket(pPacket);
return RPC_S_OK;
#else
if (FALSE == CancelPending)
{
FreePacket(pPacket);
return RPC_S_OK;
}
QUACK_BODY_0 __RPC_FAR * pBody = (QUACK_BODY_0 __RPC_FAR *) pPacket->Header.Data;
if (0 == pPacket->Header.PacketBodyLen)
{
//
// 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->Header.PacketBodyLen < 9 ||
pBody->Version != 0)
{
#ifdef DEBUGRPC
PrintToDebugger("RPC DG: unknown QUACK format: version 0x%lx, length 0x%hx\n",
pBody->Version, pPacket->Header.PacketBodyLen
);
#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_CALL_CANCELLED;
#endif // ifndef multithreaded..else
}
RPC_STATUS
DG_CCALL::SealAndSendPacket(
PNCA_PACKET_HEADER pHeader
)
{
retry_packet:
RPC_STATUS Status = RPC_S_OK;
unsigned TrailerLength = 0;
unsigned MaxTrailerSize = 0;
PDG_SECURITY_TRAILER pTrailer = 0;
void __RPC_FAR * pDataUnderTrailer = 0;
//
// This fn uses _alloca. With Visual C++ 1.5, the fn must also declare
// at least one local variable, or the stack may be messed up.
//
if (UseSecurity && pHeader->PacketType != DG_RESPONSE)
{
//
// Stub data is encrypted in-place; we need not to encrypt the original data
// so we can retransmit it if necessary.
//
if (AuthInfo.AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
{
RpcpMemoryCopy(&pSavedPacket->Header, pHeader, sizeof(NCA_PACKET_HEADER) + pHeader->PacketBodyLen);
pHeader = &pSavedPacket->Header;
}
// Pad the stub data length to a multiple of 8, so the security
// trailer is properly aligned. OSF requires that the pad bytes
// be included in PacketBodyLen.
//
pHeader->PacketBodyLen = Align8(pHeader->PacketBodyLen);
pTrailer = (PDG_SECURITY_TRAILER) (pHeader->Data + pHeader->PacketBodyLen);
pHeader->AuthProto = (unsigned char) AuthInfo.AuthenticationService;
SECURITY_BUFFER_DESCRIPTOR BufferDescriptor;
SECURITY_BUFFER SecurityBuffers[5];
DCE_MSG_SECURITY_INFO MsgSecurityInfo;
BufferDescriptor.ulVersion = 0;
BufferDescriptor.cBuffers = 5;
BufferDescriptor.pBuffers = SecurityBuffers;
SecurityBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
SecurityBuffers[0].pvBuffer = pHeader;
SecurityBuffers[0].cbBuffer = sizeof(NCA_PACKET_HEADER);
SecurityBuffers[1].BufferType = SECBUFFER_DATA;
SecurityBuffers[1].pvBuffer = pHeader->Data;
SecurityBuffers[1].cbBuffer = pHeader->PacketBodyLen;
SecurityBuffers[2].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
SecurityBuffers[2].pvBuffer = pTrailer;
if (AuthInfo.AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
{
SecurityBuffers[2].cbBuffer = Align(sizeof(DG_SECURITY_TRAILER), Align4(ActiveSecurityContext->BlockSize()));
SecurityBuffers[3].cbBuffer = ActiveSecurityContext->MaximumHeaderLength();
}
else
{
SecurityBuffers[2].cbBuffer = Align4(sizeof(DG_SECURITY_TRAILER));
SecurityBuffers[3].cbBuffer = ActiveSecurityContext->MaximumSignatureLength();
}
MaxTrailerSize = SecurityBuffers[2].cbBuffer
+ SecurityBuffers[3].cbBuffer;
SecurityBuffers[3].BufferType = SECBUFFER_TOKEN;
SecurityBuffers[3].pvBuffer = (unsigned char PAPI *) pTrailer
+ SecurityBuffers[2].cbBuffer;
SecurityBuffers[4].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
SecurityBuffers[4].pvBuffer = &MsgSecurityInfo;
SecurityBuffers[4].cbBuffer = sizeof(DCE_MSG_SECURITY_INFO);
MsgSecurityInfo.SendSequenceNumber = pHeader->FragmentNumber;
MsgSecurityInfo.ReceiveSequenceNumber = ActiveSecurityContext->AuthContextId;
MsgSecurityInfo.PacketType = ~0;
TrailerLength = SecurityBuffers[2].cbBuffer;
if ( (pHeader->PacketFlags & DG_PF_FRAG) &&
!(pHeader->PacketFlags & DG_PF_LAST_FRAG) &&
AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
{
//
// The trailer will overwrite some user data, so save the
// user data on the stack.
//
pDataUnderTrailer = _alloca(MaxTrailerSize);
#if defined(__RPC_WIN16__)
// <<obscenity>> Visual C++ 1.5 declares _alloca() as returning
// a near pointer, when in fact it returns an offset based
// on the stack segment. So we need to coerce the segment to
// be correct.
//
// It should have been so easy...
//
void __far * StackSegmentPtr = &pDataUnderTrailer;
FP_SEG(pDataUnderTrailer) = FP_SEG(StackSegmentPtr);
#endif
if (0 == pDataUnderTrailer)
{
return RPC_S_OUT_OF_MEMORY;
}
RpcpMemoryCopy(pDataUnderTrailer, pTrailer, MaxTrailerSize);
}
pTrailer->protection_level = (unsigned char) AuthInfo.AuthenticationLevel;
pTrailer->key_vers_num = ActiveSecurityContext->AuthContextId;
Status = ActiveSecurityContext->SignOrSeal (
pHeader->SequenceNumber,
AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
&BufferDescriptor );
TrailerLength += SecurityBuffers[3].cbBuffer;
}
else
{
//
// Unsecure packet.
//
pHeader->AuthProto = 0;
}
if (RPC_S_OK == Status)
{
if (pHeader->PacketType == DG_RESPONSE)
{
Status = pCAssociation->pTransport->Send(
Endpoint,
pHeader,
sizeof(NCA_PACKET_HEADER)
+ pHeader->PacketBodyLen
+ TrailerLength,
FALSE,
CallbackAddress
);
}
else
{
Status = pCAssociation->pTransport->Send(
Endpoint,
pHeader,
sizeof(NCA_PACKET_HEADER)
+ pHeader->PacketBodyLen
+ TrailerLength,
(BufferFlags & RPC_NCA_FLAGS_BROADCAST) ? TRUE : FALSE,
pCAssociation->ServerAddress
);
}
}
// restore data underneath packet header and security trailer
if (pDataUnderTrailer)
{
RpcpMemoryCopy(pTrailer, pDataUnderTrailer, MaxTrailerSize);
}
if (Status == SEC_E_CONTEXT_EXPIRED)
{
delete PreviousSecurityContext;
PreviousSecurityContext = ActiveSecurityContext;
ActiveSecurityContext = 0;
Status = InitializeSecurityContext();
if (RPC_S_OK == Status)
{
goto retry_packet;
}
}
return Status;
}
void
DG_CCALL::InitErrorPacket(
PNCA_PACKET_HEADER pHeader,
unsigned char PacketType,
RPC_STATUS RpcStatus
)
/*++
Routine Description:
Maps <ProcessStatus> to an NCA error code and sends
a FAULT or REJECT packet, as appropriate.
Arguments:
pSendPacket - a packet to use
ProcessStatus - NT RPC error code
Return Value:
none
--*/
{
pHeader->RpcVersion = DG_RPC_PROTOCOL_VERSION;
pHeader->ServerBootTime = 0;
SetMyDataRep(pHeader);
pHeader->PacketFlags &= DG_PF_IDEMPOTENT;
pHeader->PacketFlags2 = 0;
pHeader->PacketType = PacketType;
pHeader->PacketBodyLen = sizeof(unsigned long);
*(unsigned long PAPI *)(pHeader->Data) = MapToNcaStatusCode(RpcStatus);
}
RPC_STATUS
DG_CCALL::InitializeSecurityContext(
)
{
RPC_STATUS RpcStatus = RPC_S_OK;
ActiveSecurityContext = new DG_SECURITY_CONTEXT(
&AuthInfo,
PreviousSecurityContext
? 1 + PreviousSecurityContext->AuthContextId
: 0,
&RpcStatus
);
if (0 == ActiveSecurityContext)
{
return RPC_S_OUT_OF_MEMORY;
}
if (RpcStatus)
{
delete ActiveSecurityContext;
ActiveSecurityContext = 0;
return RPC_S_OUT_OF_MEMORY;
}
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,
&ActivityUuid,
sizeof(UUID)
);
InitSecurityInfo.AuthorizationService = AuthInfo.AuthorizationService;
InitSecurityInfo.PacketType = ~0;
RpcStatus = ActiveSecurityContext->InitializeFirstTime(
AuthInfo.Credentials,
AuthInfo.ServerPrincipalName,
AuthInfo.AuthenticationLevel,
&BufferDescriptorIn
);
if (RpcStatus == RPC_P_COMPLETE_NEEDED ||
RpcStatus == RPC_P_COMPLETE_AND_CONTINUE ||
RpcStatus == RPC_P_CONTINUE_NEEDED ||
RpcStatus == RPC_S_OK )
{
ActiveSecurityContext->SetMaximumLengths();
SetFragmentLengths(ActiveSecurityContext);
}
return RpcStatus;
}
RPC_STATUS
DG_CCALL::DealWithAuthCallback(
IN void PAPI * InToken,
IN long InTokenLength,
OUT void PAPI * OutToken,
OUT long MaxOutTokenLength,
OUT long PAPI * 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;
RpcpMemoryCopy(&InitSecurityInfo.DceSecurityInfo.AssociationUuid,
&ActivityUuid,
sizeof(UUID)
);
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->InitializeOnCallback(pCAssociation->ServerDataRep,
&BufferDescriptorIn,
&BufferDescriptorOut
);
if (Status != RPC_S_OK)
{
*OutTokenLength = 0;
return (Status);
}
else
{
*OutTokenLength = SecurityBuffersOut[0].cbBuffer;
}
return (Status);
}
RPC_STATUS
DG_SECURITY_CONTEXT::InitializeFirstTime(
IN SECURITY_CREDENTIALS * Credentials,
IN RPC_CHAR * ServerPrincipalName,
IN unsigned long AuthenticationLevel,
IN OUT SECURITY_BUFFER_DESCRIPTOR PAPI * BufferDescriptor
)
/*++
Routine Description:
Arguments:
Return Value:
RPC_S_OK - Send the token to the server; everything worked fine so
far.
RPC_P_CONTINUE_NEEDED - Indicates that that everything is ok, but that
we need to call into the security package again when we have
received a token back from the server.
RPC_P_COMPLETE_NEEDED - Indicates that everyting is ok, but that we
need to call CompleteAuthToken before sending the message.
RPC_P_COMPLETE_AND_CONTINUE - Needs both a complete and a continue.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
operation.
RPC_S_ACCESS_DENIED - Access is denied.
--*/
{
SECURITY_STATUS SecurityStatus;
unsigned long ContextAttributes;
TimeStamp TimeStamp;
unsigned long ContextRequirements;
ASSERT( ( SecuritySupportLoaded != 0 )
&& ( FailedToLoad == 0 ) );
RpcSecurityInterface = Credentials->InquireProviderFunctionTable();
switch ( AuthenticationLevel )
{
case RPC_C_AUTHN_LEVEL_PKT :
ContextRequirements = ISC_REQ_USE_DCE_STYLE | ISC_REQ_DATAGRAM
| ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH
| ISC_REQ_REPLAY_DETECT;
break;
case RPC_C_AUTHN_LEVEL_PKT_INTEGRITY :
ContextRequirements = ISC_REQ_USE_DCE_STYLE | ISC_REQ_DATAGRAM
| ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH
| ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT;
break;
case RPC_C_AUTHN_LEVEL_PKT_PRIVACY :
ContextRequirements = ISC_REQ_USE_DCE_STYLE | ISC_REQ_DATAGRAM
| ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH
| ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT
| ISC_REQ_CONFIDENTIALITY;
break;
default :
ASSERT( ( AuthenticationLevel == RPC_C_AUTHN_LEVEL_CONNECT )
|| ( AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT )
|| ( AuthenticationLevel ==
RPC_C_AUTHN_LEVEL_PKT_INTEGRITY )
|| ( AuthenticationLevel ==
RPC_C_AUTHN_LEVEL_PKT_PRIVACY ) );
return(RPC_S_INVALID_LEVEL);
}
#ifdef NTENV
if (ImpersonationType != RPC_C_IMP_LEVEL_IMPERSONATE)
{
ContextRequirements |= ISC_REQ_IDENTIFY;
}
#endif
#ifdef NTENV
SecurityStatus = (*RpcSecurityInterface->InitializeSecurityContextW)(
Credentials->InquireCredentials(),
0,
ServerPrincipalName,
#else // NTENV
SecurityStatus = (*RpcSecurityInterface->InitializeSecurityContextA)(
Credentials->InquireCredentials(),
0,
(SEC_CHAR __SEC_FAR *) ServerPrincipalName,
#endif // NTENV
ContextRequirements,
0,
0, // don't know server data rep yet
BufferDescriptor,
0,
&SecurityContext,
0,
&ContextAttributes,
&TimeStamp
);
if ( ( SecurityStatus != SEC_E_OK )
&& ( SecurityStatus != SEC_I_CONTINUE_NEEDED )
&& ( SecurityStatus != SEC_I_COMPLETE_NEEDED )
&& ( SecurityStatus != SEC_I_COMPLETE_AND_CONTINUE ) )
{
if ( (SecurityStatus == SEC_E_SECPKG_NOT_FOUND)
|| (SecurityStatus == SEC_E_NO_CREDENTIALS)
|| (SecurityStatus == SEC_E_UNKNOWN_CREDENTIALS) )
{
return(RPC_S_ACCESS_DENIED);
}
if ( SecurityStatus == SEC_E_INSUFFICIENT_MEMORY )
{
return (RPC_S_OUT_OF_MEMORY);
}
else
{
#if DBG
PrintToDebugger("RPC: Initialize SecurityContext Returned %lx\n",
SecurityStatus);
#endif
return(RPC_S_SEC_PKG_ERROR);
}
}
DontForgetToDelete = 1;
SetMaximumLengths();
#ifdef NTENV
if ( (ImpersonationType == RPC_C_IMP_LEVEL_IDENTIFY) &&
(!(ContextAttributes & ISC_RET_IDENTIFY)) )
{
return (RPC_S_INVALID_ARG);
}
if ( (!(ContextAttributes & ISC_RET_MUTUAL_AUTH) )&&
(Capabilities == RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH) )
{
return (RPC_S_INVALID_ARG);
}
#endif
if ( SecurityStatus == SEC_I_CONTINUE_NEEDED )
{
return(RPC_P_CONTINUE_NEEDED);
}
if ( SecurityStatus == SEC_I_COMPLETE_NEEDED )
{
// Can't set the maximum lengths on a partly completed connection.
return(RPC_P_COMPLETE_NEEDED);
}
if ( SecurityStatus == SEC_I_COMPLETE_AND_CONTINUE )
{
return(RPC_P_COMPLETE_AND_CONTINUE);
}
else
{
// Can't set the maximum lengths on a partly completed connection.
SetMaximumLengths();
}
SecurityContextInitialized = 1;
return (RPC_S_OK);
}
RPC_STATUS
DG_SECURITY_CONTEXT::InitializeOnCallback(
IN unsigned long DataRepresentation,
SECURITY_BUFFER_DESCRIPTOR PAPI * InputBufferDescriptor,
SECURITY_BUFFER_DESCRIPTOR PAPI * OutputBufferDescriptor
)
/*++
Routine Description:
Arguments:
Return Value:
RPC_S_OK - Send the token to the server; everything worked fine so
far.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
operation.
RPC_S_ACCESS_DENIED - Access is denied.
--*/
{
SECURITY_STATUS SecurityStatus;
unsigned long ContextAttributes;
TimeStamp TimeStamp;
ASSERT( (SecuritySupportLoaded != 0)
&& (FailedToLoad == 0) );
#ifdef NTENV
SecurityStatus = (*RpcSecurityInterface->InitializeSecurityContextW)(
#else // NTENV
SecurityStatus = (*RpcSecurityInterface->InitializeSecurityContextA)(
#endif
0, &SecurityContext, 0, 0, 0, DataRepresentation,
InputBufferDescriptor, 0, &SecurityContext, OutputBufferDescriptor,
&ContextAttributes, &TimeStamp);
if ( ( SecurityStatus != SEC_E_OK )
&& ( SecurityStatus != SEC_I_COMPLETE_NEEDED ) )
{
DontForgetToDelete = 0;
if ( (SecurityStatus == SEC_E_SECPKG_NOT_FOUND)
|| (SecurityStatus == SEC_E_NO_CREDENTIALS)
|| (SecurityStatus == SEC_E_UNKNOWN_CREDENTIALS) )
{
return(RPC_S_ACCESS_DENIED);
}
if ( SecurityStatus == SEC_E_INSUFFICIENT_MEMORY )
{
return (RPC_S_OUT_OF_MEMORY);
}
else
{
return SecurityStatus;
}
}
SetMaximumLengths();
if ( SecurityStatus == SEC_I_COMPLETE_NEEDED )
{
return(RPC_P_COMPLETE_NEEDED);
}
return(RPC_S_OK);
}
RPC_STATUS
DG_SECURITY_CONTEXT::SignOrSeal (
IN unsigned long SequenceNumber,
IN unsigned int SignNotSealFlag,
IN OUT SECURITY_BUFFER_DESCRIPTOR PAPI * BufferDescriptor
)
/*++
Routine Description:
A protocol module will use this routine to prepare a message to be
sent so that it can be verified that the message has not been tampered
with, and that it has not been exchanged out of sequence. The sender
will use this routine to prepare the message; the receiver will use
SECURITY_CONTEXT::VerifyOrUnseal to verify the message. Typically,
the security package will generate a cryptographic checksum of the
message and include sequencing information.
Arguments:
SignNotSealFlag - Supplies a flag indicating that MakeSignature should
be called rather than SealMessage.
BufferDescriptor - Supplies the message to to signed or sealed and returns
the resulting message (after being signed or sealed).
Return Value:
RPC_S_OK - This routine will always succeed.
--*/
{
SECURITY_STATUS SecurityStatus;
SEAL_MESSAGE_FN SealMessage;
if ( SignNotSealFlag == 0 )
{
SealMessage = (SEAL_MESSAGE_FN) RpcSecurityInterface->Reserved3;
SecurityStatus = (*SealMessage)(&SecurityContext,
0, BufferDescriptor, SequenceNumber);
}
else
{
SecurityStatus = (*RpcSecurityInterface->MakeSignature)(
&SecurityContext, 0, BufferDescriptor, SequenceNumber);
}
return SecurityStatus;
}
RPC_STATUS
DG_SECURITY_CONTEXT::VerifyOrUnSealPacket(
IN unsigned long SequenceNumber,
IN unsigned int VerifyNotUnsealFlag,
IN OUT SECURITY_BUFFER_DESCRIPTOR PAPI * BufferDescriptor
)
{
SECURITY_STATUS SecurityStatus;
unsigned long QualityOfProtection;
UNSEAL_MESSAGE_FN UnsealMessage;
if ( VerifyNotUnsealFlag == 0 )
{
UnsealMessage = (UNSEAL_MESSAGE_FN) RpcSecurityInterface->Reserved4;
SecurityStatus = (*UnsealMessage) (
&SecurityContext, BufferDescriptor, SequenceNumber,
&QualityOfProtection );
}
else
{
SecurityStatus = (*RpcSecurityInterface->VerifySignature)(
&SecurityContext, BufferDescriptor, SequenceNumber,
&QualityOfProtection);
}
if ( SecurityStatus != SEC_E_OK )
{
#if DBG
if ((SecurityStatus != SEC_E_MESSAGE_ALTERED)
&&(SecurityStatus != SEC_E_OUT_OF_SEQUENCE))
{
PrintToDebugger("Verify/UnSeal Returned Unexp. Code [%lx]\n",
SecurityStatus);
}
#endif
ASSERT( (SecurityStatus == SEC_E_MESSAGE_ALTERED) ||
(SecurityStatus == SEC_E_OUT_OF_SEQUENCE) );
return(RPC_S_ACCESS_DENIED);
}
return(RPC_S_OK);
}
RPC_STATUS
DG_CCALL::VerifyPacket(
PDG_PACKET pPacket
)
{
PDG_SECURITY_TRAILER pVerifier = (PDG_SECURITY_TRAILER) (pPacket->Header.Data + pPacket->Header.PacketBodyLen);
if (pVerifier->key_vers_num == ActiveSecurityContext->AuthContextId)
{
return VerifySecurePacket(pPacket, ActiveSecurityContext);
}
if (pVerifier->key_vers_num == ActiveSecurityContext->AuthContextId - 1)
{
return VerifySecurePacket(pPacket, PreviousSecurityContext);
}
return RPC_P_CONTEXT_EXPIRED;
}
inline
int
DG_CCALL::IsSupportedAuthInfo(
IN CLIENT_AUTH_INFO * ClientAuthInfo
)
{
if (!UseSecurity)
{
if (!ClientAuthInfo || ClientAuthInfo->AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE)
{
return TRUE;
}
else
{
return FALSE;
}
}
if (!ClientAuthInfo)
{
return FALSE;
}
return( AuthInfo.IsSupportedAuthInfo(ClientAuthInfo) );
}
RPC_STATUS
DG_BINDING_HANDLE::SetConnectionParameter (
IN unsigned Parameter,
IN unsigned long Value
)
/*++
Routine Description:
This fn changes connection parameters for the binding handle.
Unrecognized parameter constants are ignored.
Arguments:
Parameter - a constant representing the parameter to change
Value - new value for the parameter
Return Value:
RPC_S_OK - all is well
RPC_S_WRONG_KIND_OF_BINDING - you can't change some things once a call
has been made over the handle
--*/
{
switch (Parameter)
{
case RPC_C_PARM_BUFFER_LENGTH:
{
if (pCAssociation)
{
return RPC_S_WRONG_KIND_OF_BINDING;
}
if (Value)
{
ParmBufferLength = (unsigned) Value;
}
else
{
ParmBufferLength = pTransport->DefaultBufferLength;
}
if (ParmBufferLength < ParmMaxDatagramLength)
{
ParmBufferLength = ParmMaxDatagramLength;
}
break;
}
case RPC_C_PARM_MAX_PACKET_LENGTH:
{
if (pCAssociation)
{
return RPC_S_WRONG_KIND_OF_BINDING;
}
if (Value)
{
ParmMaxDatagramLength = min(Value, pTransport->MaxPduSize);
}
else
{
ParmMaxDatagramLength = pTransport->PreferredPduSize;
}
if (ParmBufferLength < ParmMaxDatagramLength)
{
ParmBufferLength = ParmMaxDatagramLength;
}
break;
}
}
return RPC_S_OK;
}
RPC_STATUS
DG_BINDING_HANDLE::InqConnectionParameter (
IN unsigned Parameter,
IN unsigned long __RPC_FAR * Value
)
/*++
Routine Description:
This fn examines connection parameters for the binding handle.
Arguments:
Parameter - a constant representing the parameter to see
Value - pointer where the value is copied
Return Value:
RPC_S_OK - all is well
RPC_S_INVALID_ARG - unknown or inapplicable parm constant
--*/
{
switch (Parameter)
{
case RPC_C_PARM_BUFFER_LENGTH:
{
*Value = InqTransportBufferLength();
return RPC_S_OK;
}
case RPC_C_PARM_MAX_PACKET_LENGTH:
{
*Value = InqMaxDatagramLength();
return RPC_S_OK;
}
default:
{
return RPC_S_INVALID_ARG;
}
}
}
int
DG_CASSOCIATION::CompareWithBinding(
IN PDG_BINDING_HANDLE pBinding
)
{
ASSERT(Magic == MAGIC_ASSOC);
if (0 != pDceBinding->Compare(pBinding->pDceBinding))
{
return 1;
}
// BUGBUG
// if (pBinding->InqTransportBufferLength() != TransportBufferLength)
// {
// return 1;
// }
return 0;
}
#if defined(NTENV) || defined(DOSWIN32RPC)
RPC_STATUS
DG_CCALL::Cancel(
void * ThreadHandle
)
{
InterlockedIncrement(&Cancelled);
return RPC_S_OK;
}
unsigned
DG_CCALL::TestCancel(
)
{
return InterlockedExchange(&Cancelled, 0);
}
#endif
RPC_STATUS
DG_CCALL::SendSomething(
)
{
RPC_STATUS Status;
#ifdef DEBUGRPC
Status = 0xbaadcccc;
#endif
if (!TimeoutCount)
{
Status = SendSomeFragments(DG_REQUEST);
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;
}
}
}
else
{
unsigned TimeoutLimit;
unsigned TimeoutLevel = pBindingHandle->InqComTimeout();
if (BufferFlags & RPC_NCA_FLAGS_BROADCAST)
{
TimeoutLimit = (TimeoutLevel+1)/2;
}
else if (TimeoutLevel == RPC_C_BINDING_INFINITE_TIMEOUT)
{
TimeoutLimit = ~0;
}
else
{
TimeoutLimit = ( 1 << TimeoutLevel );
}
if (TimeoutCount > TimeoutLimit)
{
SendQuit();
return RPC_P_TIMEOUT;
}
#ifdef MULTITHREADED
if (CancelPending)
{
SendQuit();
Status = RPC_S_OK;
}
else
#endif
{
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(DG_REQUEST);
}
else
{
//
// Send a FACK if we have seen at least one response packet.
//
if (pReceivedPackets || ReceiveFragmentBase > 0)
{
Status = SendFack(pReceivedPackets);
}
else
{
Status = SendPing();
}
}
}
}
return Status;
}
#if defined(MULTITHREADED)
void
DG_CASSOCIATION::ScavengerProc(
void * Unused
)
/*++
Routine Description:
Deletes associations that have remained unused for five minutes. To limit
time spent holding the global mutex, we move all discarded associations
to a temporary dictionary, and then actually free the dictionary contents
while the mutex is not held.
--*/
{
BOOL PostScavenger = FALSE;
PDG_CASSOCIATION Association;
PDG_CCALL Call;
//
// Delete stale calls from each association.
//
DG_CCALL_DICT DiscardedCallDict;
GlobalMutexRequest();
pAssociationDict->Reset();
while ( (Association = pAssociationDict->Next()) != 0 )
{
Association->ScavengeCalls(&DiscardedCallDict);
}
GlobalMutexClear();
DiscardedCallDict.Reset();
while ( (Call = DiscardedCallDict.Next()) != 0 )
{
delete Call;
}
//
// Delete stale associations.
//
DG_CASSOCIATION_DICT DiscardedAssociationDict;
GlobalMutexRequest();
pAssociationDict->Reset();
while ( (Association = pAssociationDict->Next()) != 0 )
{
if (0 == Association->ReferenceCount.GetInteger())
{
if (Association->ErrorFlag() ||
CurrentTimeInMsec() - Association->ExpirationTime > CASSOCIATION_CACHE_LIMIT)
{
pAssociationDict->Delete(Association->DictionaryKey);
if (-1 == DiscardedAssociationDict.Insert(Association))
{
delete Association;
}
}
else
{
//
// There is an association with no references - monitor it
// until it expires or its reference count increases.
//
PostScavenger = TRUE;
}
}
}
GlobalMutexClear();
DiscardedAssociationDict.Reset();
while ( (Association = DiscardedAssociationDict.Next()) != 0 )
{
delete Association;
}
if (PostScavenger)
{
DelayedActions->Add(ClientScavengerTimer, CLIENT_SCAVENGER_INTERVAL, FALSE);
}
}
void
DG_CASSOCIATION::ScavengeCalls(
DG_CCALL_DICT * DiscardedCallsDict
)
/*++
Routine Description:
This deletes unused calls in the association. We define "unused" as
"sitting in the inactive calls dictionary for two minutes". To limit
time spent holding the association mutex, we move all discarded calls to
a temporary dictionary, and then actually free the dictionary contents
while the mutex is not held.
--*/
{
PDG_CCALL Call;
BOOL PostScavenger = FALSE;
Mutex.Request();
InactiveCallsDict.Reset();
while ( (Call = InactiveCallsDict.Next()) != 0 )
{
if (CurrentTimeInMsec() - Call->ExpirationTime > CCALL_CACHE_TIME)
{
InactiveCallsDict.Delete(Call->AssociationKey);
if (-1 == DiscardedCallsDict->Insert(Call))
{
delete Call;
}
}
else
{
//
// This call is unused but is not being cleaned up yet. Monitor
// it until it is used or expires.
//
PostScavenger = TRUE;
}
}
Mutex.Clear();
if (PostScavenger)
{
DelayedActions->Add(ClientScavengerTimer, CLIENT_SCAVENGER_INTERVAL, FALSE);
}
}
#endif // MULTITHREADED
BOOL
INTERFACE_AND_OBJECT_LIST::Insert(
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))
{
return TRUE;
}
}
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;
}
delete Current;
return TRUE;
}
INTERFACE_AND_OBJECT_LIST::~INTERFACE_AND_OBJECT_LIST(
)
{
INTERFACE_AND_OBJECT * Next;
while (Head)
{
Next = Head->Next;
delete Head;
Head = Next;
}
}
#ifdef MAJORDEBUG
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