mirror of https://github.com/lianthony/NT4.0
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.
1417 lines
30 KiB
1417 lines
30 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
spcclnt.cxx
|
|
|
|
Abstract:
|
|
|
|
Author:
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include <sysinc.h>
|
|
#include <rpc.h>
|
|
#include <rpcerrp.h>
|
|
#include <rpcdcep.h>
|
|
#include <rpcqos.h>
|
|
#include <util.hxx>
|
|
#include <threads.hxx>
|
|
#include <sdict.hxx>
|
|
#include <interlck.hxx>
|
|
#include <rpcuuid.hxx>
|
|
#include <binding.hxx>
|
|
#include <handle.hxx>
|
|
#include <lpcheap.hxx>
|
|
#include <lpcmsg.hxx>
|
|
#include <lpcport.hxx>
|
|
#include <lpcproc.hxx>
|
|
#include <lpcsys.hxx>
|
|
#include <lrpcpack.hxx>
|
|
#include <lrpcclnt.hxx>
|
|
#include <epmap.h>
|
|
|
|
static LRPC_ASSOC_GROUP_DICT * AssocGroups = NULL;
|
|
|
|
RPC_STATUS
|
|
I_RpcParseSecurity (
|
|
IN RPC_CHAR * NetworkOptions,
|
|
OUT SECURITY_QUALITY_OF_SERVICE * SecurityQualityOfService
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse a string of security options and build into the binary format
|
|
required by the operating system. The network options must follow
|
|
the following syntax. Case is not sensitive.
|
|
|
|
security=
|
|
[anonymous|identification|impersonation|delegation]
|
|
[dynamic|static]
|
|
[true|false]
|
|
|
|
All three fields must be present. To specify impersonation
|
|
with dynamic tracking and effective only, use the following
|
|
string for the network options.
|
|
|
|
"security=impersonation dynamic true"
|
|
|
|
Arguments:
|
|
|
|
NetworkOptions - Supplies the string containing the network options
|
|
to be parsed.
|
|
|
|
SecurityQualityOfService - Returns the binary format of the network
|
|
options.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_CANNOT_SUPPORT - Not implemented for Chicago.
|
|
|
|
--*/
|
|
{
|
|
return (RPC_S_CANNOT_SUPPORT);
|
|
}
|
|
|
|
|
|
LRPC_BINDING_HANDLE::LRPC_BINDING_HANDLE (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We just allocate an LRPC_BINDING_HANDLE and initialize things so that
|
|
we can use it later.
|
|
|
|
Arguments:
|
|
|
|
--*/
|
|
{
|
|
DceBinding = NULL;
|
|
|
|
AssocGroup = NULL;
|
|
}
|
|
|
|
|
|
LRPC_BINDING_HANDLE::~LRPC_BINDING_HANDLE (
|
|
)
|
|
/*++
|
|
|
|
--*/
|
|
{
|
|
if (DceBinding != NULL) {
|
|
delete DceBinding;
|
|
DceBinding = 0;
|
|
}
|
|
|
|
if (AssocGroup != NULL) {
|
|
AssocGroup->Dereference();
|
|
AssocGroup = 0;
|
|
}
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-C: Deleted LRPC_BINDING_HANDLE %x\r\n", this);
|
|
#endif
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_BINDING_HANDLE::GetBuffer (
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Message - Supplies the length of the buffer required, and returns the
|
|
new buffer.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
LRPC_CASSOCIATION * Association = NULL;
|
|
|
|
if (DceBinding->InqEndpoint() == NULL || *DceBinding->InqEndpoint() == NULL) {
|
|
RpcStatus = ResolveBinding((RPC_CLIENT_INTERFACE *)
|
|
Message->RpcInterfaceInformation);
|
|
if (RpcStatus != RPC_S_OK) {
|
|
return (RpcStatus);
|
|
}
|
|
|
|
if (AssocGroup != NULL) {
|
|
AssocGroup->Dereference();
|
|
AssocGroup = NULL;
|
|
}
|
|
}
|
|
|
|
// Attach binding handle to association group, if not already attached.
|
|
|
|
if (AssocGroup == NULL) {
|
|
RpcStatus = AttachToGroup();
|
|
if (RpcStatus != RPC_S_OK) {
|
|
return (RpcStatus);
|
|
}
|
|
}
|
|
|
|
RpcStatus = AllocateAssociation(&Association,
|
|
(RPC_CLIENT_INTERFACE *)
|
|
Message->RpcInterfaceInformation);
|
|
|
|
// Retry if necessary
|
|
|
|
if (RpcStatus == RPC_P_CONNECTION_CLOSED) {
|
|
delete Association;
|
|
RpcStatus = AllocateAssociation(&Association,
|
|
(RPC_CLIENT_INTERFACE *)
|
|
Message->RpcInterfaceInformation);
|
|
}
|
|
|
|
if ( RpcStatus != RPC_S_OK) {
|
|
return (RpcStatus);
|
|
}
|
|
|
|
ASSERT(Association != NULL);
|
|
|
|
// This binding handle owns the Association. Make it so.
|
|
|
|
Association->CurrentBindingHandle = this;
|
|
|
|
// Get a real buffer.
|
|
|
|
RpcStatus = Association->GetBuffer(Message);
|
|
if ( RpcStatus != RPC_S_OK ) {
|
|
Association->AbortAssociation();
|
|
ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY);
|
|
return(RpcStatus);
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_BINDING_HANDLE::BindingCopy (
|
|
OUT BINDING_HANDLE * * DestinationBinding,
|
|
IN unsigned int MaintainContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We will make a copy of this binding handle in one of two ways, depending
|
|
on whether on not this binding handle has an association.
|
|
|
|
Arguments:
|
|
|
|
DestinationBinding - Returns a copy of this binding handle.
|
|
|
|
MaintainContext - Supplies a flag that indicates whether or not context
|
|
is being maintained over this binding handle. A non-zero value
|
|
indicates that context is being maintained.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - This binding handle has been successfully copied.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to make a copy
|
|
of this binding handle.
|
|
|
|
--*/
|
|
{
|
|
LRPC_BINDING_HANDLE * NewBindingHandle = NULL;
|
|
|
|
UNUSED(MaintainContext);
|
|
|
|
// Create a brand new binding handle.
|
|
|
|
NewBindingHandle = new LRPC_BINDING_HANDLE();
|
|
if ( NewBindingHandle == NULL ) {
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
CritSec.Enter();
|
|
|
|
// Copy DceBinding out of existing binding handle.
|
|
|
|
NewBindingHandle->DceBinding = DceBinding->DuplicateDceBinding();
|
|
|
|
// If existing handle attached to association group, attach new handle to
|
|
// same group.
|
|
|
|
if (AssocGroup != NULL) {
|
|
NewBindingHandle->AssocGroup = AssocGroup;
|
|
AssocGroup->AddRef();
|
|
}
|
|
|
|
CritSec.Leave();
|
|
|
|
*DestinationBinding = (BINDING_HANDLE *) NewBindingHandle;
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_BINDING_HANDLE::BindingFree (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When the application is done with a binding handle, this routine will
|
|
get called.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - This operation always succeeds.
|
|
|
|
--*/
|
|
{
|
|
delete this;
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
void
|
|
LRPC_BINDING_HANDLE::PrepareBindingHandle (
|
|
IN void * TransportInformation,
|
|
IN DCE_BINDING * DceBinding
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method will be called just before a new binding handle is returned
|
|
to the user. We just stack the binding information so that we can use
|
|
it later when the first remote procedure call is made. At that time,
|
|
we will actually bind to the interface.
|
|
|
|
Arguments:
|
|
|
|
TransportInformation - Unused.
|
|
|
|
DceBinding - Supplies the binding information for this binding handle.
|
|
|
|
--*/
|
|
{
|
|
UNUSED(TransportInformation);
|
|
|
|
ASSERT(DceBinding != NULL);
|
|
|
|
this->DceBinding = DceBinding;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_BINDING_HANDLE::ToStringBinding (
|
|
OUT RPC_CHAR * * StringBinding
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need to convert the binding handle into a string binding. If the
|
|
handle is unbound, use the DceBinding directly, otherwise, get it from
|
|
the association.
|
|
|
|
Arguments:
|
|
|
|
StringBinding - Returns the string representation of the binding
|
|
handle.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - The binding handle has successfully been converted into a
|
|
string binding.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate the
|
|
string.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(DceBinding != NULL);
|
|
|
|
*StringBinding = DceBinding->StringBindingCompose(
|
|
InqPointerAtObjectUuid());
|
|
|
|
if ( *StringBinding == NULL ) {
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_BINDING_HANDLE::ResolveBinding (
|
|
IN RPC_CLIENT_INTERFACE * Interface
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need to try and resolve the endpoint for this binding handle
|
|
if necessary (the binding handle is partially-bound).
|
|
|
|
Arguments:
|
|
|
|
Interface - Supplies interface information to be used
|
|
in resolving the endpoint.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - This binding handle is a full resolved binding handle.
|
|
|
|
RPC_S_NO_ENDPOINT_FOUND - The endpoint can not be resolved.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to resolve
|
|
the endpoint.
|
|
|
|
EPT_S_NOT_REGISTERED - There are no more endpoints to be found
|
|
for the specified combination of interface, network address,
|
|
and lookup handle.
|
|
|
|
EPT_S_CANT_PERFORM_OP - The operation failed due to misc. error e.g.
|
|
unable to bind to the EpMapper.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
LPC_PORT * Port;
|
|
|
|
ASSERT(DceBinding != NULL);
|
|
|
|
// If Endpoint not specified in DceBinding, get it from the Interface
|
|
// specification (if possible), otherwise, use the endpoint mapper.
|
|
|
|
do {
|
|
|
|
RpcStatus = DceBinding->ResolveEndpointIfNecessary(Interface,
|
|
InqPointerAtObjectUuid(),
|
|
InquireEpLookupHandle(),
|
|
FALSE,
|
|
InqComTimeout()
|
|
);
|
|
|
|
if (RpcStatus != RPC_S_OK) {
|
|
return (RpcStatus);
|
|
}
|
|
|
|
Port = LpcSystemReferencePortByName((const char *)DceBinding->InqEndpoint());
|
|
if (Port != NULL) {
|
|
Port->Dereference();
|
|
Port = NULL;
|
|
return (RPC_S_OK);
|
|
}
|
|
|
|
DceBinding->MakePartiallyBound();
|
|
|
|
} while (*InquireEpLookupHandle() != NULL);
|
|
|
|
return(RPC_S_SERVER_UNAVAILABLE);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_BINDING_HANDLE::AttachToGroup (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - This binding handle is a full resolved binding handle.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to resolve
|
|
the endpoint.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
ASSERT(DceBinding != NULL);
|
|
|
|
// If binding handle was attached to association group, dereference
|
|
// it (AssocGroup), since it is probably different now.
|
|
|
|
if (AssocGroup != NULL) {
|
|
AssocGroup->Dereference();
|
|
AssocGroup = NULL;
|
|
}
|
|
|
|
// Look for new DceBinding in all assocication groups.
|
|
|
|
GlobalMutexRequest();
|
|
|
|
ASSERT(AssocGroups != NULL);
|
|
|
|
AssocGroups->Reset();
|
|
while ( (AssocGroup = AssocGroups->Next() ) != 0 ) {
|
|
if ( AssocGroup->DceBinding->Compare(DceBinding) == 0) {
|
|
AssocGroup->AddRef();
|
|
GlobalMutexClear();
|
|
return(RPC_S_OK);
|
|
}
|
|
}
|
|
GlobalMutexClear();
|
|
|
|
// Association group not found, create a new one.
|
|
|
|
AssocGroup = new LRPC_ASSOC_GROUP(DceBinding);
|
|
|
|
if (AssocGroup == NULL) {
|
|
return (RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_BINDING_HANDLE::BindingReset (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will set the endpoint of this binding handle to zero,
|
|
if possible. The binding handle will become partially bound as a
|
|
result. If a remote procedure call has been made on this binding
|
|
handle, it will fail as well.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - The binding handle has successfully been made partially
|
|
bound.
|
|
|
|
RPC_S_WRONG_KIND_OF_BINDING - The binding handle currently has remote
|
|
procedure calls active.
|
|
|
|
--*/
|
|
{
|
|
DceBinding->MakePartiallyBound();
|
|
|
|
// Destroy endpoint mapper context, if set.
|
|
|
|
if ( *InquireEpLookupHandle() != 0 )
|
|
{
|
|
EpFreeLookupHandle(*InquireEpLookupHandle());
|
|
*InquireEpLookupHandle() = 0;
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_BINDING_HANDLE::AllocateAssociation (
|
|
OUT LRPC_CASSOCIATION ** ppAssociation,
|
|
IN PRPC_CLIENT_INTERFACE Interface
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Interface - Supplies information describing the
|
|
interface to which we wish to bind.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
RPC_CHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
|
|
DWORD ComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1;
|
|
BOOL Boolean;
|
|
LRPC_CASSOCIATION * Association;
|
|
|
|
ASSERT (AssocGroup != NULL);
|
|
|
|
// Find existing Association for callbacks. Kind of a hack, but so are
|
|
// callbacks.
|
|
|
|
if ((Association =
|
|
AssocGroup->FindActiveAssociation(Interface)) != NULL) {
|
|
*ppAssociation = Association;
|
|
return (RPC_S_OK);
|
|
}
|
|
|
|
// Before we even bother to find or create an association, lets
|
|
// check to make sure that we are on the same machine as the server.
|
|
|
|
ASSERT(DceBinding != 0);
|
|
|
|
ASSERT( DceBinding->InqNetworkAddress() != 0 );
|
|
|
|
if ( DceBinding->InqNetworkAddress()[0] != 0 ) {
|
|
Boolean = GetComputerName((char *)ComputerName, &ComputerNameLength);
|
|
|
|
#if DEBUGRPC
|
|
|
|
if ( Boolean != TRUE ) {
|
|
PrintToDebugger("LRPC-C: GetComputerName : %d\n", GetLastError());
|
|
}
|
|
|
|
#endif
|
|
|
|
ASSERT( Boolean == TRUE );
|
|
|
|
if ( RpcpStringCompare(DceBinding->InqNetworkAddress(),
|
|
ComputerName) != 0 ) {
|
|
return(RPC_S_SERVER_UNAVAILABLE);
|
|
}
|
|
}
|
|
|
|
// Allocate association from association group.
|
|
|
|
RpcStatus = AssocGroup->AllocateAssociation(Interface,
|
|
&Association,
|
|
InqComTimeout());
|
|
|
|
if (RpcStatus != RPC_S_OK) {
|
|
return(RpcStatus);
|
|
}
|
|
|
|
*ppAssociation = Association;
|
|
|
|
return(RpcStatus);
|
|
}
|
|
|
|
|
|
|
|
LRPC_CASSOCIATION::LRPC_CASSOCIATION (
|
|
DCE_BINDING * DceBinding
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This association will be initialized, so that it is ready to be
|
|
placed into the dictionary of associations.
|
|
|
|
Arguments:
|
|
|
|
DceBinding - Supplies the DCE_BINDING which will name this association.
|
|
|
|
--*/
|
|
{
|
|
this->DceBinding = DceBinding->DuplicateDceBinding();
|
|
|
|
LpcClientPort = NULL;
|
|
|
|
CurrentBindingHandle = 0;
|
|
|
|
Thread = 0;
|
|
|
|
IBinding = NULL;
|
|
|
|
CallbackDepth = 0;
|
|
|
|
ReferenceCount = 1;
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-C: Created Association %x\r\n", this);
|
|
#endif
|
|
}
|
|
|
|
|
|
LRPC_CASSOCIATION::~LRPC_CASSOCIATION (
|
|
)
|
|
{
|
|
AbortAssociation();
|
|
|
|
if (DceBinding != NULL) {
|
|
delete DceBinding;
|
|
DceBinding = 0;
|
|
}
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-C: Deleted Association %x\n", this);
|
|
#endif
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_CASSOCIATION::SetContext (
|
|
IN PRPC_CLIENT_INTERFACE InterfaceDesired
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Interface - Supplies information describing the
|
|
interface to which we wish to bind.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
LRPC_IBINDING * IBinding;
|
|
RPC_STATUS RpcStatus;
|
|
|
|
CritSec.Enter();
|
|
|
|
// Try and find interface in already negotiated interfaces.
|
|
|
|
IBindings.Reset();
|
|
while ( (IBinding = IBindings.Next()) != 0 ) {
|
|
if ( IBinding->Compare(InterfaceDesired) == 0 ) {
|
|
RpcStatus = RPC_S_OK;
|
|
goto finish_up;
|
|
}
|
|
}
|
|
|
|
// Negotiate a new interface, and add to set.
|
|
|
|
RpcStatus = IBind(InterfaceDesired, &IBinding);
|
|
if (RpcStatus != RPC_S_OK) {
|
|
CritSec.Leave();
|
|
return (RpcStatus);
|
|
}
|
|
|
|
finish_up:
|
|
|
|
// Set association current interface context.
|
|
|
|
this->IBinding = IBinding;
|
|
|
|
CritSec.Leave();
|
|
|
|
return(RpcStatus);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_CASSOCIATION::IBind (
|
|
IN PRPC_CLIENT_INTERFACE InterfaceDesired,
|
|
OUT LRPC_IBINDING ** IBinding
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Interface - Supplies information describing the interface
|
|
to which we wish to bind.
|
|
|
|
IBinding - Returns an object representing the binding to the interface
|
|
described by the first argument.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
DWORD ActualSize;
|
|
RPC_STATUS RpcStatus;
|
|
LRPC_BIND_MESSAGE Bind;
|
|
|
|
// send the bind request message to the
|
|
// server, and then wait for the bind response.
|
|
|
|
Bind.MessageType = LRPC_MSG_BIND;
|
|
Bind.BindExchange.InterfaceId = InterfaceDesired->InterfaceId;
|
|
Bind.BindExchange.TransferSyntax = InterfaceDesired->TransferSyntax;
|
|
|
|
RpcStatus = LpcClientPort->Transceive((LPVOID)&Bind,
|
|
sizeof(Bind),
|
|
0,
|
|
0,
|
|
(LPVOID)&Bind,
|
|
sizeof(Bind),
|
|
&ActualSize,
|
|
0,
|
|
0);
|
|
|
|
if (RpcStatus != RPC_S_OK) {
|
|
return (RpcStatus);
|
|
}
|
|
|
|
ASSERT( Bind.MessageType == LRPC_MSG_BIND );
|
|
|
|
if (Bind.BindExchange.RpcStatus != RPC_S_OK) {
|
|
return (Bind.BindExchange.RpcStatus);
|
|
}
|
|
|
|
// Create new interface.
|
|
|
|
*IBinding = new LRPC_IBINDING(InterfaceDesired);
|
|
if ( *IBinding == 0 ) {
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
(*IBinding)->ContextId = Bind.BindExchange.PresentationContext;
|
|
|
|
// Add new interface to association.
|
|
|
|
if ( IBindings.Insert(*IBinding) != (*IBinding)->ContextId ) {
|
|
delete *IBinding;
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_CASSOCIATION::OpenLpcPort (
|
|
int Timeout
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete the
|
|
operation.
|
|
|
|
Notes:
|
|
|
|
The global mutex will be held when this routine is called.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
LpcClientPort = new LPC_CLIENT_PORT;
|
|
|
|
if (LpcClientPort == NULL) {
|
|
return (RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
ASSERT(DceBinding != NULL);
|
|
|
|
retry:
|
|
|
|
RpcStatus = LpcClientPort->Connect((const char*)DceBinding->InqEndpoint());
|
|
if (RpcStatus != RPC_S_OK) {
|
|
if (RpcStatus == RPC_S_SERVER_UNAVAILABLE &&
|
|
Timeout == RPC_C_BINDING_INFINITE_TIMEOUT) {
|
|
goto retry;
|
|
}
|
|
LpcClientPort->Dereference();
|
|
LpcClientPort = NULL;
|
|
return (RPC_S_SERVER_UNAVAILABLE);
|
|
}
|
|
|
|
return (RPC_S_OK);
|
|
}
|
|
|
|
|
|
void
|
|
ShutdownLrpcClient (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will get called when the process which is using this dll
|
|
exits. We will go through and notify any servers that we are going
|
|
away.
|
|
|
|
--*/
|
|
{
|
|
LRPC_ASSOC_GROUP * Group;
|
|
|
|
if (AssocGroups != NULL) {
|
|
AssocGroups->Reset();
|
|
while ((Group = AssocGroups->Next()) != 0) {
|
|
delete Group;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
LRPC_CASSOCIATION::AbortAssociation (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This association needs to be aborted because a the server side of the
|
|
lpc port has been closed.
|
|
|
|
--*/
|
|
{
|
|
LRPC_IBINDING * IBinding;
|
|
|
|
CritSec.Enter();
|
|
|
|
CloseLpcClientPort();
|
|
|
|
// Delete all negotiated interfaces.
|
|
|
|
IBindings.Reset();
|
|
while ( (IBinding = IBindings.Next()) != 0 ) {
|
|
IBindings.Delete(IBinding->ContextId);
|
|
delete IBinding;
|
|
}
|
|
|
|
CritSec.Leave();
|
|
}
|
|
|
|
void
|
|
LRPC_CASSOCIATION::CloseLpcClientPort (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LpcClientPort will be closed (and a close message sent to the server).
|
|
|
|
--*/
|
|
{
|
|
LRPC_CLOSE_MESSAGE Close;
|
|
|
|
if (LpcClientPort) {
|
|
Close.MessageType = LRPC_MSG_CLOSE;
|
|
LpcClientPort->Send(&Close, sizeof(Close), 0, 0);
|
|
LpcClientPort->Disconnect();
|
|
LpcClientPort->Dereference();
|
|
LpcClientPort = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_CASSOCIATION::GetBuffer (
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We will allocate a buffer which will be used to either send a request
|
|
or receive a response.
|
|
|
|
Arguments:
|
|
|
|
Message - Supplies the length of the buffer that is needed. The buffer
|
|
will be returned.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - A buffer has been successfully allocated. It will be of at
|
|
least the required length.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate that
|
|
large a buffer.
|
|
|
|
--*/
|
|
{
|
|
Message->Handle = this;
|
|
Message->Buffer = LpcClientPort->GetBuffer(Message->BufferLength);
|
|
if (Message->Buffer == NULL) {
|
|
return (RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
AddRef();
|
|
|
|
return (RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_CASSOCIATION::SendReceive (
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
Message - Supplies the request and returns the response of a remote
|
|
procedure call.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - The remote procedure call completed successful.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
|
|
remote procedure call.
|
|
|
|
RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to complete
|
|
the remote procedure call.
|
|
|
|
--*/
|
|
{
|
|
DWORD ActualSize;
|
|
RPC_STATUS ExceptionCode, RpcStatus;
|
|
RPC_MESSAGE OriginalMessage;
|
|
void * SavedBuffer;
|
|
LRPC_RPC_MESSAGE Rpc;
|
|
LRPC_MESSAGE Any;
|
|
|
|
ASSERT(Message != NULL);
|
|
|
|
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
|
|
|
|
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
|
|
|
|
Rpc.RpcHeader.MessageType = LRPC_MSG_REQUEST;
|
|
Rpc.RpcHeader.ProcedureNumber = Message->ProcNum;
|
|
Rpc.RpcHeader.PresentationContext = IBinding->ContextId;
|
|
|
|
ASSERT(CurrentBindingHandle != NULL);
|
|
|
|
if (CurrentBindingHandle->InqIfNullObjectUuid() == 0 && CallbackDepth == 0 ) {
|
|
CurrentBindingHandle->InquireObjectUuid(
|
|
(RPC_UUID *) &(Rpc.RpcHeader.ObjectUuid));
|
|
Rpc.RpcHeader.ObjectUuidFlag = 1;
|
|
} else {
|
|
Rpc.RpcHeader.ObjectUuidFlag = 0;
|
|
}
|
|
|
|
ASSERT(LpcClientPort != 0);
|
|
|
|
ASSERT(Message->Buffer != NULL);
|
|
|
|
RpcStatus = LpcClientPort->Transceive((LPVOID)&Rpc,
|
|
sizeof(Rpc),
|
|
Message->Buffer,
|
|
Message->BufferLength,
|
|
&Any,
|
|
sizeof(Any),
|
|
&ActualSize,
|
|
&Message->Buffer,
|
|
(PDWORD)&Message->BufferLength);
|
|
|
|
if (RpcStatus == RPC_P_CONNECTION_CLOSED) {
|
|
AbortAssociation();
|
|
RpcStatus = OpenLpcPort(CurrentBindingHandle->InqComTimeout());
|
|
if (RpcStatus != RPC_S_OK) {
|
|
return (RPC_S_CALL_FAILED);
|
|
}
|
|
RpcStatus = SetContext((PRPC_CLIENT_INTERFACE)Message->RpcInterfaceInformation);
|
|
if (RpcStatus != RPC_S_OK) {
|
|
return (RPC_S_CALL_FAILED);
|
|
}
|
|
ASSERT(LpcClientPort != NULL);
|
|
RpcStatus = LpcClientPort->Transceive((LPVOID)&Rpc,
|
|
sizeof(Rpc),
|
|
Message->Buffer,
|
|
Message->BufferLength,
|
|
&Any,
|
|
sizeof(Any),
|
|
&ActualSize,
|
|
&Message->Buffer,
|
|
(PDWORD)&Message->BufferLength);
|
|
|
|
}
|
|
|
|
if (RpcStatus != RPC_S_OK) {
|
|
if (RpcStatus == RPC_S_OUT_OF_MEMORY) {
|
|
return (RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
return (RPC_S_CALL_FAILED);
|
|
}
|
|
|
|
while (1) {
|
|
if ( Any.Rpc.RpcHeader.MessageType == LRPC_MSG_FAULT ) {
|
|
RpcStatus = Any.Fault.RpcStatus;
|
|
break;
|
|
}
|
|
|
|
if ( Any.Rpc.RpcHeader.MessageType == LRPC_MSG_RESPONSE ) {
|
|
RpcStatus = RPC_S_OK;
|
|
break;
|
|
}
|
|
|
|
ASSERT( Any.Rpc.RpcHeader.MessageType == LRPC_MSG_REQUEST );
|
|
|
|
CallbackDepth++;
|
|
|
|
OriginalMessage = *Message;
|
|
|
|
Message->TransferSyntax = 0;
|
|
Message->ProcNum = Any.Rpc.RpcHeader.ProcedureNumber;
|
|
|
|
ASSERT(IBinding != NULL);
|
|
|
|
RpcStatus = DispatchCallback((PRPC_DISPATCH_TABLE)
|
|
IBinding->Interface.DispatchTable, Message,
|
|
&ExceptionCode);
|
|
|
|
if ( RpcStatus != RPC_S_OK ) {
|
|
ASSERT( ( RpcStatus == RPC_P_EXCEPTION_OCCURED )
|
|
|| ( RpcStatus == RPC_S_PROCNUM_OUT_OF_RANGE ) );
|
|
|
|
if ( RpcStatus == RPC_S_PROCNUM_OUT_OF_RANGE ) {
|
|
Any.Fault.RpcStatus = RPC_S_PROCNUM_OUT_OF_RANGE;
|
|
} else {
|
|
Any.Fault.RpcStatus = ExceptionCode;
|
|
}
|
|
Any.Fault.MessageType = LRPC_MSG_FAULT;
|
|
} else {
|
|
Any.Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE;
|
|
}
|
|
|
|
if (LpcClientPort == NULL) { // If server died, Port could be NULL here
|
|
return (RPC_S_CALL_FAILED);
|
|
}
|
|
|
|
RpcStatus = LpcClientPort->Transceive(&Any,
|
|
sizeof(LRPC_RPC_MESSAGE),
|
|
Message->Buffer,
|
|
Message->BufferLength,
|
|
&Any,
|
|
sizeof(Any),
|
|
&ActualSize,
|
|
&Message->Buffer,
|
|
(PDWORD)&Message->BufferLength);
|
|
|
|
CallbackDepth--;
|
|
|
|
if (RpcStatus != RPC_S_OK) {
|
|
if ( RpcStatus != RPC_S_OUT_OF_MEMORY) {
|
|
return (RPC_S_CALL_FAILED);
|
|
} else {
|
|
return (RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CallbackDepth == 0) {
|
|
Thread = 0; // Clearing thread will make association available.
|
|
CurrentBindingHandle = 0;
|
|
}
|
|
|
|
return(RpcStatus);
|
|
}
|
|
|
|
|
|
void
|
|
LRPC_CASSOCIATION::FreeBuffer (
|
|
IN PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We will free the supplied buffer.
|
|
|
|
Arguments:
|
|
|
|
Message - Supplies the buffer to be freed.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(Message->Buffer != NULL);
|
|
|
|
LpcClientPort->FreeBuffer(Message->Buffer);
|
|
|
|
Dereference();
|
|
}
|
|
|
|
int
|
|
LRPC_CASSOCIATION::IsIdle(
|
|
)
|
|
{
|
|
CritSec.Enter();
|
|
|
|
// If association is not allocated, grab it.
|
|
// This must be an atomic operation.
|
|
// NOTE: Only one thread can use an association at one time.
|
|
|
|
if ( Thread == 0) {
|
|
Thread = GetThreadIdentifier();
|
|
CritSec.Leave();
|
|
return (1);
|
|
}
|
|
|
|
CritSec.Leave();
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
LRPC_CASSOCIATION::AddRef(
|
|
)
|
|
{
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-C: Association::AddRef %x\n", this);
|
|
#endif
|
|
InterlockedIncrement(&ReferenceCount);
|
|
}
|
|
|
|
void
|
|
LRPC_CASSOCIATION::Dereference(
|
|
)
|
|
{
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-C: Association::Dereference %x\r\n", this);
|
|
#endif
|
|
if (InterlockedDecrement(&ReferenceCount) == 0) {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
LRPC_ASSOC_GROUP::LRPC_ASSOC_GROUP(
|
|
DCE_BINDING * DceBinding
|
|
)
|
|
{
|
|
ASSERT(AssocGroups != NULL);
|
|
|
|
this->DceBinding = DceBinding->DuplicateDceBinding();
|
|
|
|
ASSERT(this->DceBinding != NULL);
|
|
|
|
ReferenceCount = 1;
|
|
|
|
GlobalMutexRequest();
|
|
AssocGroupKey = AssocGroups->Insert(this);
|
|
GlobalMutexClear();
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-C: Created AssocGroup %x\n", this);
|
|
#endif
|
|
}
|
|
|
|
LRPC_ASSOC_GROUP::~LRPC_ASSOC_GROUP(
|
|
)
|
|
{
|
|
int NumAssocs = 0;
|
|
LRPC_CASSOCIATION * Association;
|
|
|
|
ASSERT(AssocGroups != NULL);
|
|
|
|
GlobalMutexRequest();
|
|
AssocGroups->Delete(AssocGroupKey);
|
|
GlobalMutexClear();
|
|
|
|
ASSERT(DceBinding != NULL);
|
|
|
|
delete DceBinding;
|
|
DceBinding = 0;
|
|
|
|
CritSec.Enter();
|
|
Associations.Reset();
|
|
while ( (Association = Associations.Next()) != 0 ) {
|
|
NumAssocs++;
|
|
Association->Dereference();
|
|
}
|
|
CritSec.Leave();
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-C: Deleted AssocGroup %x (%d Assocs)\r\n", this, NumAssocs);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
LRPC_ASSOC_GROUP::AddRef(
|
|
)
|
|
{
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-C: AssocGroup::AddRef %x\n", this);
|
|
#endif
|
|
InterlockedIncrement(&ReferenceCount);
|
|
}
|
|
|
|
void
|
|
LRPC_ASSOC_GROUP::Dereference(
|
|
)
|
|
{
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-C: AssocGroup::Dereference %x\r\n", this);
|
|
#endif
|
|
if (InterlockedDecrement(&ReferenceCount) == 0) {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
LRPC_CASSOCIATION *
|
|
LRPC_ASSOC_GROUP::FindActiveAssociation (
|
|
PRPC_CLIENT_INTERFACE Interface
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
LRPC_CASSOCIATION * Association;
|
|
|
|
ASSERT(Interface != NULL);
|
|
|
|
CritSec.Enter();
|
|
|
|
// Try and find an existing association for callbacks. Yuck.
|
|
|
|
Associations.Reset();
|
|
|
|
while ( (Association = Associations.Next()) != 0 ) {
|
|
|
|
if (Association->IBinding &&
|
|
Association->Thread == GetThreadIdentifier() &&
|
|
Association->IBinding->Compare(Interface) == 0 &&
|
|
Association->CallbackDepth > 0) {
|
|
|
|
CritSec.Leave();
|
|
|
|
return(Association);
|
|
}
|
|
}
|
|
|
|
CritSec.Leave();
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
RPC_STATUS
|
|
LRPC_ASSOC_GROUP::AllocateAssociation(
|
|
PRPC_CLIENT_INTERFACE Interface,
|
|
LRPC_CASSOCIATION ** ppAssociation,
|
|
int Timeout
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
LRPC_CASSOCIATION * Association = NULL;
|
|
|
|
CritSec.Enter();
|
|
|
|
// Try to find an idle association in the association group.
|
|
|
|
Associations.Reset();
|
|
while ( (Association = Associations.Next()) != 0 ) {
|
|
if (Association->IsIdle()) {
|
|
goto set_context;
|
|
}
|
|
}
|
|
|
|
// Create a new association if no previous found.
|
|
|
|
Association = new LRPC_CASSOCIATION(DceBinding);
|
|
if (Association == NULL) {
|
|
CritSec.Leave();
|
|
return (RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
Association->IsIdle();
|
|
|
|
if ((RpcStatus = Association->OpenLpcPort(Timeout)) != RPC_S_OK) {
|
|
delete Association;
|
|
CritSec.Leave();
|
|
return (RpcStatus);
|
|
}
|
|
|
|
// Insert new association in association group.
|
|
|
|
Association->AssociationDictKey = Associations.Insert(Association);
|
|
if ( Association->AssociationDictKey == -1 ) {
|
|
delete Association;
|
|
CritSec.Leave();
|
|
return (RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
set_context:
|
|
|
|
RpcStatus = Association->SetContext(Interface);
|
|
|
|
if (RpcStatus != RPC_S_OK) {
|
|
CritSec.Leave();
|
|
return (RpcStatus);
|
|
}
|
|
|
|
*ppAssociation = Association;
|
|
|
|
CritSec.Leave();
|
|
|
|
return (RPC_S_OK);
|
|
}
|
|
|
|
|
|
BINDING_HANDLE *
|
|
SpcCreateBindingHandle (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We just need to create a new LRPC_BINDING_HANDLE. This routine is a
|
|
proxy for the new constructor to isolate the other modules.
|
|
|
|
--*/
|
|
{
|
|
LRPC_BINDING_HANDLE * BindingHandle;
|
|
|
|
BindingHandle = new LRPC_BINDING_HANDLE();
|
|
if (BindingHandle == NULL) {
|
|
return (NULL);
|
|
}
|
|
|
|
return(BindingHandle);
|
|
}
|
|
|
|
|
|
int
|
|
InitializeRpcProtocolSPC (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For each process, this routine will be called once. All initialization
|
|
will be done here.
|
|
|
|
Return Value:
|
|
|
|
Zero will be returned if initialization completes successfully,
|
|
otherwise, non-zero will be returned.
|
|
|
|
--*/
|
|
{
|
|
|
|
ASSERT(AssocGroups == NULL);
|
|
|
|
AssocGroups = new LRPC_ASSOC_GROUP_DICT;
|
|
if ( AssocGroups == 0 ) {
|
|
#ifdef DEBUGRPC
|
|
PrintToDebugger("LRPC-C: Failed to initialize\n");
|
|
#endif
|
|
return(1);
|
|
}
|
|
|
|
return(0);
|
|
}
|