Leaked source code of windows server 2003
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.
 
 
 
 
 
 

6612 lines
193 KiB

/*++
Copyright (C) Microsoft Corporation, 1992 - 1999
Module Name:
lpcclnt.cxx
Abstract:
Implementation of the RPC on LPC protocol engine for the client.
Revision History:
Mazhar Mohammed: Code fork from spcclnt.cxx, 08/02/95
Tony Chan: Added Singled Security Model, 12/15/95
Mazhar Mohammed Merged WMSG and LRPC into a single protocol 05-06-96
Mazhar Mohammed Added Pipes Support
Mazhar Mohammed Added support for Async RPC 08-14-96
Kamen Moutafov (KamenM) Jan-2000 Support for multiple transfer syntaxes
Kamen Moutafov (KamenM) Dec 99 - Feb 2000 Support for cell debugging stuff
Kamen Moutafov (KamenM) Mar-2000 Support for extended error info
--*/
#include <precomp.hxx>
#include <limits.h>
#include <rpcqos.h>
#include <queue.hxx>
#include <lpcpack.hxx>
#include <hndlsvr.hxx>
#include <lpcsvr.hxx>
#include <ProtBind.hxx>
#include <lpcclnt.hxx>
#include <epmap.h>
#include <sidcache.hxx>
#include <CharConv.hxx>
// These need to be the same because client and server will be receiving lpc
// reply messages into LRPC_MESSAGE structures and they must have sufficient space.
C_ASSERT(sizeof(LRPC_MESSAGE) == PORT_MAXIMUM_MESSAGE_LENGTH);
// This is a sanity check to make sure as we modify the format of the
// bind message we leave enough space for the old security contexts.
C_ASSERT(MAX_LRPC_CONTEXTS >= 8);
const SECURITY_IMPERSONATION_LEVEL RpcToNtImp[] =
{
// RPC_C_IMP_LEVEL_DEFAULT
SecurityImpersonation,
// RPC_C_IMP_LEVEL_ANONYMOUS
SecurityAnonymous,
// RPC_C_IMP_LEVEL_IDENTIFY
SecurityIdentification,
//RPC_C_IMP_LEVEL_IMPERSONATE
SecurityImpersonation,
//RPC_C_IMP_LEVEL_DELEGATE
SecurityDelegation
};
const unsigned long NtToRpcImp[] =
{
//SecurityAnonymous,
RPC_C_IMP_LEVEL_ANONYMOUS,
//SecurityIdentification,
RPC_C_IMP_LEVEL_IDENTIFY,
// SecurityImpersonation,
RPC_C_IMP_LEVEL_IMPERSONATE,
//SecurityDelegation
RPC_C_IMP_LEVEL_DELEGATE
};
SECURITY_IMPERSONATION_LEVEL
MapRpcToNtImp (
IN unsigned long ImpersonationType
)
{
if (ImpersonationType <= RPC_C_IMP_LEVEL_DELEGATE)
{
return RpcToNtImp[ImpersonationType];
}
ASSERT(0) ;
return SecurityImpersonation ;
}
unsigned long
MapNtToRpcImp (
IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
)
{
if (ImpersonationLevel <= SecurityDelegation)
{
return NtToRpcImp[ImpersonationLevel];
}
ASSERT(0);
return RPC_C_IMP_LEVEL_IMPERSONATE;
}
RPC_STATUS
InitializeLrpcIfNecessary(
) ;
RPC_STATUS
InitializeAsyncLrpcIfNecessary (
)
/*++
Routine Description:
We need to perform the required initialization for Async RPC to
work. If we currently don't have a listening thread. We need to
add a note in the docs that if the app ever plans to start a
listening thread on the client (ie: become a server),
it should do it before it makes the first Async RPC call. This is
not a requirement, it is just an effeciency consideration.
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status;
Status = InitializeLrpcIfNecessary();
if (Status != RPC_S_OK)
{
#if DBG
PrintToDebugger("LRPC: InitializeLrpcIfNecessary failed: %x\n", Status) ;
#endif
return RPC_S_OUT_OF_MEMORY ;
}
return GlobalLrpcServer->InitializeAsync();
}
LRPC_BINDING_HANDLE::LRPC_BINDING_HANDLE (
OUT RPC_STATUS * Status
) : BINDING_HANDLE (Status),
BindingReferenceCount(1)
/*++
Routine Description:
We just allocate an LRPC_BINDING_HANDLE and initialize things so that
we can use it later.
Arguments:
Status - Returns the result of initializing the binding mutex.
--*/
{
BindingMutex.SetSpinCount(4000);
ObjectType = LRPC_BINDING_HANDLE_TYPE;
CurrentAssociation = 0;
DceBinding = 0;
AuthInfoInitialized = 0;
StaticTokenHandle = 0;
EffectiveOnly = TRUE;
}
LRPC_BINDING_HANDLE::~LRPC_BINDING_HANDLE (
)
/*++
--*/
{
LRPC_CASSOCIATION *Association;
DictionaryCursor cursor;
if (SecAssociation.Size() != 0)
{
SecAssociation.Reset(cursor);
while ((Association = SecAssociation.Next(cursor)) != 0)
{
if (Association != 0)
{
// take away from the bindinghandle dictionary
RemoveAssociation(Association);
// take away from the global dict
Association->RemoveBindingHandleReference();
}
}
}
delete DceBinding;
if (StaticTokenHandle)
{
CloseHandle(StaticTokenHandle);
}
}
RPC_STATUS
LRPC_BINDING_HANDLE::NegotiateTransferSyntax (
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
Arguments:
Message - Supplies the length of the buffer required, and returns the
new buffer.
Return Value:
--*/
{
LRPC_CCALL * CCall;
RPC_STATUS Status;
int RetryCount = 0;
static long nInitialized = -1 ;
LRPC_CASSOCIATION *Association ;
DictionaryCursor cursor;
for (;;)
{
for (;;)
{
Status = AllocateCCall(&CCall, (RPC_CLIENT_INTERFACE *)
Message->RpcInterfaceInformation,
Message);
if (Status != RPC_S_SERVER_UNAVAILABLE)
{
break;
}
if (!fDynamicEndpoint)
{
break;
}
// If we reach here, it means that we are iterating through the
// list of endpoints obtained from the endpoint mapper.
BindingMutex.Request() ;
if (BindingReferenceCount.GetInteger() == 1)
{
if (SecAssociation.Size() != 0)
{
DceBinding = CurrentAssociation->DuplicateDceBinding();
if(DceBinding == 0)
{
BindingMutex.Clear() ;
return(RPC_S_OUT_OF_MEMORY);
}
CurrentAssociation = 0;
DceBinding->MaybeMakePartiallyBound(
(PRPC_CLIENT_INTERFACE)Message->RpcInterfaceInformation,
InqPointerAtObjectUuid());
if ( *InquireEpLookupHandle() != 0 )
{
EpFreeLookupHandle(*InquireEpLookupHandle());
*InquireEpLookupHandle() = 0;
}
// remove references
SecAssociation.Reset(cursor);
while((Association = SecAssociation.Next(cursor)) != 0)
{
if (Association != 0)
{
// in the AssociationDict all DceBinding should be the same
// may be we can take out this line. or remove ref
// on the first Association
RemoveAssociation(Association);
Association->RemoveReference();
}
}
}
}
BindingMutex.Clear() ;
RetryCount ++;
if (RetryCount > 2)
{
break;
}
RpcpPurgeEEInfo();
}
if (Status == RPC_S_OK)
{
break;
}
if (InqComTimeout() != RPC_C_BINDING_INFINITE_TIMEOUT)
{
return(Status);
}
if ((Status != RPC_S_SERVER_UNAVAILABLE)
&& (Status != RPC_S_SERVER_TOO_BUSY))
{
return(Status);
}
}
Message->TransferSyntax = CCall->Binding->GetTransferSyntaxId();
Message->Handle = CCall;
return RPC_S_OK;
}
RPC_STATUS
LRPC_BINDING_HANDLE::GetBuffer (
IN OUT PRPC_MESSAGE Message,
IN UUID *ObjectUuid
)
/*++
Routine Description:
Arguments:
Message - Supplies the length of the buffer required, and returns the
new buffer.
Return Value:
--*/
{
ASSERT(!"We should never be here - the binding handle cannot allocate a buffer");
return RPC_S_INTERNAL_ERROR;
}
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.
--*/
{
RPC_STATUS Status = RPC_S_OK;
LRPC_BINDING_HANDLE * NewBindingHandle;
CLIENT_AUTH_INFO * AuthInfo;
LRPC_CASSOCIATION *SecAssoc;
DictionaryCursor cursor;
int Key;
UNUSED(MaintainContext);
NewBindingHandle = new LRPC_BINDING_HANDLE(&Status);
if (NewBindingHandle == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
if (Status != RPC_S_OK)
{
delete NewBindingHandle;
return(Status);
}
BindingMutex.Request();
Status = NewBindingHandle->BINDING_HANDLE::BaseClone(this);
if (Status != RPC_S_OK)
{
delete NewBindingHandle;
BindingMutex.Clear();
return(Status);
}
NewBindingHandle->fDynamicEndpoint = fDynamicEndpoint;
if ((AuthInfo = InquireAuthInformation()) != 0)
{
Status = NewBindingHandle->SetAuthInformation(
AuthInfo->ServerPrincipalName,
AuthInfo->AuthenticationLevel,
AuthInfo->AuthenticationService,
NULL,
AuthInfo->AuthorizationService,
0,
AuthInfo->ImpersonationType,
AuthInfo->IdentityTracking,
AuthInfo->Capabilities,
FALSE, // bAcquireNewCredentials
AuthInfo->AdditionalTransportCredentialsType,
AuthInfo->AdditionalCredentials,
AuthInfo->ServerSid
);
if (Status != RPC_S_OK)
{
ASSERT (Status == RPC_S_OUT_OF_MEMORY);
delete NewBindingHandle;
BindingMutex.Clear();
return(RPC_S_OUT_OF_MEMORY);
}
}
if (SecAssociation.Size() == 0)
{
NewBindingHandle->DceBinding = DceBinding->DuplicateDceBinding();
if (NewBindingHandle->DceBinding == 0)
{
BindingMutex.Clear() ;
delete NewBindingHandle;
return(RPC_S_OUT_OF_MEMORY);
}
}
else
{
// copy all sec associations
SecAssociation.Reset(cursor);
while((SecAssoc = SecAssociation.Next(cursor)) != 0)
{
Key = NewBindingHandle->AddAssociation(SecAssoc);
if (Key == -1)
{
BindingMutex.Clear() ;
delete NewBindingHandle;
return (RPC_S_OUT_OF_MEMORY);
}
SecAssoc->DuplicateAssociation();
}
// since the CurrentAssociation is in the SecAssociation dictionary,
// it should have already been copied. Just assign it
NewBindingHandle->CurrentAssociation = CurrentAssociation;
}
BindingMutex.Clear();
*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.
--*/
{
int LocalRefCount;
LocalRefCount = BindingReferenceCount.Decrement();
if (LocalRefCount == 0)
{
delete this;
}
return(RPC_S_OK);
}
RPC_STATUS
LRPC_BINDING_HANDLE::PrepareBindingHandle (
IN TRANS_INFO * 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.
--*/
{
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
RPC_STATUS Status;
UNUSED(TransportInformation);
if (DceBinding->InqNetworkOptions() != 0 &&
DceBinding->InqNetworkOptions()[0] != 0)
{
Status = I_RpcParseSecurity(DceBinding->InqNetworkOptions(),
&SecurityQualityOfService);
if (Status != RPC_S_OK)
{
ASSERT(Status == RPC_S_INVALID_NETWORK_OPTIONS);
return(Status);
}
Status = SetAuthInformation(NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_AUTHN_WINNT,
NULL,
NULL,
0,
MapNtToRpcImp(SecurityQualityOfService.ImpersonationLevel),
SecurityQualityOfService.ContextTrackingMode,
RPC_C_QOS_CAPABILITIES_DEFAULT,
TRUE);
if (Status != RPC_S_OK)
{
return Status;
}
EffectiveOnly = SecurityQualityOfService.EffectiveOnly;
}
this->DceBinding = DceBinding;
fDynamicEndpoint = DceBinding->IsNullEndpoint();
return RPC_S_OK;
}
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.
--*/
{
if (CurrentAssociation == 0)
{
*StringBinding = DceBinding->StringBindingCompose(
InqPointerAtObjectUuid());
}
else
{
*StringBinding = CurrentAssociation->StringBindingCompose(
InqPointerAtObjectUuid());
}
if (*StringBinding == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
return(RPC_S_OK);
}
RPC_STATUS
LRPC_BINDING_HANDLE::ResolveBinding (
IN RPC_CLIENT_INTERFACE * RpcClientInterface
)
/*++
Routine Description:
We need to try and resolve the endpoint for this binding handle
if necessary (the binding handle is partially-bound). If there is
isn't a association allocated, call the binding management routines
to do it.
Arguments:
RpcClientInterface - 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 Status;
if (CurrentAssociation == 0)
{
BindingMutex.Request();
Status = DceBinding->ResolveEndpointIfNecessary(
RpcClientInterface,
InqPointerAtObjectUuid(),
InquireEpLookupHandle(),
FALSE,
InqComTimeout(),
INFINITE, // CallTimeout
NULL // AuthInfo
);
BindingMutex.Clear();
return(Status);
}
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.
--*/
{
LRPC_CASSOCIATION *Association ;
DictionaryCursor cursor;
BindingMutex.Request() ;
if (CurrentAssociation != 0)
{
if (BindingReferenceCount.GetInteger() != 1)
{
BindingMutex.Clear() ;
return(RPC_S_WRONG_KIND_OF_BINDING);
}
DceBinding = CurrentAssociation->DuplicateDceBinding();
if(DceBinding == 0)
{
BindingMutex.Clear() ;
return(RPC_S_OUT_OF_MEMORY);
}
CurrentAssociation = 0;
SecAssociation.Reset(cursor);
while((Association = SecAssociation.Next(cursor)) != 0)
{
RemoveAssociation(Association);
Association->RemoveBindingHandleReference();
}
}
fDynamicEndpoint = TRUE;
DceBinding->MakePartiallyBound();
if (*InquireEpLookupHandle() != 0)
{
EpFreeLookupHandle(*InquireEpLookupHandle());
*InquireEpLookupHandle() = 0;
}
BindingMutex.Clear() ;
return(RPC_S_OK);
}
void
LRPC_BINDING_HANDLE::FreeCCall (
IN LRPC_CCALL * CCall
)
/*++
Routine Description:
This routine will get called to notify this binding handle that a remote
procedure call on this binding handle has completed.
Arguments:
CCall - Supplies the remote procedure call which has completed.
--*/
{
int LocalRefCount;
CCall->InqAssociation()->FreeCCall(CCall);
// do not touch the association beyond this. It could be freed.
LocalRefCount = BindingReferenceCount.Decrement();
if (LocalRefCount == 0)
{
delete this;
}
}
RPC_STATUS
LRPC_BINDING_HANDLE::AllocateCCall (
OUT LRPC_CCALL ** CCall,
IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation,
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
This method will allocate an LRPC_CCALL which has been bound to the
interface specified by the interface information. First, we have got
to see if we have an association for this binding. If not, we need
to find or create one. Before we can find or create an association,
we need to resolve the endpoint if necessary. Next we need to see
if there is already an LRPC_CCALL allocated for this interface and
thread. Otherwise, we need to ask the association to allocate a
LRPC_CCALL for us.
Arguments:
CCall - Returns the allocated LRPC_CCALL which has been bound to
the interface specified by the rpc interface information.
RpcInterfaceInformation - Supplies information describing the
interface to which we wish to bind.
Return Value:
--*/
{
RPC_STATUS Status;
BOOL Boolean;
BOOL FoundSameAuthInfo = FALSE;
LRPC_CASSOCIATION * Association;
LRPC_CASSOCIATION *MyAssociation = NULL;
DictionaryCursor cursor;
int LocalRefCount;
BOOL Supported;
BindingMutex.Request();
if (AuthInfoInitialized == 0)
{
Status = SetAuthInformation(NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_AUTHN_WINNT,
NULL,
NULL,
0,
RPC_C_IMP_LEVEL_IMPERSONATE,
RPC_C_QOS_IDENTITY_DYNAMIC,
RPC_C_QOS_CAPABILITIES_DEFAULT,
TRUE);
if (Status != RPC_S_OK)
{
BindingMutex.Clear();
return Status;
}
}
// First we need to check if there is already a call active for this
// thread and interface. To make the common case quicker, we will check
// to see if there are any calls in the dictionary first.
if (RecursiveCalls.Size() != 0)
{
RecursiveCalls.Reset(cursor);
while ((*CCall = RecursiveCalls.Next(cursor)) != 0)
{
if ((*CCall)->IsThisMyActiveCall(
GetThreadIdentifier(),
RpcInterfaceInformation) != 0)
{
BindingMutex.Clear();
return(RPC_S_OK);
}
}
}
// To start off, see if the binding handle points to an association
// yet. If not, we have got to get one.
if (CurrentAssociation == 0)
{
// 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->InqNetworkAddress() != 0);
if (DceBinding->InqNetworkAddress()[0] != 0)
{
if (RpcpStringCompareInt(DceBinding->InqNetworkAddress(),
gLocalComputerName) != 0)
{
BindingMutex.Clear();
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_SERVER_UNAVAILABLE,
EEInfoDLLRPC_BINDING_HANDLE__AllocateCCall10,
DceBinding->InqNetworkAddress(),
gLocalComputerName);
return(RPC_S_SERVER_UNAVAILABLE);
}
}
if (DceBinding->IsNullEndpoint())
{
LrpcMutexRequest();
MyAssociation = FindOrCreateLrpcAssociation(
DceBinding,
InquireAuthInformation(),
RpcInterfaceInformation);
LrpcMutexClear();
// don't do anything in the both success and failure
// case. In failure case we'll try full endpoint resolution
// In success case, we leave FoundSameAuthInfo to be FALSE,
// and the code below will figure out we have something
// in MyAssociation and will do the housekeeping tasks
// associated with finding an association
ASSERT(DceBinding);
}
if (!MyAssociation)
{
Status = DceBinding->ResolveEndpointIfNecessary(
RpcInterfaceInformation,
InqPointerAtObjectUuid(),
InquireEpLookupHandle(),
FALSE,
InqComTimeout(),
INFINITE, // CallTimeout
&ClientAuthInfo // AuthInfo
);
if (Status != RPC_S_OK)
{
BindingMutex.Clear();
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLRPC_BINDING_HANDLE__AllocateCCall20,
RpcInterfaceInformation->InterfaceId.SyntaxGUID.Data1);
return(Status);
}
}
}
else
{
Status = CurrentAssociation->IsSupportedAuthInfo(InquireAuthInformation(),
&Supported);
if (Status != RPC_S_OK)
{
BindingMutex.Clear();
return(Status);
}
if (Supported)
{
MyAssociation = CurrentAssociation ;
FoundSameAuthInfo = TRUE;
}
else
{
SecAssociation.Reset(cursor);
while ((Association = SecAssociation.Next(cursor)) != 0)
{
Status = Association->IsSupportedAuthInfo(InquireAuthInformation(),
&Supported);
if (Status != RPC_S_OK)
{
BindingMutex.Clear();
return(Status);
}
if(Supported)
{
MyAssociation = Association ;
FoundSameAuthInfo = TRUE;
break;
}
}
}
}
if (FoundSameAuthInfo == FALSE)
{
// we have some association in the dictionary, check for security level
if (DceBinding == 0)
{
SecAssociation.Reset(cursor);
Association = SecAssociation.Next(cursor);
// it will get deleted when Assoc goes
DceBinding = Association->DuplicateDceBinding();
if(DceBinding == 0)
{
BindingMutex.Clear() ;
return(RPC_S_OUT_OF_MEMORY);
}
}
// if we still haven't found the association
// (may do so during the interface based search for
// an endpoint).
if (!MyAssociation)
{
LrpcMutexRequest();
MyAssociation = FindOrCreateLrpcAssociation(
DceBinding,
InquireAuthInformation(),
NULL);
LrpcMutexClear();
}
if (CurrentAssociation == 0)
{
CurrentAssociation = MyAssociation ;
}
if (MyAssociation == 0)
{
BindingMutex.Clear();
return(RPC_S_OUT_OF_MEMORY);
}
// The association now owns the DceBinding.
DceBinding = 0;
if((AddAssociation(MyAssociation)) == -1)
{
delete MyAssociation;
if (CurrentAssociation == MyAssociation)
{
CurrentAssociation = 0;
}
BindingMutex.Clear();
return (RPC_S_OUT_OF_MEMORY);
}
}
BindingReferenceCount.Increment();
BindingMutex.Clear();
ASSERT(MyAssociation) ;
Status = MyAssociation->AllocateCCall(
this,
CCall,
Message,
RpcInterfaceInformation);
if (Status != RPC_S_OK)
{
LocalRefCount = BindingReferenceCount.Decrement();
ASSERT(LocalRefCount != 0);
}
return(Status);
}
RPC_STATUS
LRPC_BINDING_HANDLE::SetAuthInformation (
IN RPC_CHAR * ServerPrincipalName, OPTIONAL
IN unsigned long AuthenticationLevel,
IN unsigned long AuthenticationService,
IN RPC_AUTH_IDENTITY_HANDLE AuthIdentity, OPTIONAL
IN unsigned long AuthorizationService,
IN SECURITY_CREDENTIALS * Credentials,
IN unsigned long ImpersonationType,
IN unsigned long IdentityTracking,
IN unsigned long Capabilities,
IN BOOL bAcquireNewCredentials,
IN ULONG AdditionalTransportCredentialsType, OPTIONAL
IN void *AdditionalCredentials, OPTIONAL
IN PSID Sid OPTIONAL
)
/*++
Routine Description:
We set the authentication and authorization information in this binding
handle.
Arguments:
ServerPrincipalName - Optionally supplies the server principal name.
AuthenticationLevel - Supplies the authentication level to use.
AuthenticationService - Supplies the authentication service to use.
AuthIdentity - Optionally supplies the security context to use.
AuthorizationService - Supplies the authorization service to use.
AdditionalTransportCredentialsType - the type of additional credentials
supplied in AdditionalCredentials. Not supported for LRPC.
AdditionalCredentials - pointer to additional credentials if any. Not supported
for LRPC.
Sid - the SID to mutually authenticate to
Return Value:
RPC_S_OK - The supplied authentication and authorization information has
been set in the binding handle.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete the
operation.
RPC_S_UNKNOWN_AUTHN_SERVICE - The specified authentication service is
not supported.
RPC_S_UNKNOWN_AUTHN_LEVEL - The specified authentication level is
not supported.
RPC_S_INVALID_AUTH_IDENTITY - The specified security context (supplied
by the auth identity argument) is invalid.
RPC_S_UNKNOWN_AUTHZ_SERVICE - The specified authorization service is
not supported.
--*/
{
RPC_CHAR * NewString ;
RPC_STATUS Status;
HANDLE hToken;
unsigned long MappedAuthenticationLevel;
PSID ServerSid = NULL;
ULONG SidLength;
BOOL Result;
BOOL fCachedSid = TRUE;
if ((AdditionalTransportCredentialsType != 0) || (AdditionalCredentials != NULL))
return RPC_S_CANNOT_SUPPORT;
if (AuthenticationLevel == RPC_C_AUTHN_LEVEL_DEFAULT)
{
AuthenticationLevel = RPC_C_AUTHN_LEVEL_CONNECT;
}
MappedAuthenticationLevel = MapAuthenticationLevel(AuthenticationLevel);
if (AuthenticationLevel > RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
{
return(RPC_S_UNKNOWN_AUTHN_LEVEL);
}
ClientAuthInfo.AuthenticationLevel = MappedAuthenticationLevel;
ClientAuthInfo.AuthenticationService = AuthenticationService;
ClientAuthInfo.AuthIdentity = AuthIdentity;
ClientAuthInfo.AuthorizationService = AuthorizationService;
ClientAuthInfo.IdentityTracking = IdentityTracking;
if (MappedAuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE)
{
ClientAuthInfo.ImpersonationType = RPC_C_IMP_LEVEL_ANONYMOUS;
}
else
{
ClientAuthInfo.ImpersonationType = ImpersonationType;
}
if (AuthenticationService == RPC_C_AUTHN_NONE)
{
AuthInfoInitialized = 0;
return (RPC_S_OK);
}
if(AuthenticationService != RPC_C_AUTHN_WINNT)
{
return(RPC_S_UNKNOWN_AUTHN_SERVICE) ;
}
// For LRPC, MA implies local MA hint.
if (Capabilities & RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH)
{
Capabilities |= RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT;
// if the SPN is not translated to a SID, we must do that now
if (!ARGUMENT_PRESENT(Sid))
{
Status = RpcpLookupAccountName (ServerPrincipalName,
&fCachedSid,
&ServerSid);
if (Status != RPC_S_OK)
return Status;
Sid = ServerSid;
// fall through to SID processing code below
}
}
ClientAuthInfo.Capabilities = Capabilities;
if (ARGUMENT_PRESENT(Sid))
{
// duplicate if not already allocated
if (ServerSid == NULL)
{
ServerSid = DuplicateSID (Sid);
if (ServerSid == NULL)
return(RPC_S_OUT_OF_MEMORY);
}
BindingMutex.Request();
if ( ClientAuthInfo.ServerSid != 0 )
{
delete ClientAuthInfo.ServerSid;
}
ClientAuthInfo.ServerSid = ServerSid;
BindingMutex.Clear();
}
if (ARGUMENT_PRESENT(ServerPrincipalName) && (*ServerPrincipalName))
{
NewString = DuplicateString(ServerPrincipalName);
if ( NewString == 0 )
{
return(RPC_S_OUT_OF_MEMORY);
}
BindingMutex.Request();
if ( ClientAuthInfo.ServerPrincipalName != 0 )
{
delete ClientAuthInfo.ServerPrincipalName;
}
ClientAuthInfo.ServerPrincipalName = NewString;
BindingMutex.Clear();
}
else
{
BindingMutex.Request();
if ( ClientAuthInfo.ServerPrincipalName != 0 )
{
delete ClientAuthInfo.ServerPrincipalName;
}
ClientAuthInfo.ServerPrincipalName = NULL;
BindingMutex.Clear();
}
if (IdentityTracking == RPC_C_QOS_IDENTITY_STATIC)
{
if (StaticTokenHandle)
{
CloseHandle(StaticTokenHandle);
}
if (OpenThreadToken (GetCurrentThread(),
TOKEN_IMPERSONATE | TOKEN_QUERY,
TRUE,
&StaticTokenHandle) == FALSE)
{
StaticTokenHandle = 0;
}
Status = ReAcquireCredentialsIfNecessary();
if (Status != RPC_S_OK)
{
return Status;
}
}
AuthInfoInitialized = 1;
return(RPC_S_OK);
}
unsigned long
LRPC_BINDING_HANDLE::MapAuthenticationLevel (
IN unsigned long AuthenticationLevel
)
/*++
Routine Description:
The connection oriented protocol module supports all authentication
levels except for RPC_C_AUTHN_LEVEL_CALL. We just need to map it
to RPC_C_AUTHN_LEVEL_PKT.
--*/
{
UNUSED(this);
if (AuthenticationLevel >= RPC_C_AUTHN_LEVEL_CONNECT)
{
return(RPC_C_AUTHN_LEVEL_PKT_PRIVACY);
}
return(AuthenticationLevel);
}
inline int
LRPC_BINDING_HANDLE::AddAssociation (
IN LRPC_CASSOCIATION * Association
)
/*++
Routine Description:
This supplied remote procedure call needs to be put into the dictionary
of association
--*/
{
int err;
BindingMutex.Request() ;
err = SecAssociation.Insert(Association) ;
BindingMutex.Clear() ;
return(err);
}
inline void
LRPC_BINDING_HANDLE::RemoveAssociation (
IN LRPC_CASSOCIATION * Association
)
/*++
Routine Description:
Remove Association from BindingHandle, can keep a Key for Association because
1 association may be added to many BINDINGHANDLE::SecAssociationDict, 1 key per
Association won't do the job. Therefore, we delete Association this way.
Remember, there will be 5 Association in the SecAssoc the most, 1 per SecurityLevel
--*/
{
BindingMutex.Request() ;
SecAssociation.DeleteItemByBruteForce(Association);
BindingMutex.Clear() ;
}
RPC_STATUS
LRPC_BINDING_HANDLE::SetTransportOption( IN unsigned long option,
IN ULONG_PTR optionValue )
{
if (option == RPC_C_OPT_DONT_LINGER)
{
if (CurrentAssociation == NULL)
return RPC_S_WRONG_KIND_OF_BINDING;
if (CurrentAssociation->GetDontLingerState())
return RPC_S_OK;
CurrentAssociation->SetDontLingerState((BOOL)optionValue);
return RPC_S_OK;
}
else
{
return BINDING_HANDLE::SetTransportOption(option, optionValue);
}
}
RPC_STATUS
LRPC_BINDING_HANDLE::InqTransportOption( IN unsigned long option,
OUT ULONG_PTR * pOptionValue )
{
if (option == RPC_C_OPT_DONT_LINGER)
{
if (CurrentAssociation == NULL)
return RPC_S_WRONG_KIND_OF_BINDING;
*pOptionValue = CurrentAssociation->GetDontLingerState();
return RPC_S_OK;
}
else
{
return BINDING_HANDLE::InqTransportOption(option, pOptionValue);
}
}
MTSyntaxBinding *CreateLrpcBinding(
IN RPC_SYNTAX_IDENTIFIER *InterfaceId,
IN TRANSFER_SYNTAX_STUB_INFO *TransferSyntaxInfo,
IN int CapabilitiesBitmap
)
{
return new LRPC_BINDING(InterfaceId,
TransferSyntaxInfo,
CapabilitiesBitmap);
}
// protected by the LrpcMutex
LRPC_CASSOCIATION_DICT * LrpcAssociationDict = 0;
long LrpcLingeredAssociations = 0;
unsigned long LrpcDestroyedAssociations = 0;
ULARGE_INTEGER LastDestroyedAssociationsBatchTimestamp;
LRPC_CASSOCIATION::LRPC_CASSOCIATION (
IN DCE_BINDING * DceBinding,
IN CLIENT_AUTH_INFO *pClientAuthInfo,
IN USHORT MySequenceNumber,
OUT RPC_STATUS * Status
) : AssociationMutex(Status, 4000),
AssocAuthInfo(pClientAuthInfo, Status)
/*++
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.
Status - Returns the result of creating the association mutex.
--*/
{
ObjectType = LRPC_CASSOCIATION_TYPE;
this->DceBinding = DceBinding;
LpcClientPort = 0;
LpcReceivePort = 0;
BackConnectionCreated = 0;
CallIdCounter = 1;
SequenceNumber = MySequenceNumber;
Linger.fAssociationLingered = FALSE;
DeletedContextCount = 0;
BindingHandleReferenceCount = 1;
RefCount.SetInteger(2);
CachedCCall = NULL;
DontLinger = FALSE;
LastSecContextTrimmingTimestamp = 0;
// Generate the bind back cookie.
// The cookie will be used to verify that bind back
// originates from a legitimate server.
GenerateRandomNumber((unsigned char *)BindBackCookie, BIND_BACK_COOKIE_SIZE);
if (*Status == RPC_S_OK)
{
CachedCCall = new LRPC_CCALL(Status);
if (*Status == RPC_S_OK)
{
if (CachedCCall == 0)
{
*Status = RPC_S_OUT_OF_MEMORY ;
return;
}
CachedCCall->SetAssociation(this);
CachedCCallFlag = 1;
}
}
}
LRPC_CASSOCIATION::~LRPC_CASSOCIATION (
)
{
LRPC_BINDING * Binding;
LRPC_CCALL * CCall ;
LRPC_CCONTEXT *SecurityContext;
DictionaryCursor cursor;
if (DceBinding != 0)
{
delete DceBinding;
}
Bindings.Reset(cursor);
while ((Binding = Bindings.RemoveNext(cursor)) != 0)
{
Binding->RemoveReference();
}
SecurityContextDict.Reset(cursor);
while (SecurityContext = SecurityContextDict.RemoveNext(cursor))
{
delete SecurityContext;
}
// delete all CCalls
ActiveCCalls.Reset(cursor) ;
while ((CCall = ActiveCCalls.Next(cursor, TRUE)) != 0)
{
delete CCall ;
}
if (CachedCCallFlag != 0)
{
delete CachedCCall;
}
FreeCCalls.Reset(cursor);
while ((CCall = FreeCCalls.Next(cursor)) != 0)
{
delete CCall;
}
CloseLpcClientPort();
}
RPC_STATUS
LRPC_CASSOCIATION::CreateBackConnection (
IN LRPC_BINDING_HANDLE *BindingHandle
)
/*++
Routine Description:
Ask the server to create a back connection to us. Used in
conjuction with Async RPC.
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
#if defined(BUILD_WOW6432)
char LrpcMessageBuffer[sizeof(LRPC_BIND_BACK_MESSAGE) + 8];
char LrpcReplyBuffer[sizeof(LRPC_MESSAGE) + 8];
LRPC_BIND_BACK_MESSAGE *LrpcMessagePtr = (LRPC_BIND_BACK_MESSAGE *) AlignPtr8(LrpcMessageBuffer);
LRPC_MESSAGE *LrpcReplyPtr = (LRPC_MESSAGE *)AlignPtr8(LrpcReplyBuffer);
#else
LRPC_BIND_BACK_MESSAGE LrpcMessageBuffer;
LRPC_MESSAGE LrpcReplyBuffer;
LRPC_BIND_BACK_MESSAGE *LrpcMessagePtr = &LrpcMessageBuffer;
LRPC_MESSAGE *LrpcReplyPtr = &LrpcReplyBuffer;
#endif
NTSTATUS NtStatus ;
RPC_STATUS Status = RPC_S_OK;
if (BackConnectionCreated == 0)
{
Status = AssociationMutex.RequestSafe() ;
if (Status)
return Status;
if (BackConnectionCreated)
{
AssociationMutex.Clear() ;
return RPC_S_OK ;
}
if (LpcClientPort == 0)
{
Status = OpenLpcPort(BindingHandle, TRUE) ;
if (Status != RPC_S_OK)
{
AssociationMutex.Clear() ;
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLRPC_CASSOCIATION__CreateBackConnection10,
InqEndpoint());
return Status ;
}
}
else
{
// zero out the message to prevent data leak
RpcpMemorySet(LrpcMessagePtr, 0, sizeof(*LrpcMessagePtr));
LrpcGetEndpoint((RPC_CHAR *) LrpcMessagePtr->szPortName) ;
LrpcMessagePtr->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_BIND_BACK_MESSAGE) - sizeof(PORT_MESSAGE);
LrpcMessagePtr->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_BIND_BACK_MESSAGE);
LrpcMessagePtr->LpcHeader.u2.ZeroInit = 0;
LrpcMessagePtr->MessageType = LRPC_MSG_BIND_BACK;
// Make sure we can't be impersonated on the transport level.
// A legitimate Lrpc server will never do this on a bind back.
LrpcMessagePtr->LpcHeader.u2.s2.Type |= LPC_NO_IMPERSONATE;
DWORD Key;
LPC_KEY *LpcKey = (LPC_KEY *) &Key;
LpcKey->SeqNumber = SequenceNumber;
LpcKey->AssocKey = (unsigned short) AssociationDictKey;
LrpcMessagePtr->AssocKey = Key;
// Initialize a cookie to be sent in the message.
RpcpMemoryCopy(LrpcMessagePtr->Cookie, BindBackCookie, BIND_BACK_COOKIE_SIZE);
NtStatus = NtRequestWaitReplyPort(LpcClientPort,
(PORT_MESSAGE *) LrpcMessagePtr,
(PORT_MESSAGE *) LrpcReplyPtr) ;
if (NT_ERROR(NtStatus))
{
AssociationMutex.Clear() ;
if (NtStatus == STATUS_NO_MEMORY)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else if (NtStatus == STATUS_INSUFFICIENT_RESOURCES)
{
Status = RPC_S_OUT_OF_RESOURCES;
}
else
{
VALIDATE(NtStatus)
{
STATUS_INVALID_PORT_HANDLE,
STATUS_INVALID_HANDLE,
STATUS_PORT_DISCONNECTED,
STATUS_LPC_REPLY_LOST
} END_VALIDATE;
Status = RPC_S_SERVER_UNAVAILABLE;
}
RpcpErrorAddRecord(EEInfoGCLPC,
Status,
EEInfoDLLRPC_CASSOCIATION__CreateBackConnection20,
NtStatus);
return (Status);
}
ASSERT(LrpcReplyPtr->Ack.MessageType == LRPC_MSG_ACK) ;
if (LrpcReplyPtr->Ack.RpcStatus != RPC_S_OK)
{
AssociationMutex.Clear() ;
RpcpErrorAddRecord(EEInfoGCRuntime,
LrpcReplyPtr->Ack.RpcStatus,
EEInfoDLLRPC_CASSOCIATION__CreateBackConnection30);
return LrpcReplyPtr->Ack.RpcStatus;
}
}
BackConnectionCreated = 1 ;
AssociationMutex.Clear() ;
}
return Status ;
}
void
LRPC_CASSOCIATION::RemoveBindingHandleReference (
void
)
{
BOOL fWillLinger = FALSE;
LRPC_CASSOCIATION *CurrentAssociation;
LRPC_CASSOCIATION *OldestAssociation = NULL;
DWORD OldestAssociationTimestamp;
DictionaryCursor cursor;
BOOL fEnableGarbageCollection = FALSE;
LrpcMutexRequest();
LogEvent(SU_CASSOC, EV_DEC, this, 0, BindingHandleReferenceCount, 1, 1);
BindingHandleReferenceCount --;
if (BindingHandleReferenceCount == 0)
{
if (LpcClientPort && IsGarbageCollectionAvailable() && (!DontLinger))
{
fWillLinger = PrepareForLoopbackTicklingIfNecessary();
if (fWillLinger)
{
if (LrpcLingeredAssociations >= MaxLrpcLingeredAssociations)
{
OldestAssociationTimestamp = ~(DWORD)0;
// need to walk the dictionary and clean up the oldest item
LrpcAssociationDict->Reset(cursor);
while ((CurrentAssociation = LrpcAssociationDict->Next(cursor)) != 0)
{
if (CurrentAssociation->Linger.fAssociationLingered)
{
// yes, if the tick count wraps around, we may make a
// suboptimal decision and destroy a newer lingering
// association. That's ok - it will be a slight perf hit once
// every ~47 days - it won't be a bug
if (OldestAssociationTimestamp > CurrentAssociation->Linger.Timestamp)
{
OldestAssociation = CurrentAssociation;
}
}
}
// there must be an oldest association here
ASSERT(OldestAssociation);
LrpcAssociationDict->Delete(OldestAssociation->AssociationDictKey);
OldestAssociation->AssociationDictKey = -1;
// no need to update LrpcLingeredAssociations - we removed one,
// but we add one, so the balance is the same
}
else
{
LrpcLingeredAssociations ++;
ASSERT(LrpcLingeredAssociations <= MaxLrpcLingeredAssociations);
}
Linger.Timestamp = GetTickCount() + gThreadTimeout;
Linger.fAssociationLingered = TRUE;
}
}
if (!fWillLinger)
{
LrpcDestroyedAssociations ++;
fEnableGarbageCollection = CheckIfGCShouldBeTurnedOn(
LrpcDestroyedAssociations,
NumberOfLrpcDestroyedAssociationsToSample,
DestroyedLrpcAssociationBatchThreshold,
&LastDestroyedAssociationsBatchTimestamp
);
Delete();
}
}
LrpcMutexClear();
if (fEnableGarbageCollection)
{
// ignore the return value - we'll make a best effort to
// create the thread, but if there's no memory, that's
// still ok as the garbage collection thread only
// provides better perf in this case
(void) CreateGarbageCollectionThread();
}
if (OldestAssociation)
{
#if defined (RPC_GC_AUDIT)
int Diff;
Diff = (int)(GetTickCount() - OldestAssociation->Linger.Timestamp);
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LRPC association sync gc'ed %d ms after expire\n",
GetCurrentProcessId(), GetCurrentProcessId(), Diff);
#endif
OldestAssociation->Delete();
}
if (fWillLinger)
{
fWillLinger = GarbageCollectionNeeded(TRUE, gThreadTimeout);
if (fWillLinger == FALSE)
{
// uh-oh - we couldn't register for garbage collection - probably
// extremely low on memory. If nobody has picked us up in the meantime,
// delete this association. Otherwise, let it go - somebody is using
// it and we don't need to worry about gc'ing it. We also need to guard
// against the gc thread trying to do Delete on this also. If it does
// so, it will set the AssociationDictKey to -1 before it releases
// the mutex - therefore we can check for this. A gc thread cannot
// completely kill the object as we will hold one reference on it
LrpcMutexRequest();
if (AssociationDictKey != -1)
{
LrpcLingeredAssociations --;
ASSERT(LrpcLingeredAssociations >= 0);
if (Linger.fAssociationLingered)
Delete();
}
LrpcMutexClear();
}
#if defined (RPC_GC_AUDIT)
else
{
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LRPC association lingered %X\n",
GetCurrentProcessId(), GetCurrentProcessId(), this);
}
#endif
}
// removing the reference should be the last thing to do. Otherwise we're racing
// with a gc thread which may kill the this pointer underneath us
REFERENCED_OBJECT::RemoveReference();
}
void LRPC_CASSOCIATION::Delete(void)
{
int MyCount;
if (SetDeletedFlag())
{
if (AssociationDictKey != -1)
{
LrpcMutexRequest();
LrpcAssociationDict->Delete(AssociationDictKey);
LrpcMutexClear();
}
REFERENCED_OBJECT::RemoveReference();
}
}
BOOL
LRPC_CASSOCIATION::DoesBindingForInterfaceExist (
IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation
)
/*++
Routine Description:
Checks if an association supports a binding for this interface.
Arguments:
RpcInterfaceInformation - Supplies the interface information for
which we are looking for an osf binding object.
Return Value:
FALSE if it doesn't. Non-zero if it does.
--*/
{
LRPC_BINDING *Binding;
DictionaryCursor cursor;
BOOL fRetVal = FALSE;
BOOL fMutexTaken;
fMutexTaken = AssociationMutex.TryRequest();
if (!fMutexTaken)
return FALSE;
Bindings.Reset(cursor);
while ((Binding = Bindings.Next(cursor)) != 0)
{
// if we have a binding on the same interface,
// return TRUE
if (RpcpMemoryCompare(Binding->GetInterfaceId(),
&RpcInterfaceInformation->InterfaceId,
sizeof(RPC_SYNTAX_IDENTIFIER)) == 0)
{
fRetVal = TRUE;
break;
}
}
AssociationMutex.Clear();
return fRetVal;
}
RPC_STATUS
LRPC_CASSOCIATION::AllocateCCall (
IN LRPC_BINDING_HANDLE *BindingHandle,
OUT LRPC_CCALL ** CCall,
IN OUT PRPC_MESSAGE Message,
IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation
)
/*++
Routine Description:
This method will allocate an LRPC_CCALL which has been bound to the
interface specified by the interface information. This means that
first we need to find the presentation context corresponding to the
requested interface.
Arguments:
CCall - Returns the allocated LRPC_CCALL which has been bound to
the interface specified by the rpc interface information.
RpcInterfaceInformation - Supplies information describing the
interface to which we wish to bind.
Return Value:
--*/
{
LRPC_BINDING *SelectedBinding;
int RetryCount;
RPC_STATUS Status;
LRPC_CCONTEXT *SecurityContext;
BOOL fAlterSecurityContextNeeded = FALSE;
BOOL fAlterContextNeeded = FALSE;
LRPC_CCONTEXT *CurrentSecurityContext = NULL;
BOOL fUpdateCredentials = FALSE;
LUID CurrentModifiedId;
DictionaryCursor cursor;
LRPC_BINDING *BindingsForThisInterface[MaximumNumberOfTransferSyntaxes];
int NumberOfBindingsAvailable;
BOOL BindingCreated[MaximumNumberOfTransferSyntaxes];
int i;
int PreferredTransferSyntax;
int MatchingTransferSyntax;
BOOL IsBackConnectionNeeded = IsNonsyncMessage(Message);
RPC_STATUS CaptureStatus;
LRPC_BINDING *CurrentBinding;
BOOL fAssociationAborted = FALSE;
ULONG EffectiveIdentityTracking;
if (IsBackConnectionNeeded)
{
CLIENT_AUTH_INFO * AuthInfo;
AuthInfo = BindingHandle->InquireAuthInformation();
ASSERT(AuthInfo);
if (AuthInfo->IdentityTracking == RPC_C_QOS_IDENTITY_DYNAMIC)
{
CaptureStatus = CaptureModifiedId(&CurrentModifiedId);
fUpdateCredentials = TRUE;
}
}
Status = AssociationMutex.RequestSafe();
if (Status)
return Status;
if (fUpdateCredentials)
{
BindingHandle->UpdateCredentials((CaptureStatus != RPC_S_OK), &CurrentModifiedId);
}
RetryCount = 0;
EffectiveIdentityTracking = BindingHandle->GetIdentityTracking();
do
{
//
// We need to look at two things here. Presentation context
// and security context (in the async dynamic case only). If both match
// then we can allocate the call. Otherwise, we need to first bind
// in order to negotiate the interface/presentation context.
//
if (IsBackConnectionNeeded
|| EffectiveIdentityTracking == RPC_C_QOS_IDENTITY_STATIC)
{
SecurityContextDict.Reset(cursor);
while (SecurityContext = SecurityContextDict.Next(cursor))
{
if (BindingHandle->CompareCredentials(SecurityContext))
{
CurrentSecurityContext = SecurityContext;
CurrentSecurityContext->AddReference();
CurrentSecurityContext->UpdateTimestamp();
break;
}
}
if (SecurityContext == 0)
{
fAlterSecurityContextNeeded = TRUE;
}
}
Status = MTSyntaxBinding::FindOrCreateBinding(RpcInterfaceInformation,
Message,
&Bindings,
CreateLrpcBinding,
NULL, // CheckBinding
NULL, // CallbackContext
&NumberOfBindingsAvailable,
(MTSyntaxBinding **)BindingsForThisInterface,
BindingCreated
);
if (Status != RPC_S_OK)
{
goto Cleanup;
}
PreferredTransferSyntax = -1;
MatchingTransferSyntax = -1;
for (i = 0; i < NumberOfBindingsAvailable; i ++)
{
// do we support the preferred server
if (BindingsForThisInterface[i]->IsTransferSyntaxServerPreferred())
{
PreferredTransferSyntax = i;
break;
}
else if ((BindingCreated[i] == FALSE) && (MatchingTransferSyntax < 0))
{
MatchingTransferSyntax = i;
}
}
// is there a syntax preferred by the server
if (PreferredTransferSyntax >= 0)
{
// do we already support it (i.e. the binding was not created)
if (BindingCreated[PreferredTransferSyntax] == FALSE)
{
// then we're all set - just use it
fAlterContextNeeded = FALSE;
SelectedBinding = BindingsForThisInterface[PreferredTransferSyntax];
}
else
{
// we don't support it - negotiate it. We know this
// will succeed, because the server preferences
// are set. This should be hit in the auto-retry case only
fAlterContextNeeded = TRUE;
ASSERT(_NOT_COVERED_);
}
}
else
{
// no preferred syntax - any will do. Check if we found anything supported
if (MatchingTransferSyntax >= 0)
{
SelectedBinding = BindingsForThisInterface[MatchingTransferSyntax];
fAlterContextNeeded = FALSE;
}
else
{
fAlterContextNeeded = TRUE;
}
}
if (fAlterContextNeeded == FALSE)
{
if (IsBackConnectionNeeded)
{
Status = InitializeAsyncLrpcIfNecessary() ;
if (Status == RPC_S_OK)
{
Status = CreateBackConnection(BindingHandle);
}
if (Status != RPC_S_OK)
{
goto Cleanup;
}
}
}
if (fAlterContextNeeded || fAlterSecurityContextNeeded)
{
Status = ActuallyDoBinding(
BindingHandle,
IsBackConnectionNeeded,
fAlterContextNeeded,
fAlterSecurityContextNeeded,
BindingHandle->ClientAuthInfo.DefaultLogonId,
NumberOfBindingsAvailable,
BindingsForThisInterface,
&SelectedBinding,
&CurrentSecurityContext);
if (Status != RPC_S_SERVER_UNAVAILABLE)
{
fAssociationAborted = FALSE;
break;
}
// The server appears to have gone away, close the port and retry.
RetryCount++;
SelectedBinding = 0;
// both the creation in ActuallyDoBinding and the
// retrieval from the cache will add a refcount -
// remove it
if (CurrentSecurityContext)
{
CurrentSecurityContext->RemoveReference();
CurrentSecurityContext = NULL;
}
fAlterContextNeeded = TRUE;
if (IsBackConnectionNeeded
|| EffectiveIdentityTracking == RPC_C_QOS_IDENTITY_STATIC)
{
fAlterSecurityContextNeeded = TRUE;
}
AbortAssociation();
if (RetryCount < 3)
{
RpcpPurgeEEInfo();
}
fAssociationAborted = TRUE;
}
else
{
Status = RPC_S_OK;
break;
}
} while(RetryCount < 3);
if (Status == RPC_S_OK)
{
ASSERT(SelectedBinding != 0);
Status = ActuallyAllocateCCall(
CCall,
SelectedBinding,
IsBackConnectionNeeded,
BindingHandle,
CurrentSecurityContext);
}
else
{
// if the association was aborted, the bindings were already removed from the
// dictionary - don't do it again
if (fAssociationAborted == FALSE)
{
// the binding failed - remove the created bindings from the dictionary
for (i = 0; i < NumberOfBindingsAvailable; i ++)
{
if (BindingCreated[i])
{
CurrentBinding = Bindings.Delete(BindingsForThisInterface[i]->GetPresentationContext());
ASSERT(CurrentBinding == BindingsForThisInterface[i]);
delete CurrentBinding;
}
}
}
}
Cleanup:
if (CurrentSecurityContext)
{
if (Status == RPC_S_OK)
{
(*CCall)->SetCurrentSecurityContext(CurrentSecurityContext);
}
else
{
// remove the added reference
CurrentSecurityContext->RemoveReference();
}
}
AssociationMutex.Clear();
return(Status);
}
RPC_STATUS
LRPC_CASSOCIATION::ActuallyAllocateCCall (
OUT LRPC_CCALL ** CCall,
IN LRPC_BINDING * Binding,
IN BOOL IsBackConnectionNeeded,
IN LRPC_BINDING_HANDLE * BindingHandle,
IN LRPC_CCONTEXT *SecurityContext
)
/*++
Routine Description:
We need to allocate a LRPC_CCALL object for the call. We also need
to initialize it so that it specified the correct bound interface.
Arguments:
CCall - Returns the allocated LRPC_CCALL which has been bound to
the interface specified by the rpc interface information.
Binding - Supplies a representation of the interface to which the
remote procedure call is supposed to be directed.
Return Value:
RPC_S_OK - An LRPC_CCALL has been allocated and is ready to be used
to make a remote procedure call.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate
the LRPC_CALL object.
Notes:
The global mutex will be held when this routine is called.
--*/
{
RPC_STATUS Status = RPC_S_OK ;
DictionaryCursor cursor;
LRPC_CCALL *LocalCall;
THREAD *ThisThread;
if (CachedCCallFlag != 0)
{
LocalCall = CachedCCall ;
CachedCCallFlag = 0;
}
else
{
ThisThread = RpcpGetThreadPointer();
ASSERT(ThisThread);
if (ThisThread->GetCachedLrpcCall())
{
LocalCall = ThisThread->GetCachedLrpcCall();
ThisThread->SetCachedLrpcCall(NULL);
LocalCall->SetAssociation(this);
}
else
{
FreeCCalls.Reset(cursor) ;
while ((LocalCall = FreeCCalls.Next(cursor)) != 0)
{
FreeCCalls.Delete(LocalCall->FreeCallKey) ;
break;
}
}
if (LocalCall == 0)
{
LocalCall = new LRPC_CCALL(&Status);
if (LocalCall == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
if (Status != RPC_S_OK)
{
delete LocalCall ;
return Status ;
}
LocalCall->SetAssociation(this);
}
}
Status = LocalCall->ActivateCall(BindingHandle,
Binding,
IsBackConnectionNeeded,
SecurityContext);
if (Status != RPC_S_OK)
{
goto Cleanup;
}
if (IsBackConnectionNeeded)
{
if (ActiveCCalls.Insert(ULongToPtr(CallIdCounter), LocalCall) == -1)
{
Status = RPC_S_OUT_OF_MEMORY;
// remove the reference we added to this
// binding
Binding->RemoveReference();
goto Cleanup;
}
LocalCall->CallId = CallIdCounter++;
LogEvent(SU_CCALL, EV_START, LocalCall, this, LocalCall->CallId, 1, 0);
}
AddReference();
*CCall = LocalCall;
return(RPC_S_OK);
Cleanup:
if (LocalCall == CachedCCall)
{
CachedCCallFlag = 1;
}
else
{
delete LocalCall ;
}
return (Status) ;
}
void
LRPC_CASSOCIATION::PrepareBindPacket(LRPC_MESSAGE *LrpcMessage)
{
int MessageSize;
int NumContexts = 0;
LRPC_CCONTEXT *CContext;
LRPC_CCONTEXT *DeletedContext;
DictionaryCursor cursor;
DWORD OldestUnusedAge = 0;
LRPC_CCONTEXT *OldestUnusedCContext = NULL;
DWORD CurrentTime = GetTickCount();
// Expire old and unused contexts.
// If we have some space left for expiring additional contexts and
// the cache is at the max size, we will trim the oldest remaining context.
// This is done to make sure that when the cache is at the max, even if
// none of the contexts are old, we will still expire at least one context for
// each new one we establish.
SecurityContextDict.Reset(cursor);
// Proceed while the packet has space left for expiring more contexts.
while (NumContexts < MAX_LRPC_CONTEXTS)
{
// Pick the next unused context.
CContext = SecurityContextDict.Next(cursor);
if (CContext
&& CContext->IsUnused())
{
// If the security context is old, we expire it.
if (CContext->IsSecurityContextOld())
{
LrpcMessage->Bind.OldSecurityContexts.SecurityContextId[NumContexts]
= CContext->SecurityContextId;
DeletedContext = SecurityContextDict.Delete(CContext->ContextKey);
ASSERT(DeletedContext == CContext);
NumContexts++;
delete CContext;
}
// Otherwise, we check if we should force expiration of the oldest context.
else if (SecurityContextDict.Size() > LRPC_CCONTEXT_CACHE_SIZE_THRESHOLD)
{
// Compute the age of the current context taking into account tick count wrapping.
// Note that since both values are unsigned, we will get a large positive Age if CurrentTime < CContext->GetTimestamp().
DWORD Age = CurrentTime - CContext->GetTimestamp();
// If we have the oldest context, remember it.
if (Age > OldestUnusedAge)
{
OldestUnusedAge = Age;
OldestUnusedCContext = CContext;
}
}
}
else
{
break;
}
}
// If there is space left to expire more contexts and we were able to find the oldest unused context, delete it.
if (NumContexts < MAX_LRPC_CONTEXTS
&& OldestUnusedCContext != NULL)
{
LrpcMessage->Bind.OldSecurityContexts.SecurityContextId[NumContexts] = OldestUnusedCContext->SecurityContextId;
DeletedContext = SecurityContextDict.Delete(OldestUnusedCContext->ContextKey);
ASSERT(DeletedContext == OldestUnusedCContext);
NumContexts++;
delete OldestUnusedCContext;
}
UpdateLastSecContextTrimmingTimestamp();
MessageSize = FIELD_OFFSET(LRPC_BIND_MESSAGE, OldSecurityContexts) +
FIELD_OFFSET(OLD_SECURITY_CONTEXTS, SecurityContextId) +
NumContexts*sizeof(DWORD);
LrpcMessage->Bind.OldSecurityContexts.NumContexts = NumContexts;
LrpcMessage->LpcHeader.u1.s1.DataLength = (CSHORT) (MessageSize - sizeof(PORT_MESSAGE));
LrpcMessage->LpcHeader.u1.s1.TotalLength = (CSHORT) MessageSize;
}
RPC_STATUS
LRPC_CASSOCIATION::ActuallyDoBinding (
IN LRPC_BINDING_HANDLE *BindingHandle,
IN BOOL IsBackConnectionNeeded,
IN BOOL fAlterContextNeeded,
IN BOOL fAlterSecurityContextNeeded,
IN BOOL fDefaultLogonId,
IN int NumberOfBindings,
LRPC_BINDING *BindingsForThisInterface[],
OUT LRPC_BINDING ** Binding,
OUT LRPC_CCONTEXT **pSecurityContext
)
/*++
Routine Description:
Arguments:
RpcInterfaceInformation - Supplies information describing the interface
to which we wish to bind.
Binding - Returns an object representing the binding to the interface
described by the first argument.
Return Value:
--*/
{
NTSTATUS NtStatus;
NTSTATUS MyNtStatus;
RPC_STATUS Status = RPC_S_OK;
int DictKey ;
HANDLE ImpersonationToken = 0;
BOOL fTokenAltered = 0;
int i;
LRPC_BIND_EXCHANGE *BindExchange;
int BindingForNDR20PresentationContext;
int BindingForNDR64PresentationContext;
int BindingForNDRTestPresentationContext;
int ChosenBindingIndex;
LRPC_BINDING *SelectedBinding;
LRPC_MESSAGE *LrpcMessage;
ULONG EffectiveIdentityTracking;
//
// To start with, see if we have an LPC port; if we dont, open one
// up.
//
//
// The AssociationMutex is held when this function is called
//
AssociationMutex.VerifyOwned();
if (IsBackConnectionNeeded)
{
Status = InitializeAsyncLrpcIfNecessary() ;
if (Status == RPC_S_OK)
{
Status = CreateBackConnection(BindingHandle) ;
}
if (Status != RPC_S_OK)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLRPC_CASSOCIATION__ActuallyDoBinding10,
InqEndpoint());
return Status ;
}
}
else
{
if (LpcClientPort == 0)
{
//
// we now need to bind explicitly
//
Status = OpenLpcPort(BindingHandle, FALSE);
ASSERT(fAlterContextNeeded == TRUE);
if (Status != RPC_S_OK)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLRPC_CASSOCIATION__ActuallyDoBinding20,
InqEndpoint());
return Status ;
}
}
}
LrpcMessage = (LRPC_MESSAGE *)AlignOnNaturalBoundary(
_alloca(sizeof(LRPC_MESSAGE) + RPCRT_NATURAL_BOUNDARY_ALIGNMENT_MAX_SHIFT));
SanitizeLpcHeader (&LrpcMessage->LpcHeader);
if (CacheNeedsTrimming())
{
PrepareBindPacket(LrpcMessage);
}
else
{
LrpcMessage->LpcHeader.u1.s1.DataLength = FIELD_OFFSET(LRPC_BIND_MESSAGE, OldSecurityContexts)
+ sizeof(LrpcMessage->Bind.OldSecurityContexts.NumContexts)
- sizeof(PORT_MESSAGE);
LrpcMessage->LpcHeader.u1.s1.TotalLength = FIELD_OFFSET(LRPC_BIND_MESSAGE, OldSecurityContexts)
+ sizeof(LrpcMessage->Bind.OldSecurityContexts.NumContexts);
LrpcMessage->Bind.OldSecurityContexts.NumContexts = 0;
}
BindExchange = &LrpcMessage->Bind.BindExchange;
// Zero out uninitialized data.
BindExchange->ConnectType = 0;
BindExchange->AssocKey = 0;
BindExchange->RpcStatus = 0;
BindExchange->SecurityContextId = 0;
RpcpMemorySet(BindExchange->Cookie, 0, BIND_BACK_COOKIE_SIZE);
RpcpMemorySet(&BindExchange->szPortName, 0, sizeof(BindExchange->szPortName));
// Otherwise, just go ahead and send the bind request message to the
// server, and then wait for the bind response.
LrpcMessage->LpcHeader.u2.ZeroInit = 0;
LrpcMessage->Bind.MessageType = LRPC_MSG_BIND;
LrpcMessage->Bind.Pad[0] = 0;
LrpcMessage->Bind.Pad[1] = 0;
LrpcMessage->Bind.Pad[2] = 0;
if (fAlterContextNeeded)
{
SelectedBinding = *Binding = NULL;
ASSERT(NumberOfBindings > 0);
// all bindings have the same interface ID. Therefore, it is
// safe to use the first
RpcpMemoryCopy(&BindExchange->InterfaceId,
BindingsForThisInterface[0]->GetInterfaceId(),
sizeof(RPC_SYNTAX_IDENTIFIER));
BindExchange->Flags = NEW_PRESENTATION_CONTEXT_FLAG;
BindExchange->TransferSyntaxSet = 0;
BindingForNDR20PresentationContext = BindingForNDR64PresentationContext = -1;
ASSERT (NumberOfBindings <= MaximumNumberOfTransferSyntaxes);
for (i = 0; i < NumberOfBindings; i ++)
{
if (RpcpMemoryCompare(BindingsForThisInterface[i]->GetTransferSyntaxId(),
NDR20TransferSyntax, sizeof(RPC_SYNTAX_IDENTIFIER)) == 0)
{
BindExchange->TransferSyntaxSet |= TS_NDR20_FLAG;
BindExchange->PresentationContext[0]
= BindingsForThisInterface[i]->GetOnTheWirePresentationContext();
BindingForNDR20PresentationContext = i;
}
else if (RpcpMemoryCompare(BindingsForThisInterface[i]->GetTransferSyntaxId(),
NDR64TransferSyntax, sizeof(RPC_SYNTAX_IDENTIFIER)) == 0)
{
BindExchange->TransferSyntaxSet |= TS_NDR64_FLAG;
BindExchange->PresentationContext[1]
= BindingsForThisInterface[i]->GetOnTheWirePresentationContext();
BindingForNDR64PresentationContext = i;
}
else if (RpcpMemoryCompare(BindingsForThisInterface[i]->GetTransferSyntaxId(),
NDRTestTransferSyntax, sizeof(RPC_SYNTAX_IDENTIFIER)) == 0)
{
BindExchange->TransferSyntaxSet |= TS_NDRTEST_FLAG;
BindExchange->PresentationContext[2]
= BindingsForThisInterface[i]->GetOnTheWirePresentationContext();
BindingForNDRTestPresentationContext = i;
}
else
{
ASSERT(!"Unknown transfer syntax\n");
Status = RPC_S_UNSUPPORTED_TRANS_SYN;
goto Cleanup;
}
}
}
else
{
RpcpMemorySet(&BindExchange->InterfaceId,
0,
sizeof(RPC_SYNTAX_IDENTIFIER));
BindExchange->Flags = 0;
BindExchange->TransferSyntaxSet = 0;
BindExchange->PresentationContext[0] = 0;
BindExchange->PresentationContext[1] = 0;
BindExchange->PresentationContext[2] = 0;
ASSERT(*Binding != NULL);
SelectedBinding = *Binding;
}
if (fAlterSecurityContextNeeded)
{
BindExchange->Flags |= NEW_SECURITY_CONTEXT_FLAG;
if (fDefaultLogonId)
BindExchange->Flags |= DEFAULT_LOGONID_FLAG;
EffectiveIdentityTracking = BindingHandle->GetIdentityTracking();
if (EffectiveIdentityTracking == RPC_C_QOS_IDENTITY_STATIC)
{
if (OpenThreadToken (GetCurrentThread(),
TOKEN_IMPERSONATE | TOKEN_QUERY,
TRUE,
&ImpersonationToken) == FALSE)
{
ImpersonationToken = 0;
}
MyNtStatus = NtSetInformationThread(NtCurrentThread(),
ThreadImpersonationToken,
&(BindingHandle->StaticTokenHandle),
sizeof(HANDLE));
#if DBG
if (!NT_SUCCESS(MyNtStatus))
{
PrintToDebugger("RPC : NtSetInformationThread : %lx\n", MyNtStatus);
}
#endif // DBG
fTokenAltered = 1;
}
}
// We will allow the server to impersonate us on this call.
// This is fine since for static identity tracking this is when the server
// obtains the client's token.
NtStatus = NtRequestWaitReplyPort(LpcClientPort,
(PORT_MESSAGE *) LrpcMessage,
(PORT_MESSAGE *) LrpcMessage) ;
if (fTokenAltered)
{
MyNtStatus = NtSetInformationThread(NtCurrentThread(),
ThreadImpersonationToken,
&ImpersonationToken,
sizeof(HANDLE));
#if DBG
if (!NT_SUCCESS(MyNtStatus))
{
PrintToDebugger("RPC : NtSetInformationThread : %lx\n", MyNtStatus);
}
#endif // DBG
if (ImpersonationToken)
{
CloseHandle(ImpersonationToken);
}
}
if (NT_SUCCESS(NtStatus))
{
ASSERT(LrpcMessage->Bind.MessageType == LRPC_BIND_ACK);
if (BindExchange->RpcStatus == RPC_S_OK)
{
if (fAlterSecurityContextNeeded &&
(IsBackConnectionNeeded
|| EffectiveIdentityTracking == RPC_C_QOS_IDENTITY_STATIC))
{
//
// The security context ID should never be -1 for success bind_ack.
// Note that this is the case only if we are setting up a new security context.
// If BindExchange->Flags is not set with NEW_SECURITY_CONTEXT_FLAG, then we
// may get back a -1 SecurityContextId, but we will not create a security context.
//
if (BindExchange->SecurityContextId == -1)
{
CORRUPTION_ASSERT(BindExchange->SecurityContextId != -1);
Status = RPC_S_PROTOCOL_ERROR;
goto Cleanup;
}
*pSecurityContext = new LRPC_CCONTEXT(
BindingHandle->InquireAuthInformation(),
BindExchange->SecurityContextId,
this);
if (*pSecurityContext == 0)
{
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
if ((DictKey = SecurityContextDict.Insert(*pSecurityContext)) == -1)
{
delete *pSecurityContext;
*pSecurityContext = NULL;
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
(*pSecurityContext)->AddReference();
(*pSecurityContext)->ContextKey = DictKey;
}
if (fAlterContextNeeded)
{
ChosenBindingIndex = -1;
// which presentation context did the server pick?
if (BindExchange->TransferSyntaxSet & TS_NDR20_FLAG)
{
ASSERT(BindingForNDR20PresentationContext != -1);
// the server should choose only one transfer syntax
ASSERT((BindExchange->TransferSyntaxSet & ~TS_NDR20_FLAG) == 0);
ChosenBindingIndex = BindingForNDR20PresentationContext;
}
else if (BindExchange->TransferSyntaxSet & TS_NDR64_FLAG)
{
ASSERT(BindingForNDR64PresentationContext != -1);
// the server should choose only one transfer syntax
ASSERT((BindExchange->TransferSyntaxSet & ~TS_NDR64_FLAG) == 0);
ChosenBindingIndex = BindingForNDR64PresentationContext;
}
else if (BindExchange->TransferSyntaxSet & TS_NDRTEST_FLAG)
{
ASSERT(BindingForNDRTestPresentationContext != -1);
// the server should choose only one transfer syntax
ASSERT((BindExchange->TransferSyntaxSet & ~TS_NDRTEST_FLAG) == 0);
ChosenBindingIndex = BindingForNDRTestPresentationContext;
}
else
{
ASSERT(!"Server supplied invalid response");
}
if (ChosenBindingIndex < 0)
{
ASSERT(_NOT_COVERED_);
BindExchange->RpcStatus = RPC_S_UNSUPPORTED_TRANS_SYN;
}
else
{
// if we offered the server a choice of bindings and it
// exercised this choice, record its preferences
if (NumberOfBindings > 1)
{
BindingsForThisInterface[ChosenBindingIndex]->
TransferSyntaxIsServerPreferred();
for (i = 0; i < NumberOfBindings; i ++)
{
if (ChosenBindingIndex != i)
{
BindingsForThisInterface[i]->TransferSyntaxIsNotServerPreferred();
}
}
}
SelectedBinding = BindingsForThisInterface[ChosenBindingIndex];
}
}
}
else
{
if (BindExchange->Flags & EXTENDED_ERROR_INFO_PRESENT)
{
ExtendedErrorInfo *EEInfo;
// In the [out] bind direction the eeinfo is contained in the
// Bind.BindExchangeReturn structure.
// Bind.BindExchange is used for passing the port name in the
// [in] direction and does not have enough space.
ASSERT(IsBufferAligned(LrpcMessage->Bind.BindExchangeReturn.Buffer));
Status = UnpickleEEInfo(LrpcMessage->Bind.BindExchangeReturn.Buffer,
LrpcMessage->LpcHeader.u1.s1.DataLength
- BIND_NAK_PICKLE_BUFFER_OFFSET
+ sizeof(PORT_MESSAGE),
&EEInfo);
if (Status == RPC_S_OK)
{
RpcpSetEEInfo(EEInfo);
}
RpcpErrorAddRecord(EEInfoGCRuntime,
BindExchange->RpcStatus,
EEInfoDLLRPC_CASSOCIATION__ActuallyDoBinding30);
}
}
Status = BindExchange->RpcStatus;
ASSERT (Status != RPC_S_SERVER_UNAVAILABLE
&& Status != RPC_S_ACCESS_DENIED) ;
}
else
{
Status = RPC_S_SERVER_UNAVAILABLE;
RpcpErrorAddRecord(EEInfoGCLPC,
Status,
EEInfoDLLRPC_CASSOCIATION__ActuallyDoBinding40,
NtStatus);
}
Cleanup:
*Binding = SelectedBinding;
return (Status);
}
void
LRPC_CASSOCIATION::ProcessResponse (
IN LRPC_MESSAGE *LrpcResponse,
IN BOOL AssocMutexHeld,
IN OUT LRPC_MESSAGE **LrpcReplyMessage
)
/*++
Routine Description:
Process a response on the back connection.
Two types of responses can show up on the back connection:
1. Responses from async calls.
Arguments:
LrpcResponse - Reply message.
AssocMutexHeld - used to indicate that we are calling this function
while holding the AssociationMutex.
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
LRPC_CCALL *CCall ;
RPC_MESSAGE RpcMessage ;
THREAD *ThisThread;
LRPC_CCALL *ExistingCCall;
ULONG OriginalCallId;
ThisThread = RpcpGetThreadPointer();
ASSERT(ThisThread);
ASSERT(ThisThread->GetDestroyedWithOutstandingLocksFlag() == 0);
OriginalCallId = LrpcResponse->Rpc.RpcHeader.CallId;
if (!AssocMutexHeld){
AssociationMutex.Request();
}
CCall = ActiveCCalls.Find(ULongToPtr(OriginalCallId)) ;
if (CCall == 0)
{
AssociationMutex.Clear() ;
if (LrpcResponse->Rpc.RpcHeader.Flags & LRPC_BUFFER_SERVER)
{
// There may be a server thread stuck waiting for the reply
// in which case we should treat this as a synchronous call
// and make sure the message is not dropped.
// We do this only if the buffer is server. If it is not,
// we don't do that, because the LPC response we received
// was datagram, and we can't send a response to a datagram.
// Note that this is not necessary either, since the response
// is from a server, and we don't have any use on the server
// for a fault to a response (it just gets dropped). If the
// buffer is server, we still need to do it to free
// the thread, because it is doing NtRequestWaitReplyPort,
// and this is blocking.
SetFaultPacket(LrpcResponse,
RPC_S_CALL_FAILED_DNE,
LrpcResponse->Rpc.RpcHeader.Flags | LRPC_SYNC_CLIENT,
NULL);
*LrpcReplyMessage = LrpcResponse;
}
return ;
}
CCall->LockCallFromResponse();
AssociationMutex.Clear() ;
CCall->ProcessResponse(LrpcResponse);
// if this call was destroyed with outstanding locks, don't
// touch it - just clear the flag
if (ThisThread->GetDestroyedWithOutstandingLocksFlag())
{
ThisThread->ClearDestroyedWithOutstandingLocksFlag();
}
else
{
AssociationMutex.Request() ;
// check if somebody has freed the call. If yes, don't do anything - the counter
// would have been reset
ExistingCCall = ActiveCCalls.Find(ULongToPtr(OriginalCallId));
if (ExistingCCall
&& (ExistingCCall == CCall))
{
CCall->UnlockCallFromResponse();
}
AssociationMutex.Clear() ;
}
}
RPC_STATUS
LRPC_CASSOCIATION::OpenLpcPort (
IN LRPC_BINDING_HANDLE *BindingHandle,
IN BOOL fBindBack
)
/*++
Routine Description:
Arguments:
RpcInterfaceInformation - Supplies information describing the interface
to which we wish to bind.
Binding - Returns an object representing the binding to the interface
described by the first argument.
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.
--*/
{
NTSTATUS NtStatus;
UNICODE_STRING UnicodeString;
RPC_CHAR * LpcPortName;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
RPC_STATUS Status;
LRPC_BIND_EXCHANGE BindExchange;
unsigned long BindExchangeLength = sizeof(LRPC_BIND_EXCHANGE);
PSID SidToUse;
int RetryCount;
BOOL fCachedSid = TRUE;
//
// Look at the network options and initialize the security quality
// of service appropriately.
//
SecurityQualityOfService.EffectiveOnly = (unsigned char) BindingHandle->EffectiveOnly;
SecurityQualityOfService.ContextTrackingMode =
SECURITY_DYNAMIC_TRACKING;
SecurityQualityOfService.ImpersonationLevel =
MapRpcToNtImp(AssocAuthInfo.ImpersonationType) ;
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
//
// Allocate and initialize the port name. We need to stick the
// LRPC_DIRECTORY_NAME on the front of the endpoint. This is for
// security reasons (so that anyone can create LRPC endpoints).
//
LpcPortName = new RPC_CHAR[
RpcpStringLength(DceBinding->InqEndpoint())
+ RpcpStringLength(LRPC_DIRECTORY_NAME) + 1];
if (LpcPortName == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
RpcpMemoryCopy(LpcPortName, LRPC_DIRECTORY_NAME,
RpcpStringLength(LRPC_DIRECTORY_NAME) * sizeof(RPC_CHAR));
RpcpMemoryCopy(LpcPortName + RpcpStringLength(LRPC_DIRECTORY_NAME),
DceBinding->InqEndpoint(),
(RpcpStringLength(DceBinding->InqEndpoint()) + 1)
* sizeof(RPC_CHAR));
RtlInitUnicodeString(&UnicodeString, LpcPortName);
// zero out buffer to prevent data leak
RpcpMemorySet (&BindExchange, 0, sizeof(BindExchange));
DWORD Key;
LPC_KEY *LpcKey = (LPC_KEY *) &Key;
LpcKey->SeqNumber = SequenceNumber;
LpcKey->AssocKey = (unsigned short) AssociationDictKey;
BindExchange.ConnectType = LRPC_CONNECT_REQUEST ;
BindExchange.AssocKey = Key;
if (fBindBack)
{
BindExchange.Flags = BIND_BACK_FLAG;
// Initialize a cookie to be sent in the message.
RpcpMemoryCopy(BindExchange.Cookie, BindBackCookie, BIND_BACK_COOKIE_SIZE);
// LRPC_BIND_EXCHANGE::szPortName can be at most PORT_NAME_LEN chars.
// The global lrpc server's port name can be initialized either in
// LRPC_SERVER::InitializeAsync or in LRPC_ADDRESS::ActuallySetupAddress.
// In the former case, it is just "MSAsyncRPC_DECIMALPID" and in the latter,
// we check to make sure the name does not overflow.
LrpcGetEndpoint((RPC_CHAR *) BindExchange.szPortName) ;
}
else
{
BindExchange.Flags = 0;
}
if ((AssocAuthInfo.Capabilities & RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH)
&& AssocAuthInfo.ServerPrincipalName)
{
PSID pSID;
// if we don't have a SID, we'll have to convert the name to a SID.
if (AssocAuthInfo.ServerSid == NULL)
{
Status = RpcpLookupAccountName (AssocAuthInfo.ServerPrincipalName,
&fCachedSid,
&pSID
);
if (Status != RPC_S_OK)
{
delete [] LpcPortName;
return Status;
}
}
else
{
ASSERT(IsValidSid(AssocAuthInfo.ServerSid));
pSID = AssocAuthInfo.ServerSid;
}
RetryCount = 0;
do
{
Status = NormalizeAccountSid(pSID, &SidToUse, &RetryCount);
if (Status != RPC_S_OK)
{
if (AssocAuthInfo.ServerSid == NULL)
{
delete [] pSID;
}
delete [] LpcPortName;
return Status;
}
NtStatus = NtSecureConnectPort (
&LpcClientPort,
&UnicodeString,
&SecurityQualityOfService,
NULL,
SidToUse,
NULL,
NULL,
&BindExchange,
&BindExchangeLength);
// we may attempt retry only if we got the server sid
// wrong.
if (NtStatus != STATUS_SERVER_SID_MISMATCH)
break;
}
while (RetryCount != 0);
if (AssocAuthInfo.ServerSid == NULL)
{
delete [] pSID;
}
}
else
{
NtStatus = NtConnectPort(
&LpcClientPort,
&UnicodeString,
&SecurityQualityOfService,
NULL,
NULL,
NULL,
&BindExchange,
&BindExchangeLength);
}
delete [] LpcPortName;
if (NT_SUCCESS(NtStatus))
{
ASSERT(BindExchangeLength == sizeof(LRPC_BIND_EXCHANGE));
return(BindExchange.RpcStatus);
}
if (NtStatus == STATUS_PORT_CONNECTION_REFUSED)
{
if (BindExchange.Flags & SERVER_BIND_EXCH_RESP)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
BindExchange.RpcStatus,
EEInfoDLLRPC_CASSOCIATION__OpenLpcPort20,
NtStatus);
return(BindExchange.RpcStatus);
}
// if the SERVER_BIND_EXCH_RESP flag is not set, the rejection
// comes from LPC. The only case where this can happen is if
// the server is not available.
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_SERVER_UNAVAILABLE,
EEInfoDLLRPC_CASSOCIATION__OpenLpcPort30,
NtStatus);
return(RPC_S_SERVER_UNAVAILABLE);
}
if (NtStatus == STATUS_NO_MEMORY)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else if ((NtStatus == STATUS_INSUFFICIENT_RESOURCES)
|| (NtStatus == STATUS_QUOTA_EXCEEDED))
{
Status = RPC_S_OUT_OF_RESOURCES;
}
else if (NtStatus == STATUS_OBJECT_PATH_INVALID)
{
Status = RPC_S_INVALID_ENDPOINT_FORMAT;
}
else if (NtStatus == STATUS_ACCESS_DENIED
|| NtStatus == STATUS_SERVER_SID_MISMATCH
|| NtStatus == STATUS_BAD_IMPERSONATION_LEVEL)
{
// Invalidate our cache entry for this SPN
(void) RemoveFromSIDCache(AssocAuthInfo.ServerPrincipalName);
Status = RPC_S_ACCESS_DENIED;
}
else
{
#if DBG
if (NtStatus != STATUS_OBJECT_NAME_NOT_FOUND)
{
PrintToDebugger("LRPC: NtConnectPort : %lx\n", NtStatus);
}
#endif // DBG
ASSERT(NtStatus == STATUS_OBJECT_NAME_NOT_FOUND);
Status = RPC_S_SERVER_UNAVAILABLE;
}
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLRPC_CASSOCIATION__OpenLpcPort40,
NtStatus);
return Status;
}
void
LRPC_CASSOCIATION::FreeCCall (
IN LRPC_CCALL * CCall
)
/*++
Routine Description:
This routine will get called to notify this association that a remote
procedure call on this association has completed.
Arguments:
CCall - Supplies the remote procedure call which has completed.
--*/
{
LRPC_CCALL *DeletedCall;
BOOL fMutexTaken = FALSE;
ExtendedErrorInfo *LocalEEInfo;
LRPC_MESSAGE *LocalLrpcMessage;
THREAD *ThisThread;
BOOL fCacheToThread;
BOOL fOutstandingLocks = FALSE;
BOOL fUnlocked;
void *Buffer;
unsigned int BufferLength ;
if (CCall->CallId != (ULONG) -1)
{
// Try to take both resources, but if fail on the second, release
// the first and retry. There is a potential deadlock here, since
// another thread may have the call locked while holding the AssociationMutex,
// release the mutex, and try to take it again. This may happen in
// ProcessResponse()
while (TRUE)
{
AssociationMutex.Request();
fMutexTaken = TRUE;
LogEvent(SU_CCALL, EV_STOP, CCall, this, CCall->CallId, 1, 0);
if (CCall->AsyncStatus == RPC_S_CALL_CANCELLED)
{
// if the call was cancelled, there is a race condition
// where the server may still be sending us a response
// make sure we wait for any response already in the pipeline
// to go through
fOutstandingLocks = CCall->TryWaitForCallToBecomeUnlocked(&fUnlocked);
if (fUnlocked)
break;
else
{
AssociationMutex.Clear();
fMutexTaken = FALSE;
Sleep(10);
}
}
else
{
// this is not a cancel. It is possible that a response
// is still being processed. We zero out the counter now,
// and we will remove the element from the dictionary and
// reset its CallId (we're still inside the mutex). When
// the thread that processes the response is about to
// decrease the refcount, it will check whether the call is
// in the dictionary and whether it has the same call id.
// If yes, it won't touch the call.
CCall->ResponseLockCount.SetInteger(0);
break;
}
}
DeletedCall = ActiveCCalls.Delete(ULongToPtr(CCall->CallId));
ASSERT((DeletedCall == 0) || (CCall == DeletedCall));
CCall->CallId = (ULONG) -1;
}
LogEvent(SU_CCALL, EV_REMOVED, CCall, this, 0, 1, 2);
LogEvent(SU_CCALL, EV_REMOVED, CCall, this, 0, 1, 6);
if (CCall->BufferQueue.Size() != 0)
{
if (!fMutexTaken)
{
AssociationMutex.Request();
fMutexTaken = TRUE;
}
while ((Buffer = CCall->BufferQueue.TakeOffQueue(&BufferLength)) != 0)
{
CCall->ActuallyFreeBuffer(Buffer);
}
}
if (fMutexTaken)
{
LocalEEInfo = CCall->EEInfo;
CCall->EEInfo = NULL;
}
else
{
LocalEEInfo =
(ExtendedErrorInfo *)InterlockedExchangePointer((PVOID *)(&CCall->EEInfo), NULL);
}
if (LocalEEInfo != NULL)
{
FreeEEInfoChain(LocalEEInfo);
}
CCall->Binding->RemoveReference();
CCall->Binding = NULL;
if (CCall == CachedCCall)
{
CachedCCallFlag = 1 ;
}
else
{
if (fMutexTaken)
{
LocalLrpcMessage = CCall->LrpcMessage;
CCall->LrpcMessage = 0;
}
else
{
LocalLrpcMessage =
(LRPC_MESSAGE *)InterlockedExchangePointer((PVOID *)(&CCall->LrpcMessage), 0);
}
FreeMessage(LocalLrpcMessage);
ThisThread = RpcpGetThreadPointer();
ASSERT(ThisThread);
if (gfServerPlatform && (ThisThread->GetCachedLrpcCall() == NULL))
{
CCall->FreeCallKey = -1;
// set the association to NULL to toast anybody who tries to touch it
CCall->Association = NULL;
ThisThread->SetCachedLrpcCall(CCall);
}
else if (FreeCCalls.Size() < 64)
{
if (!fMutexTaken)
{
AssociationMutex.Request();
fMutexTaken = TRUE;
}
if ((CCall->FreeCallKey = FreeCCalls.Insert(CCall)) == -1)
{
delete CCall;
}
}
else
{
CCall->FreeCallKey = -1;
delete CCall;
}
}
if (fMutexTaken)
{
AssociationMutex.Clear();
}
if (fOutstandingLocks)
{
ThisThread = RpcpGetThreadPointer();
ASSERT(ThisThread);
ThisThread->SetDestroyedWithOutstandingLocksFlag();
}
RemoveReference();
}
LRPC_CASSOCIATION *
FindOrCreateLrpcAssociation (
IN DCE_BINDING * DceBinding,
IN CLIENT_AUTH_INFO *pClientAuthInfo,
IN RPC_CLIENT_INTERFACE *InterfaceInfo
)
/*++
Routine Description:
This routine finds an existing association supporting the requested
DCE binding, or creates a new association which supports the
requested DCE binding. Ownership of the passed DceBinding passes
to this routine.
Arguments:
DceBinding - Supplies binding information; if an association is
returned the ownership of the DceBinding is passed
to the association.
pClientAuthInfo - pointer to the client auth info from the binding handle.
We compare the client auth info of each association against the client
auth info of the binding handle to determine if there is a match.
InterfaceInfo - pointer to the interface we are about to make a call on
while we try to find the association. If present, it also indicates
we are trying to do shortcut endpoint resolution. If not present,
we have already resolved the endpoint.
Return Value:
An association which supports the requested binding will be returned.
Otherwise, zero will be returned, indicating insufficient memory.
--*/
{
LRPC_CASSOCIATION * Association;
RPC_STATUS Status = RPC_S_OK;
static USHORT SequenceNumber = 1;
DictionaryCursor cursor;
BOOL fOnlyEndpointDiffers;
int Result;
BOOL Supported;
// First, we check for an existing association.
LrpcAssociationDict->Reset(cursor);
while ((Association = LrpcAssociationDict->Next(cursor)) != 0)
{
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Comparing association to: %S, %S, %S\n",
GetCurrentProcessId(), GetCurrentProcessId(), Association->DceBinding->InqRpcProtocolSequence(),
Association->DceBinding->InqNetworkAddress(), Association->DceBinding->InqEndpoint());
#endif
Status = Association->IsSupportedAuthInfo(pClientAuthInfo, &Supported);
if (Status != RPC_S_OK)
{
return NULL;
}
// if we are doing shortcut endpoint resolution, but the association
// did not mutually authenticate the server with the endpoint mapper
// (endpoint mapper name space partitioning), and the binding
// handle wants to mutually authenticate the server with the endpoint
// mapper, we cannot do shortcut endpoint resolution - we must allow
// the endpoint resolution.
if (Supported
&& InterfaceInfo
&& (pClientAuthInfo->Capabilities & RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT)
&& !(Association->GetAuthInfoCapabilities() & RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT)
)
{
return NULL;
}
Result = Association->CompareWithDceBinding(DceBinding, &fOnlyEndpointDiffers);
if ((Supported)
&&
(!Result
||
(
fOnlyEndpointDiffers
&& InterfaceInfo
&& DceBinding->IsNullEndpoint()
&& Association->DoesBindingForInterfaceExist(InterfaceInfo)
)
)
)
{
Association->AddBindingHandleReference();
if (Association->Linger.fAssociationLingered == TRUE)
{
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LRPC lingering association resurrected %X %S %S %S\n",
GetCurrentProcessId(), GetCurrentProcessId(), Association,
Association->DceBinding->InqRpcProtocolSequence(),
Association->DceBinding->InqNetworkAddress(),
Association->DceBinding->InqEndpoint());
#endif
LrpcLingeredAssociations --;
ASSERT(LrpcLingeredAssociations >= 0);
Association->Linger.fAssociationLingered = FALSE;
}
if ((pClientAuthInfo->Capabilities & RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT)
&& !(Association->GetAuthInfoCapabilities() & RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT))
{
Association->AddLocalMAToCapabilities();
}
delete DceBinding;
return(Association);
}
}
// if asked to do short endpoint resolution, don't create new association
if (InterfaceInfo)
return NULL;
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Creating association to: %S, %S, %S\n",
GetCurrentProcessId(), GetCurrentProcessId(), DceBinding->InqRpcProtocolSequence(),
DceBinding->InqNetworkAddress(), DceBinding->InqEndpoint());
#endif
SequenceNumber = (SequenceNumber+1) % (0x7FFF);
Association = new LRPC_CASSOCIATION(DceBinding,
pClientAuthInfo,
SequenceNumber,
&Status);
if ((Association != 0) && (Status == RPC_S_OK))
{
Association->AssociationDictKey = LrpcAssociationDict->Insert(Association);
if (Association->AssociationDictKey == -1)
{
Association->DceBinding = 0;
delete Association;
return(0);
}
return(Association);
}
else
{
if (Association != 0)
{
Association->DceBinding = 0;
delete Association;
}
return(0);
}
ASSERT(0);
return(0);
}
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_CASSOCIATION * Association;
DictionaryCursor cursor;
if (LrpcAssociationDict != 0)
{
LrpcAssociationDict->Reset(cursor);
while ((Association = LrpcAssociationDict->Next(cursor)) != 0)
{
Association->RemoveReference() ;
}
}
}
void
LRPC_CASSOCIATION::AbortAssociation (
IN BOOL ServerAborted
)
/*++
Routine Description:
This association needs to be aborted because a the server side of the
lpc port has been closed.
--*/
{
LRPC_BINDING * Binding;
LRPC_CCALL *CCall ;
LRPC_CCONTEXT *SecurityContext;
DictionaryCursor cursor;
AssociationMutex.Request();
LogEvent(SU_CASSOC, EV_ABORT, this, 0, ServerAborted, 1, 0);
CloseLpcClientPort();
Bindings.Reset(cursor);
while ((Binding = Bindings.RemoveNext(cursor)) != 0)
{
// RemoveReference will destroy the binding if its
// ref count reaches 0
Binding->RemoveReference();
}
SecurityContextDict.Reset(cursor);
while (SecurityContext = SecurityContextDict.RemoveNext(cursor))
{
SecurityContext->Destroy();
}
int waitIterations = 8;
if (ServerAborted)
{
ActiveCCalls.Reset(cursor);
while ((CCall = ActiveCCalls.Next(cursor, TRUE)) != 0)
{
CCall->ServerAborted(&waitIterations);
}
}
// nuke the free calls as well, because when we abort the association
// some information in them will be stale
FreeCCalls.Reset(cursor);
while ((CCall = FreeCCalls.Next(cursor)) != 0)
{
delete CCall;
}
AssociationMutex.Clear();
}
void
LRPC_CASSOCIATION::CloseLpcClientPort (
)
/*++
Routine Description:
The LpcClientPort will be closed (and a close message sent to the server).
--*/
{
NTSTATUS NtStatus;
HANDLE LpcPortLocal;
if (LpcClientPort != 0)
{
// Another thread might be trying to do a send or a send/receive on this port
// while the association is being aborted. We need to make sure the send on
// the port can't come after the port has been closed.
// To accomplish this, we set the port to NULL and close a local copy. The clients
// may still send on a NULL port, via a small race, but the probability
// can be reduced by checking for a NULL handle and we will just get an error from LPC.
// Also, the port sits on the stack for a little bit while we call the
// LPC routines, so the race is still there, but its minimal.
LpcPortLocal = LpcClientPort;
LpcClientPort = 0;
NtStatus = NtClose(LpcPortLocal);
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtClose : %lx\n", NtStatus);
}
#endif // DBG
if (LpcReceivePort)
{
// Similarly to the above case, set the port to NULL and close a local copy.
LpcPortLocal = LpcReceivePort;
LpcReceivePort = 0;
NtStatus = NtClose(LpcPortLocal) ;
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtClose : %lx\n", NtStatus);
}
#endif
ASSERT(NT_SUCCESS(NtStatus));
}
BackConnectionCreated = 0;
}
}
RPC_STATUS
LRPC_CASSOCIATION::IsSupportedAuthInfo(
IN CLIENT_AUTH_INFO * ClientAuthInfo,
OUT BOOL *Supported
)
/*++
Routine Description:
Check if this association supports the needed auth info.
Arguments:
ClientAuthInfo - description
Supported - on output, contains non-zero if the association supports
this auth info, and 0 otherwise. Undefined on failure
Return Value:
RPC_S_OK or RPC_S_* error
--*/
{
PSID Sid;
RPC_STATUS RpcStatus;
*Supported = FALSE;
if (!ClientAuthInfo)
{
if (AssocAuthInfo.ImpersonationType == RPC_C_IMP_LEVEL_IMPERSONATE)
{
*Supported = TRUE;
return RPC_S_OK;
}
return RPC_S_OK;
}
if ((ClientAuthInfo->AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE
&& AssocAuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE)
|| (AssocAuthInfo.AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE
&& ClientAuthInfo->AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE))
{
return RPC_S_OK;
}
ASSERT(ClientAuthInfo->AuthenticationService == RPC_C_AUTHN_WINNT);
if (ClientAuthInfo->AuthorizationService
!= AssocAuthInfo.AuthorizationService)
{
return RPC_S_OK;
}
#if 0
if (ClientAuthInfo->IdentityTracking != AssocAuthInfo.IdentityTracking)
{
return RPC_S_OK;
}
#endif
if (ClientAuthInfo->ImpersonationType != AssocAuthInfo.ImpersonationType)
{
return RPC_S_OK;
}
if (ClientAuthInfo->Capabilities & RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH)
{
// if the SPN and SID are equal, this is supported
return AssocAuthInfo.CompareSPNAndSID (ClientAuthInfo, Supported);
}
*Supported = TRUE;
return RPC_S_OK;
}
LRPC_CCALL::LRPC_CCALL (
IN OUT RPC_STATUS *Status
) : CallMutex(Status),
SyncEvent(Status, 0),
ResponseLockCount(0)
/*++
--*/
{
ObjectType = LRPC_CCALL_TYPE;
CurrentBindingHandle = 0;
Association = 0;
CallAbortedFlag = 0;
LrpcMessage = 0;
CachedLrpcMessage = 0;
FreeCallKey = -1;
CallId = (ULONG) -1;
EEInfo = NULL;
}
LRPC_CCALL::~LRPC_CCALL (
)
/*++
--*/
{
if (LrpcMessage)
{
FreeMessage(LrpcMessage) ;
}
if (CachedLrpcMessage)
{
FreeMessage(CachedLrpcMessage) ;
}
if (CallId != (ULONG) -1)
{
// the association mutex is currently held
Association->ActiveCCalls.Delete(ULongToPtr(CallId));
}
if (FreeCallKey != -1)
{
Association->FreeCCalls.Delete(FreeCallKey) ;
}
}
RPC_STATUS
LRPC_CCALL::NegotiateTransferSyntax (
IN OUT PRPC_MESSAGE Message
)
{
// just return the transfer syntax already negotiated in the binding
Message->TransferSyntax = Binding->GetTransferSyntaxId();
return RPC_S_OK;
}
RPC_STATUS
LRPC_CCALL::GetBuffer (
IN OUT PRPC_MESSAGE Message,
IN UUID *ObjectUuid
)
/*++
Routine Description:
We will allocate a buffer which will be used to either send a request
or receive a response.
ObjectUuid - Ignored
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.
--*/
{
RPC_STATUS Status;
SetObjectUuid(ObjectUuid);
if (LrpcMessage == 0)
{
LrpcMessage = AllocateMessage();
if (LrpcMessage == 0)
{
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
}
if (PARTIAL(Message))
{
CurrentBufferLength = (Message->BufferLength < MINIMUM_PARTIAL_BUFFLEN)
? MINIMUM_PARTIAL_BUFFLEN:Message->BufferLength ;
Message->Buffer = RpcpFarAllocate(CurrentBufferLength);
if (Message->Buffer == 0)
{
CurrentBufferLength = 0 ;
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
}
else if (Message->BufferLength <= MAXIMUM_MESSAGE_BUFFER)
{
CurrentBufferLength = MAXIMUM_MESSAGE_BUFFER ;
// Uncomment to check for 16 byte alignment on 64 bit
// ASSERT(IsBufferAligned(LrpcMessage->Rpc.Buffer));
Message->Buffer = LrpcMessage->Rpc.Buffer;
LrpcMessage->Rpc.RpcHeader.Flags = LRPC_BUFFER_IMMEDIATE;
LrpcMessage->LpcHeader.u2.ZeroInit = 0;
LrpcMessage->LpcHeader.u1.s1.DataLength = (USHORT)
(Align4(Message->BufferLength) + sizeof(LRPC_RPC_HEADER));
return(RPC_S_OK);
}
else
{
CurrentBufferLength = Message->BufferLength ;
Message->Buffer = RpcpFarAllocate(Message->BufferLength);
if (Message->Buffer == 0)
{
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
}
LrpcMessage->Rpc.RpcHeader.Flags = LRPC_BUFFER_REQUEST;
LrpcMessage->Rpc.Request.CountDataEntries = 1;
LrpcMessage->Rpc.Request.DataEntries[0].Base = PtrToMsgPtr(Message->Buffer);
LrpcMessage->Rpc.Request.DataEntries[0].Size = Message->BufferLength;
LrpcMessage->LpcHeader.CallbackId = 0;
LrpcMessage->LpcHeader.u2.ZeroInit = 0;
LrpcMessage->LpcHeader.u2.s2.DataInfoOffset = sizeof(PORT_MESSAGE)
+ sizeof(LRPC_RPC_HEADER);
LrpcMessage->LpcHeader.u1.s1.DataLength = sizeof(LRPC_RPC_HEADER)
+ sizeof(PORT_DATA_INFORMATION);
Status = RPC_S_OK;
Cleanup:
if (Status != RPC_S_OK)
{
AbortCCall();
ASSERT(Status == RPC_S_OUT_OF_MEMORY);
}
return(Status);
}
RPC_STATUS
LpcError (
IN NTSTATUS NtStatus,
IN BOOL fDNE
)
{
if (NtStatus == STATUS_NO_MEMORY)
{
return(RPC_S_OUT_OF_MEMORY);
}
if (NtStatus == STATUS_INSUFFICIENT_RESOURCES)
{
return(RPC_S_OUT_OF_RESOURCES);
}
VALIDATE(NtStatus)
{
STATUS_INVALID_PORT_HANDLE,
STATUS_INVALID_HANDLE,
STATUS_PORT_DISCONNECTED,
STATUS_LPC_REPLY_LOST
} END_VALIDATE;
if ((NtStatus != STATUS_LPC_REPLY_LOST) && fDNE)
{
return (RPC_S_CALL_FAILED_DNE) ;
}
return (RPC_S_CALL_FAILED);
}
RPC_STATUS
LRPC_CCALL::AsyncSend (
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
Send an async request. This request can be either partial or complete.
Arguments:
Message - contains the request.
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status ;
NTSTATUS NtStatus ;
BOOL fRetVal ;
BOOL Shutup ;
ULONG_PTR fNonCausal;
ULONG AsyncStateFlags;
// If it is a small request, we send it here, otherwise, we
// use the helper function.
ASSERT(pAsync) ;
Status = CurrentBindingHandle->InqTransportOption(
RPC_C_OPT_BINDING_NONCAUSAL,
&fNonCausal);
ASSERT(Status == RPC_S_OK);
if (fNonCausal == 0)
{
LrpcMessage->Rpc.RpcHeader.Flags |= LRPC_CAUSAL;
}
if (LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_IMMEDIATE)
{
LrpcMessage->LpcHeader.u1.s1.TotalLength = sizeof(PORT_MESSAGE)
+ LrpcMessage->LpcHeader.u1.s1.DataLength;
LrpcMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_REQUEST;
LrpcMessage->Rpc.RpcHeader.Pad = 0;
LrpcMessage->Rpc.RpcHeader.ProcedureNumber = (unsigned short) Message->ProcNum;
LrpcMessage->Rpc.RpcHeader.PresentContext = GetOnTheWirePresentationContext();
if (CurrentSecurityContext)
{
LrpcMessage->Rpc.RpcHeader.SecurityContextId = CurrentSecurityContext->SecurityContextId;
}
else
{
LrpcMessage->Rpc.RpcHeader.SecurityContextId = -1;
}
ASSERT(CallId != (ULONG) -1);
LrpcMessage->Rpc.RpcHeader.CallId = CallId ;
if (UuidSpecified)
{
RpcpMemoryCopy(&(LrpcMessage->Rpc.RpcHeader.ObjectUuid),
&ObjectUuid, sizeof(UUID));
LrpcMessage->Rpc.RpcHeader.Flags |= LRPC_OBJECT_UUID;
}
else
{
// zero out the guid to prevent data leak
RpcpMemorySet(&(LrpcMessage->Rpc.RpcHeader.ObjectUuid),
0, sizeof(UUID));
}
// Make sure the port has not been closed due to an association abort.
if (Association->LpcClientPort)
{
NtStatus = NtRequestPort(Association->LpcClientPort,
(PORT_MESSAGE *) LrpcMessage) ;
}
else
{
NtStatus = STATUS_INVALID_PORT_HANDLE;
}
if (NT_ERROR(NtStatus))
{
FreeCCall() ;
return LpcError(NtStatus, TRUE) ;
}
Status = RPC_S_OK;
}
else
{
AsyncStateFlags = pAsync->Flags;
// Take the call mutex if this is an async pipe call, we can assume that
// it is async since we are in AsyncSend, we must check if its pipe.
// We need to take the mutex to avoid a race between setting fSendMoreExpected in AsyncSend and
// checking it in the thread pool when servicing a LRPC_SERVER_SEND_MORE (ProcessResponse). Without this
// we could end up not getting a notification for this send
if (PARTIAL(Message))
CallMutex.Request();
Status = SendRequest(Message, &Shutup) ;
if ((AsyncStateFlags & RPC_C_NOTIFY_ON_SEND_COMPLETE)
&& (Status == RPC_S_OK || Status == RPC_S_SEND_INCOMPLETE))
{
if (Shutup)
{
fSendMoreExpected = TRUE;
}
else
{
fSendMoreExpected = FALSE;
if (!IssueNotification(RpcSendComplete))
{
Status = RPC_S_OUT_OF_MEMORY ;
}
}
}
if (PARTIAL(Message))
CallMutex.Clear();
}
if (Status == RPC_S_OK)
{
CallMutex.Request();
if (AsyncStatus == RPC_S_CALL_FAILED)
{
LogEvent(SU_CCALL, EV_ABORT, this, (PVOID) 44, 44, 1, 0);
Status = RPC_S_CALL_FAILED;
CallMutex.Clear();
FreeCCall();
}
else
{
fSendComplete = 1;
CallMutex.Clear();
}
}
return(Status);
}
RPC_STATUS
LRPC_CCALL::AsyncReceive (
IN OUT PRPC_MESSAGE Message,
IN unsigned int Size
)
/*++
Routine Description:
Arguments:
Message - Contains the request. On return, it will contain the received buffer
and its length.
Size - Requested size.
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status;
int Extra = IsExtraMessage(Message);
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
if (!Extra && Message->Buffer)
{
ActuallyFreeBuffer((char *)Message->Buffer);
Message->Buffer = 0;
Message->BufferLength = 0;
}
CallMutex.Request();
if (BufferComplete)
{
Status = GetCoalescedBuffer(Message, Extra);
}
else
{
if (PARTIAL(Message))
{
if (RcvBufferLength < Size)
{
if (NOTIFY(Message))
{
NeededLength = Size ;
}
Status = RPC_S_ASYNC_CALL_PENDING;
}
else
{
Status = GetCoalescedBuffer(Message, Extra);
}
}
else
{
Status = AsyncStatus;
ASSERT(Status != RPC_S_OK);
}
}
CallMutex.Clear();
if (Status == RPC_S_OK
|| Status == RPC_S_ASYNC_CALL_PENDING)
{
return Status;
}
FreeCCall();
return Status;
}
RPC_STATUS
LRPC_CCALL::CancelAsyncCall (
IN BOOL fAbort
)
/*++
Function Name:CancelAsyncCall
Parameters:
fAbort - TRUE: the cancel is abortive, ie, the call completes immediately
FALSE: a cancel PDU is sent to the server, the call doesn't complete
until the server returns
Description:
Returns:
RPC_S_OK: The call was successfully cancelled
others - an error occured during the cancellation process
--*/
{
#if defined(BUILD_WOW6432)
char LrpcCancelMessageBuffer[sizeof(LRPC_CANCEL_MESSAGE) + 8];
LRPC_CANCEL_MESSAGE *LrpcCancelMessagePtr = (LRPC_CANCEL_MESSAGE *)AlignPtr8(LrpcCancelMessageBuffer);
#else
LRPC_CANCEL_MESSAGE LrpcCancelMessageBuffer;
LRPC_CANCEL_MESSAGE *LrpcCancelMessagePtr = &LrpcCancelMessageBuffer;
#endif
NTSTATUS NtStatus;
LogEvent(SU_CCALL, EV_ABORT, this, 0, fAbort, 1, 1);
//
// Notify the server that the call has been cancelled
//
LrpcCancelMessagePtr->LpcHeader.u1.s1.DataLength = sizeof(LRPC_CANCEL_MESSAGE)
- sizeof(PORT_MESSAGE);
LrpcCancelMessagePtr->LpcHeader.u1.s1.TotalLength = sizeof(LRPC_CANCEL_MESSAGE);
LrpcCancelMessagePtr->LpcHeader.u2.ZeroInit = 0;
SanitizeLpcHeader (&LrpcCancelMessagePtr->LpcHeader);
ASSERT(CallId != (ULONG) -1);
// zero out everything b/n MessageType and CallId
RPCP_ZERO_OUT_STRUCT_RANGE(LRPC_RPC_HEADER,
&LrpcCancelMessagePtr->RpcHeader,
Pad,
CallId
);
LrpcCancelMessagePtr->RpcHeader.CallId = CallId;
LrpcCancelMessagePtr->RpcHeader.MessageType = LRPC_MSG_CANCEL;
// Make sure the port has not been closed due to an association abort.
if (Association->LpcClientPort)
{
NtStatus = NtRequestPort(Association->LpcClientPort,
(PORT_MESSAGE *) LrpcCancelMessagePtr) ;
}
else
{
NtStatus = STATUS_INVALID_PORT_HANDLE;
}
// sending the notification to the server is a best effort. We ignore the
// result
if (fAbort)
{
//
// If the cancel was abortive, complete the call right away.
//
//
// We indicate completion. When the app calls RpcAsyncCompleteCall
// we will destroy the call. That is fine, even if the server
// hasn't replied yet, because if the server sends a reply to
// a call or an association that is not there, the client code
// is protected, and will simply free the packet.
//
CallFailed(RPC_S_CALL_CANCELLED);
}
return RPC_S_OK;
}
void
LRPC_CCALL::ProcessResponse(
IN LRPC_MESSAGE *LrpcResponse
)
/*++
Routine Description:
A buffer has just arrived, process it. If some other buffer is already
processing buffers, simply queue it and go away. Otherwise, does
the processing ourselves.
Arguments:
Message - Details on the arrived message
--*/
{
RPC_MESSAGE Message ;
RPC_STATUS Status ;
BOOL fRetVal = 0;
BOOL fFault2;
RPC_STATUS FaultStatus;
THREAD *ThisThread;
ExtendedErrorInfo *EEInfo;
DelayedPipeAckData AckData;
//
// So that abort will not issue a notification
//
fSendComplete = 0;
switch (LrpcResponse->Rpc.RpcHeader.MessageType)
{
case LRPC_MSG_FAULT:
if (LrpcResponse->Fault.RpcHeader.Flags & LRPC_EEINFO_PRESENT)
{
ThisThread = RpcpGetThreadPointer();
ASSERT(ThisThread);
ASSERT(ThisThread->GetEEInfo() == NULL);
if (LrpcResponse->Fault.LpcHeader.u1.s1.TotalLength >
(sizeof(LRPC_FAULT_MESSAGE) - sizeof(LrpcResponse->Fault.Buffer)))
{
Status = UnpickleEEInfo(LrpcResponse->Fault.Buffer,
LrpcResponse->Fault.LpcHeader.u1.s1.TotalLength
- sizeof(LRPC_FAULT_MESSAGE)
+ sizeof(LrpcResponse->Fault.Buffer),
&EEInfo);
if (Status == RPC_S_OK)
{
this->EEInfo = EEInfo;
}
}
}
if (pAsync == 0)
{
AsyncStatus = LrpcResponse->Fault.RpcStatus ;
SyncEvent.Raise();
}
else
{
CallFailed(LrpcResponse->Fault.RpcStatus);
}
FreeMessage(LrpcResponse);
return ;
case LRPC_CLIENT_SEND_MORE:
if (pAsync
&& (pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE))
{
// Take the call mutex if this is an async pipe call, we can assume that
// it is async since we are in AsyncSend, we must check if its pipe.
// We need to take the mutex to avoid a race between setting fSendMoreExpected in AsyncSend and
// checking it in the thread pool when servicing a LRPC_SERVER_SEND_MORE (ProcessResponse). Without this
// we could end up not getting a notification for this send
CallMutex.Request();
if (fSendMoreExpected)
{
CallMutex.Clear();
if (!IssueNotification(RpcSendComplete))
{
CallFailed(RPC_S_OUT_OF_MEMORY);
}
}
else
{
CallMutex.Clear();
CORRUPTION_ASSERT(0);
CallFailed(RPC_S_PROTOCOL_ERROR);
}
}
FreeMessage(LrpcResponse);
return;
}
ASSERT((LrpcResponse->Rpc.RpcHeader.MessageType == LRPC_MSG_FAULT2)
|| (LrpcResponse->Rpc.RpcHeader.MessageType == LRPC_MSG_RESPONSE));
if (LrpcResponse->Rpc.RpcHeader.MessageType == LRPC_MSG_FAULT2)
{
fFault2 = TRUE;
FaultStatus = LrpcResponse->Fault2.RpcStatus;
}
else
fFault2 = FALSE;
Message.RpcFlags = 0;
AckData.DelayedAckPipeNeeded = FALSE;
Status = LrpcMessageToRpcMessage(LrpcResponse,
&Message,
Association->LpcReceivePort,
TRUE, // IsReplyFromBackConnection
&AckData
) ;
if (fFault2 && (Status == RPC_S_OK))
{
ThisThread = RpcpGetThreadPointer();
ASSERT(ThisThread);
ASSERT(ThisThread->GetEEInfo() == NULL);
Status = UnpickleEEInfo((unsigned char *)Message.Buffer,
Message.BufferLength,
&EEInfo);
if (Status == RPC_S_OK)
{
this->EEInfo = EEInfo;
}
// else
// fall through the error case below, which will
// handle the failure properly
}
if ((Status != RPC_S_OK) || fFault2)
{
// remember to send delayed ack if any
if (AckData.DelayedAckPipeNeeded)
{
(void) SendPipeAck(Association->LpcReceivePort,
LrpcResponse,
AckData.CurrentStatus);
if ((Status != RPC_S_OK) && (Message.Buffer))
{
RpcpFarFree(Message.Buffer);
}
FreeMessage(LrpcResponse) ;
}
if (fFault2)
{
AsyncStatus = FaultStatus;
Status = FaultStatus;
}
else
AsyncStatus = Status ;
if (pAsync == 0)
{
SyncEvent.Raise();
}
else
{
CallFailed(Status);
}
return;
}
CallMutex.Request() ;
// we have taken the mutex - now we can send the ack
// The reason we need to wait for the mutex to be
// taken before we send the delayed ack for pipes is
// that once we send an ack, the server will send more
// data and these can race with this thread. To be
// safe, we need to take the mutex.
if (AckData.DelayedAckPipeNeeded)
{
Status = SendPipeAck(Association->LpcReceivePort,
LrpcResponse,
AckData.CurrentStatus);
FreeMessage(LrpcResponse) ;
if (Status != RPC_S_OK)
{
CallMutex.Clear();
if (Message.Buffer)
{
RpcpFarFree(Message.Buffer);
}
AsyncStatus = Status ;
if (pAsync == 0)
{
SyncEvent.Raise();
}
else
{
CallFailed(Status);
}
return;
}
}
if (COMPLETE(&Message))
{
BufferComplete = 1;
}
RcvBufferLength += Message.BufferLength ;
if (Message.BufferLength)
{
if (BufferQueue.PutOnQueue(Message.Buffer,
Message.BufferLength))
{
Status = RPC_S_OUT_OF_MEMORY ;
#if DBG
PrintToDebugger("RPC: PutOnQueue failed\n") ;
#endif
if (pAsync)
{
CallMutex.Clear();
CallFailed(Status);
return;
}
else
{
AsyncStatus = Status;
}
}
}
if (pAsync == 0)
{
CallMutex.Clear() ;
SyncEvent.Raise();
return;
}
if (BufferComplete)
{
AsyncStatus = RPC_S_OK;
CallMutex.Clear() ;
IssueNotification();
}
else
{
if (NeededLength > 0 && RcvBufferLength >= NeededLength)
{
CallMutex.Clear() ;
IssueNotification(RpcReceiveComplete);
}
else
{
CallMutex.Clear() ;
}
}
}
RPC_STATUS
LRPC_CCALL::GetCoalescedBuffer (
IN PRPC_MESSAGE Message,
IN BOOL BufferValid
)
/*++
Routine Description:
Remove buffers from the queue and coalesce them into a single buffer.
Arguments:
Message - on return this will contain the coalesced buffer, Message->BufferLength
gives us the length of the coalesced buffer.
BufferValid - Tells us if Message->Buffer is valid on entry.
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
void *NewBuffer, *Buffer ;
char *Current ;
unsigned int bufferlength ;
unsigned int TotalLength ;
LRPC_SENDMORE_MESSAGE SendMore ;
NTSTATUS NtStatus ;
CallMutex.Request() ;
if (RcvBufferLength == 0)
{
if (BufferComplete)
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
}
if (BufferValid == 0)
{
Message->Buffer = 0;
Message->BufferLength = 0;
}
CallMutex.Clear();
return RPC_S_OK;
}
BOOL fFillNewBuffer;
if (BufferValid)
{
TotalLength = RcvBufferLength + Message->BufferLength ;
NewBuffer = RpcpFarAllocate(TotalLength) ;
if (NewBuffer == 0)
{
CallMutex.Clear() ;
return RPC_S_OUT_OF_MEMORY;
}
RpcpMemoryCopy(NewBuffer, Message->Buffer, Message->BufferLength) ;
Current = (char *) NewBuffer + Message->BufferLength ;
fFillNewBuffer = 1;
}
else
{
TotalLength = RcvBufferLength ;
if (BufferQueue.Size() == 1)
{
Buffer = BufferQueue.TakeOffQueue(&bufferlength);
ASSERT(Buffer);
NewBuffer = Buffer;
ASSERT(TotalLength == bufferlength);
fFillNewBuffer = 0;
}
else
{
NewBuffer = RpcpFarAllocate(TotalLength) ;
if (NewBuffer == 0)
{
CallMutex.Clear() ;
return RPC_S_OUT_OF_MEMORY;
}
Current = (char *) NewBuffer;
fFillNewBuffer = 1;
}
}
if (fFillNewBuffer)
{
while ((Buffer = BufferQueue.TakeOffQueue(&bufferlength)) != 0)
{
RpcpMemoryCopy(Current, Buffer, bufferlength) ;
Current += bufferlength ;
ActuallyFreeBuffer(Buffer);
}
}
if (Message->Buffer)
{
ActuallyFreeBuffer(Message->Buffer);
}
Message->Buffer = NewBuffer ;
Message->BufferLength = TotalLength ;
RcvBufferLength = 0;
if (BufferComplete)
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
}
else
{
if (Choked)
{
CallMutex.Clear() ;
//
// send a message to the server
// to start sending data again
//
SendMore.LpcHeader.u1.s1.DataLength =
sizeof(SendMore) - sizeof(PORT_MESSAGE);
SendMore.LpcHeader.u1.s1.TotalLength = sizeof(SendMore);
SendMore.LpcHeader.u2.ZeroInit = 0;
SendMore.LpcHeader.CallbackId = 0;
SendMore.LpcHeader.MessageId = 0;
SanitizeLpcHeader (&SendMore.LpcHeader);
// zero out everything b/n MessageType and CallId
RPCP_ZERO_OUT_STRUCT_RANGE(LRPC_RPC_HEADER,
&SendMore.RpcHeader,
Pad,
CallId
);
SendMore.RpcHeader.MessageType = LRPC_SERVER_SEND_MORE;
SendMore.RpcHeader.CallId = CallId ;
ASSERT(CallId != (ULONG) -1);
// Make sure the port has not been closed due to an association abort.
if (Association->LpcClientPort)
{
NtStatus = NtRequestPort(Association->LpcClientPort,
(PORT_MESSAGE *) &SendMore) ;
}
else
{
NtStatus = STATUS_INVALID_PORT_HANDLE;
}
if (!NT_SUCCESS(NtStatus))
{
return RPC_S_CALL_FAILED ;
}
return RPC_S_OK;
}
}
CallMutex.Clear() ;
return RPC_S_OK ;
}
void
LRPC_CCALL::ServerAborted (
IN OUT int *waitIterations
)
/*++
Routine Description:
The server has died, we need the call needs to reflect that, and
cleanup if possible.
--*/
{
if (pAsync)
{
int i;
for (;*waitIterations && AsyncStatus == RPC_S_ASYNC_CALL_PENDING; (*waitIterations)--)
{
Sleep(500);
}
LogEvent(SU_CCALL, EV_ABORT, this, (PVOID) 22, 22, 1, 0);
CallMutex.Request();
if (AsyncStatus == RPC_S_ASYNC_CALL_PENDING)
{
if (fSendComplete == 0)
{
AsyncStatus = RPC_S_CALL_FAILED;
CallMutex.Clear();
}
else
{
CallMutex.Clear();
CallFailed(RPC_S_CALL_FAILED);
}
}
else
{
CallMutex.Clear();
}
}
}
RPC_STATUS
LRPC_CCALL::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.
--*/
{
NTSTATUS NtStatus;
RPC_STATUS ExceptionCode, Status;
void * OriginalMessageBuffer;
LRPC_MESSAGE *SavedLrpcMessage = 0;
LRPC_MESSAGE *TmpLrpcMessage = 0;
int ActiveCallSetupFlag = 0;
void * TempBuffer;
ExtendedErrorInfo *EEInfo;
DebugClientCallInfo *ClientCallInfo;
DebugCallTargetInfo *CallTargetInfo;
CellTag ClientCallInfoCellTag;
CellTag CallTargetInfoCellTag;
THREAD *ThisThread = RpcpGetThreadPointer();
BOOL fDebugInfoSet = FALSE;
if (CallAbortedFlag != 0)
{
//
// Don't know if it is safe to free the buffer here
//
return(RPC_S_CALL_FAILED_DNE);
}
ASSERT(ThisThread);
// if either client side debugging is enabled or we are
// calling on a thread that has a scall dispatched
if ((IsClientSideDebugInfoEnabled() || ((ThisThread->Context) && IsServerSideDebugInfoEnabled())) && (RecursionCount == 0))
{
CStackAnsi AnsiString;
RPC_CHAR *Endpoint;
int EndpointLength;
if (!IsClientSideDebugInfoEnabled())
{
Status = SetDebugClientCallInformation(&ClientCallInfo, &ClientCallInfoCellTag,
&CallTargetInfo, &CallTargetInfoCellTag, Message, ThisThread->DebugCell,
ThisThread->DebugCellTag);
}
else
{
Status = SetDebugClientCallInformation(&ClientCallInfo, &ClientCallInfoCellTag,
&CallTargetInfo, &CallTargetInfoCellTag, Message, NULL, NULL);
}
if (Status != RPC_S_OK)
{
TempBuffer = NULL;
goto Cleanup;
}
ClientCallInfo->CallID = CallId;
Endpoint = Association->InqEndpoint();
EndpointLength = RpcpStringLength(Endpoint) + 1;
*(AnsiString.GetPAnsiString()) = (char *)_alloca(EndpointLength);
Status = AnsiString.Attach(Endpoint, EndpointLength, EndpointLength * 2);
// effectively ignore failure in the conversion
if (Status == RPC_S_OK)
{
strncpy(ClientCallInfo->Endpoint, AnsiString, sizeof(ClientCallInfo->Endpoint));
}
CallTargetInfo->ProtocolSequence = LRPC_TOWER_ID;
CallTargetInfo->TargetServer[0] = 0;
fDebugInfoSet = TRUE;
}
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
if (CallStack == 0)
{
if (UuidSpecified)
{
RpcpMemoryCopy(&(LrpcMessage->Rpc.RpcHeader.ObjectUuid),
&ObjectUuid, sizeof(UUID));
LrpcMessage->Rpc.RpcHeader.Flags |= LRPC_OBJECT_UUID;
}
}
else
{
LrpcMessage->LpcHeader.u2.s2.Type = LPC_REQUEST;
LrpcMessage->LpcHeader.ClientId = ClientIdToMsgClientId(ClientId);
LrpcMessage->LpcHeader.MessageId = MessageId;
LrpcMessage->LpcHeader.CallbackId = CallbackId;
}
LrpcMessage->LpcHeader.u1.s1.TotalLength = sizeof(PORT_MESSAGE)
+ LrpcMessage->LpcHeader.u1.s1.DataLength;
LrpcMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_REQUEST;
LrpcMessage->Rpc.RpcHeader.ProcedureNumber = (unsigned short) Message->ProcNum;
LrpcMessage->Rpc.RpcHeader.PresentContext = GetOnTheWirePresentationContext();
if (CurrentSecurityContext)
{
LrpcMessage->Rpc.RpcHeader.SecurityContextId = CurrentSecurityContext->SecurityContextId;
// Make sure we can't be impersonated on the transport level if
// static identity tracking is being used. We may be under different
// identity now then we were during the bind. The server is supposed
// to capture the token during bind and use it later under
// static idenitity tracking.
ASSERT(CurrentBindingHandle != NULL);
if (CurrentBindingHandle->GetIdentityTracking() == RPC_C_QOS_IDENTITY_STATIC)
{
LrpcMessage->LpcHeader.u2.s2.Type |= LPC_NO_IMPERSONATE;
}
}
else
{
LrpcMessage->Rpc.RpcHeader.SecurityContextId = -1;
}
TempBuffer = Message->Buffer;
LrpcMessage->Rpc.RpcHeader.Flags |= LRPC_SYNC_CLIENT | LRPC_NON_PIPE;
LrpcMessage->Rpc.RpcHeader.Pad = 0;
// Make sure the port has not been closed due to an association abort.
if (Association->LpcClientPort)
{
NtStatus = NtRequestWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) LrpcMessage,
(PORT_MESSAGE *) LrpcMessage);
}
else
{
NtStatus = STATUS_INVALID_PORT_HANDLE;
}
if (NT_ERROR(NtStatus))
{
if (NtStatus == STATUS_NO_MEMORY)
{
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
if (NtStatus == STATUS_INSUFFICIENT_RESOURCES)
{
Status = RPC_S_OUT_OF_RESOURCES;
goto Cleanup;
}
VALIDATE(NtStatus)
{
STATUS_INVALID_PORT_HANDLE,
STATUS_INVALID_HANDLE,
STATUS_PORT_DISCONNECTED,
STATUS_LPC_REPLY_LOST
} END_VALIDATE;
Association->AbortAssociation();
if ((CallStack == 0)
&& (NtStatus != STATUS_LPC_REPLY_LOST))
{
//
// It's possible that the server stopped and has now restarted.
// We'll try re-binding and only fail if the new call fails.
//
// We can only retry if we are SURE that the server did not
// execute the request.
if (RecursionCount > 3)
{
// Prevent an infinite loop when GetBuffer returns ok but
// the SendReceive always fails.
Status = RPC_S_CALL_FAILED_DNE;
}
else
{
Status = AutoRetryCall(Message,
TRUE // fFromSendReceive
);
}
}
else
{
// In a callback and/or couldn't retry.
Status = RPC_S_CALL_FAILED;
}
Cleanup:
if (fDebugInfoSet)
{
FreeCell(CallTargetInfo, &CallTargetInfoCellTag);
FreeCell(ClientCallInfo, &ClientCallInfoCellTag);
}
ActuallyFreeBuffer(TempBuffer);
AbortCCall();
return Status;
}
// The message was sent and we got a reply okay.
ActuallyFreeBuffer(Message->Buffer);
for (;;)
{
if (LrpcMessage->Rpc.RpcHeader.MessageType == LRPC_MSG_FAULT)
{
if (LrpcMessage->Fault.RpcHeader.Flags & LRPC_EEINFO_PRESENT)
{
Status = UnpickleEEInfo(LrpcMessage->Fault.Buffer,
LrpcMessage->Fault.LpcHeader.u1.s1.TotalLength
- sizeof(LRPC_FAULT_MESSAGE)
+ sizeof(LrpcMessage->Fault.Buffer),
&EEInfo);
if (Status == RPC_S_OK)
{
RpcpSetEEInfoForThread(ThisThread, EEInfo);
}
// else we just fall through and return an error code -
// this is best effort, so it's Ok
}
Status = LrpcMessage->Fault.RpcStatus;
break;
}
if ((LrpcMessage->Rpc.RpcHeader.MessageType == LRPC_MSG_RESPONSE)
|| (LrpcMessage->Rpc.RpcHeader.MessageType == LRPC_MSG_FAULT2))
{
BOOL fFault2;
RPC_STATUS FaultStatus;
// remember if the message was fault2
if (LrpcMessage->Rpc.RpcHeader.MessageType == LRPC_MSG_FAULT2)
{
fFault2 = TRUE;
FaultStatus = LrpcMessage->Fault2.RpcStatus;
}
else
fFault2 = FALSE;
Status = LrpcMessageToRpcMessage(LrpcMessage,
Message,
Association->LpcClientPort,
FALSE, // IsReplyFromBackConnection
NULL // StatusIfDelayedAck
);
if (fFault2)
{
if (Status == RPC_S_OK)
{
Status = UnpickleEEInfo((unsigned char *)Message->Buffer,
Message->BufferLength,
&EEInfo);
if (Status == RPC_S_OK)
{
RpcpSetEEInfoForThread(ThisThread, EEInfo);
}
// else
// fall through to restoring the original status
}
// the status of the retrieval of
// the extended error info is irrelevant - we
// need to restore the original fault code
Status = FaultStatus;
}
break;
}
ASSERT(LrpcMessage->Rpc.RpcHeader.MessageType
== LRPC_MSG_CALLBACK);
CallStack += 1;
Status = RPC_S_OK;
if ((CallStack == 1)
&& (ActiveCallSetupFlag == 0))
{
ClientId = MsgClientIdToClientId(LrpcMessage->LpcHeader.ClientId);
MessageId = LrpcMessage->LpcHeader.MessageId;
CallbackId = LrpcMessage->LpcHeader.CallbackId;
RecursiveCallsKey = CurrentBindingHandle->AddRecursiveCall(this);
if (RecursiveCallsKey == -1)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else
{
ActiveCallSetupFlag = 1;
}
}
if (SavedLrpcMessage == 0)
{
// First callback, we may need to allocated a new LRPC_MESSAGE.
if (CachedLrpcMessage == 0)
{
CachedLrpcMessage = AllocateMessage() ;
}
if (CachedLrpcMessage == 0)
Status = RPC_S_OUT_OF_MEMORY;
}
if (Status == RPC_S_OK)
{
Status = LrpcMessageToRpcMessage(LrpcMessage,
Message,
Association->LpcClientPort,
FALSE, // IsReplyFromBackConnection
NULL // StatusIfDelayedAck
);
}
if (Status != RPC_S_OK)
{
ActuallyFreeBuffer(Message->Buffer);
LrpcMessage->Fault.RpcHeader.MessageType = LRPC_MSG_FAULT;
LrpcMessage->Fault.RpcStatus = LrpcMapRpcStatus(Status);
LrpcMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_FAULT_MESSAGE) - sizeof(PORT_MESSAGE);
LrpcMessage->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_FAULT_MESSAGE);
LrpcMessage->LpcHeader.ClientId = ClientIdToMsgClientId(ClientId);
LrpcMessage->LpcHeader.MessageId = MessageId;
LrpcMessage->LpcHeader.CallbackId = CallbackId;
// Make sure the port has not been closed due to an association abort.
if (Association->LpcClientPort)
{
NtStatus = NtReplyWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) LrpcMessage);
}
else
{
NtStatus = STATUS_INVALID_PORT_HANDLE;
}
}
else
{
PRPC_DISPATCH_TABLE DispatchTableToUse;
OriginalMessageBuffer = Message->Buffer;
Message->TransferSyntax = Binding->GetTransferSyntaxId();
Message->ProcNum = LrpcMessage->Rpc.RpcHeader.ProcedureNumber;
if (SavedLrpcMessage == 0)
{
// First callback
ASSERT(CachedLrpcMessage != 0);
SavedLrpcMessage = LrpcMessage;
LrpcMessage = CachedLrpcMessage;
CachedLrpcMessage = 0;
}
else
{
// >First callback, LrpcMessage and SavedLrpcMessages swap roles
TmpLrpcMessage = SavedLrpcMessage;
SavedLrpcMessage = LrpcMessage;
LrpcMessage = TmpLrpcMessage;
}
// Check to make sure this procnum can actually receive callbacks and
// a valid callback is specified.
// On error - ignore the PDU.
PRPC_DISPATCH_TABLE DispatchTableCallback = Binding->GetDispatchTable();
if (DispatchTableCallback &&
Message->ProcNum < DispatchTableCallback->DispatchTableCount &&
DispatchTableCallback->DispatchTable[Message->ProcNum] != NULL)
{
Status = DispatchCallback(DispatchTableCallback,
Message,
&ExceptionCode);
}
else
{
CORRUPTION_ASSERT(0);
Status = RPC_S_PROTOCOL_ERROR;
}
if (OriginalMessageBuffer != SavedLrpcMessage->Rpc.Buffer)
{
ActuallyFreeBuffer(OriginalMessageBuffer);
}
if (Status != RPC_S_OK)
{
VALIDATE(Status)
{
RPC_P_EXCEPTION_OCCURED,
RPC_S_PROCNUM_OUT_OF_RANGE
} END_VALIDATE;
if (Status == RPC_P_EXCEPTION_OCCURED)
{
Status = LrpcMapRpcStatus(ExceptionCode);
}
LrpcMessage->Fault.RpcStatus = Status;
LrpcMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_FAULT_MESSAGE) - sizeof(PORT_MESSAGE);
LrpcMessage->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_FAULT_MESSAGE);
LrpcMessage->Fault.RpcHeader.MessageType = LRPC_MSG_FAULT;
}
else
{
LrpcMessage->Rpc.RpcHeader.MessageType =
LRPC_MSG_RESPONSE;
if (LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_REQUEST)
{
Status = MakeServerCopyResponse();
if (Status != RPC_S_OK)
{
break;
}
}
}
LrpcMessage->LpcHeader.ClientId = ClientIdToMsgClientId(ClientId);
LrpcMessage->LpcHeader.MessageId = MessageId;
LrpcMessage->LpcHeader.CallbackId = CallbackId;
LrpcMessage->LpcHeader.u1.s1.TotalLength =
LrpcMessage->LpcHeader.u1.s1.DataLength + sizeof(PORT_MESSAGE);
// Make sure the port has not been closed due to an association abort.
if (Association->LpcClientPort)
{
NtStatus = NtReplyWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) LrpcMessage);
}
else
{
NtStatus = STATUS_INVALID_PORT_HANDLE;
}
RpcpPurgeEEInfo();
}
CallStack -= 1;
if (NT_ERROR(NtStatus))
{
if (NtStatus == STATUS_NO_MEMORY)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else if (NtStatus == STATUS_INSUFFICIENT_RESOURCES)
{
Status = RPC_S_OUT_OF_RESOURCES;
}
else
{
Association->AbortAssociation();
VALIDATE(NtStatus)
{
STATUS_INVALID_PORT_HANDLE,
STATUS_INVALID_HANDLE,
STATUS_PORT_DISCONNECTED,
STATUS_LPC_REPLY_LOST
} END_VALIDATE;
Status = RPC_S_CALL_FAILED;
}
break;
}
}
if (SavedLrpcMessage != 0)
{
if (CachedLrpcMessage != 0)
{
FreeMessage(CachedLrpcMessage) ;
}
CachedLrpcMessage = SavedLrpcMessage;
}
if (ActiveCallSetupFlag != 0)
{
CurrentBindingHandle->RemoveRecursiveCall(RecursiveCallsKey);
}
if (Status != RPC_S_OK)
{
if (CallStack == 0)
{
FreeCCall();
}
}
if (fDebugInfoSet)
{
FreeCell(CallTargetInfo, &CallTargetInfoCellTag);
FreeCell(ClientCallInfo, &ClientCallInfoCellTag);
}
return(Status);
}
RPC_STATUS
LRPC_CCALL::SendRequest (
IN OUT PRPC_MESSAGE Message,
OUT BOOL *Shutup
)
/*++
Routine Description:
Helper function used for sending async requests or pipe requests
Arguments:
Message - request message
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
RPC_S_SEND_INCOMPLETE - we were unable to send the complete request.
--*/
{
NTSTATUS NtStatus;
RPC_STATUS ExceptionCode, Status;
void * OriginalMessageBuffer;
LRPC_MESSAGE *TmpLrpcMessage = 0;
void * TempBuffer;
LRPC_MESSAGE *LrpcReplyMessage ;
int RemainingLength = 0;
ASSERT((LrpcMessage->Rpc.RpcHeader.Flags
& LRPC_BUFFER_IMMEDIATE) == 0) ;
*Shutup = 0;
if (CallAbortedFlag != 0)
{
return(RPC_S_CALL_FAILED_DNE);
}
if (CallStack > 0)
{
return (RPC_S_CALL_FAILED);
}
if (PARTIAL(Message))
{
if (Message->BufferLength < MINIMUM_PARTIAL_BUFFLEN)
{
return (RPC_S_SEND_INCOMPLETE);
}
LrpcMessage->Rpc.RpcHeader.Flags |= LRPC_BUFFER_PARTIAL ;
if (NOT_MULTIPLE_OF_EIGHT(Message->BufferLength))
{
RemainingLength = Message->BufferLength & LOW_BITS ;
Message->BufferLength &= ~LOW_BITS ;
}
}
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
ASSERT(CallId != (ULONG) -1);
LrpcMessage->Rpc.RpcHeader.CallId = CallId ;
if (FirstFrag)
{
LrpcMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_REQUEST;
}
else
{
LrpcMessage->Rpc.RpcHeader.MessageType = LRPC_PARTIAL_REQUEST;
}
LrpcMessage->LpcHeader.u1.s1.TotalLength =
sizeof(PORT_MESSAGE) + LrpcMessage->LpcHeader.u1.s1.DataLength;
LrpcMessage->Rpc.Request.DataEntries[0].Size = Message->BufferLength;
LrpcMessage->Rpc.RpcHeader.ProcedureNumber = (unsigned short) Message->ProcNum;
LrpcMessage->Rpc.RpcHeader.PresentContext = GetOnTheWirePresentationContext();
LrpcMessage->Rpc.RpcHeader.Pad = 0;
if (CurrentSecurityContext)
{
LrpcMessage->Rpc.RpcHeader.SecurityContextId = CurrentSecurityContext->SecurityContextId;
}
else
{
LrpcMessage->Rpc.RpcHeader.SecurityContextId = -1;
}
if (UuidSpecified)
{
ASSERT(CallStack == 0) ;
RpcpMemoryCopy(&(LrpcMessage->Rpc.RpcHeader.ObjectUuid),
&ObjectUuid, sizeof(UUID));
LrpcMessage->Rpc.RpcHeader.Flags |= LRPC_OBJECT_UUID;
}
else
{
// zero out uninitialized data
RpcpMemorySet(&(LrpcMessage->Rpc.RpcHeader.ObjectUuid),
0, sizeof(UUID));
}
// Make sure we can't be impersonated on the transport level if
// static identity tracking is being used.
ASSERT(CurrentBindingHandle != NULL);
if (CurrentBindingHandle->GetIdentityTracking() == RPC_C_QOS_IDENTITY_STATIC)
{
LrpcMessage->LpcHeader.u2.s2.Type |= LPC_NO_IMPERSONATE;
}
if (Association->LpcClientPort)
{
NtStatus = NtRequestWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) LrpcMessage,
(PORT_MESSAGE *) LrpcMessage);
}
else
{
NtStatus = STATUS_INVALID_PORT_HANDLE;
}
if (NT_ERROR(NtStatus))
{
TempBuffer = Message->Buffer;
if (NtStatus == STATUS_NO_MEMORY)
{
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
if (NtStatus == STATUS_INSUFFICIENT_RESOURCES)
{
Status = RPC_S_OUT_OF_RESOURCES;
goto Cleanup;
}
VALIDATE(NtStatus)
{
STATUS_INVALID_PORT_HANDLE,
STATUS_INVALID_HANDLE,
STATUS_PORT_DISCONNECTED,
STATUS_LPC_REPLY_LOST
} END_VALIDATE;
if (pAsync)
{
ASSERT(RecursionCount == 0);
if (NtStatus != STATUS_LPC_REPLY_LOST)
{
Status = RPC_S_CALL_FAILED_DNE;
}
else
{
Status = RPC_S_CALL_FAILED;
}
goto Cleanup;
}
Association->AbortAssociation();
if ((NtStatus != STATUS_LPC_REPLY_LOST) && FirstFrag)
{
ASSERT(CallStack == 0) ;
//
// It's possible that the server stopped and has now restarted.
// We'll try re-binding and only fail if the new call fails.
//
// We can only retry if we are SURE that the server did not
// execute the request.
if (RecursionCount > 3)
{
// Prevent an infinite loop when GetBuffer returns ok but
// the SendReceive always fails.
Status = RPC_S_CALL_FAILED_DNE;
}
else
{
Status = AutoRetryCall(Message,
FALSE // fFromSendReceive
);
}
}
else
{
// In a callback and/or couldn't retry.
Status = RPC_S_CALL_FAILED;
}
Cleanup:
ActuallyFreeBuffer(TempBuffer);
AbortCCall();
return Status;
}
else
{
FirstFrag = 0;
}
if (LrpcMessage->Rpc.RpcHeader.MessageType == LRPC_MSG_ACK)
{
*Shutup = LrpcMessage->Ack.Shutup ;
if (PARTIAL(Message))
{
if (LrpcMessage->Ack.RpcStatus == RPC_S_OK)
{
if (RemainingLength)
{
RpcpMemoryMove(Message->Buffer,
(char *) Message->Buffer + Message->BufferLength,
RemainingLength) ;
Message->BufferLength = RemainingLength ;
return (RPC_S_SEND_INCOMPLETE) ;
}
return RPC_S_OK;
}
}
ActuallyFreeBuffer(Message->Buffer);
Message->Buffer = 0;
return LrpcMessage->Ack.RpcStatus ;
}
ActuallyFreeBuffer(Message->Buffer);
Message->Buffer = 0;
if (LrpcMessage->Rpc.RpcHeader.MessageType == LRPC_MSG_RESPONSE)
{
ASSERT(!PARTIAL(Message)) ;
CurrentBufferLength = 0;
Status = LrpcMessageToRpcMessage(LrpcMessage,
Message,
Association->LpcClientPort,
FALSE, // IsReplyFromBackConnection
NULL // StatusIfDelayedAck
);
if (Status == RPC_S_OK
&& COMPLETE(Message))
{
BufferComplete = 1;
}
Message->RpcFlags = 0;
// we have no out pipes
ASSERT(Status != RPC_S_OK || BufferComplete) ;
}
else if (LrpcMessage->Rpc.RpcHeader.MessageType == LRPC_MSG_FAULT)
{
CurrentBufferLength = 0;
Status = LrpcMessage->Fault.RpcStatus;
}
else
{
// The server should only return messages of types: LRPC_MSG_ACK,
// LRPC_MSG_RESPONSE, and LRPC_MSG_FAULT. All of these have been handled in
// the above clauses.
ASSERT(0 && "Invalid message type");
Status = RPC_S_CALL_FAILED;
}
if (Status != RPC_S_OK)
{
ASSERT(CallStack == 0) ;
FreeCCall();
}
return Status ;
}
RPC_STATUS
LRPC_CCALL::AutoRetryCall (
IN OUT PRPC_MESSAGE Message,
BOOL fFromSendReceive
)
{
RPC_STATUS Status;
void *OldBuffer;
UUID *UuidToUse;
LRPC_CCALL *NewCall;
// any failure after this is unrelated
RpcpPurgeEEInfo();
OldBuffer = Message->Buffer;
Message->Handle = (RPC_BINDING_HANDLE) CurrentBindingHandle;
if (UuidSpecified)
{
UuidToUse = &ObjectUuid;
}
else
{
UuidToUse = 0;
}
Status = CurrentBindingHandle->NegotiateTransferSyntax(Message);
if (Status != RPC_S_OK)
goto CleanupAndReturn;
NewCall = ((LRPC_CCALL *)(Message->Handle));
Status = NewCall->GetBuffer(Message, UuidToUse);
if (Status != RPC_S_OK)
goto CleanupAndReturn;
ASSERT(Message->Buffer != OldBuffer);
RpcpMemoryCopy(Message->Buffer, OldBuffer,
Message->BufferLength);
// This CCALL should be freed,
// a new one was allocated in NegotiateTransferSyntax and is now being used.
ASSERT(NewCall != this);
NewCall->SetRecursionCount(RecursionCount + 1);
if (fFromSendReceive)
Status = NewCall->SendReceive(Message);
else
Status = NewCall->Send(Message);
// the caller has remembered the old buffer and call object,
// and will clean them up regardless of what we return - our
// job is simply to allocate a new call and buffer, and stick
// them in the Message
CleanupAndReturn:
if (Status == RPC_S_SERVER_UNAVAILABLE)
{
// Since we're retrying, if the server has gone missing,
// it just means that the call failed.
Status = RPC_S_CALL_FAILED_DNE;
}
return(Status);
}
RPC_STATUS
LRPC_CCALL::Send (
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
This rountine is used by pipes to send partila data...
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.
--*/
{
RPC_STATUS Status ;
BOOL Shutup ;
Status = SendRequest(Message, &Shutup) ;
return(Status);
}
RPC_STATUS
LRPC_CCALL::Receive (
IN PRPC_MESSAGE Message,
IN unsigned int Size
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
int size = 0 ;
int BufferLength ;
int RequestedLength ;
RPC_STATUS Status ;
int ActualBufferLength = 0;
int Extra = IsExtraMessage(Message) ;
if (BufferComplete
&& RcvBufferLength == 0)
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
return (RPC_S_OK) ;
}
// If you get here, it means that you have out pipe data.
//
// allocate a buffer big enough to hold the out data:
// if you have a partial receive, you can allocate the buffer up
// front and start receive data.
//
if (PARTIAL(Message))
{
if (Extra)
{
ActualBufferLength = Message->BufferLength ;
BufferLength = Message->BufferLength+Size ;
}
else
{
BufferLength = Size ;
}
}
else
{
if (Extra)
{
ActualBufferLength = Message->BufferLength ;
BufferLength = Message->BufferLength + MINIMUM_PARTIAL_BUFFLEN ;
}
else
{
BufferLength = MINIMUM_PARTIAL_BUFFLEN ;
}
}
Status = GetBufferDo(Message, BufferLength, Extra) ;
if (Status != RPC_S_OK)
{
FreeCCall();
return Status ;
}
RequestedLength = Message->BufferLength - ActualBufferLength;
while (!BufferComplete
&& (!PARTIAL(Message) || (RcvBufferLength < Size)))
{
if (SyncEvent.Wait() == WAIT_FAILED)
{
return RPC_S_CALL_FAILED;
}
}
return GetCoalescedBuffer(Message, Extra);
}
void
LRPC_CCALL::FreeBuffer (
IN PRPC_MESSAGE Message
)
/*++
Routine Description:
We will free the supplied buffer.
Arguments:
Message - Supplies the buffer to be freed.
--*/
{
ActuallyFreeBuffer(Message->Buffer);
if (CallStack == 0)
{
FreeCCall();
}
}
void
LRPC_CCALL::FreePipeBuffer (
IN PRPC_MESSAGE Message
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RpcpFarFree(Message->Buffer) ;
}
RPC_STATUS
LRPC_CCALL::GetBufferDo (
IN OUT PRPC_MESSAGE Message,
IN unsigned long NewSize,
IN int fDataValid
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
void *NewBuffer ;
int SizeToAlloc ;
if (NewSize < CurrentBufferLength)
{
Message->BufferLength = NewSize ;
}
else
{
SizeToAlloc = (NewSize < MINIMUM_PARTIAL_BUFFLEN) ?
MINIMUM_PARTIAL_BUFFLEN:NewSize ;
NewBuffer = RpcpFarAllocate(SizeToAlloc) ;
if (NewBuffer == 0)
{
RpcpFarFree(Message->Buffer) ;
CurrentBufferLength = 0;
Message->BufferLength = 0;
return RPC_S_OUT_OF_MEMORY ;
}
if (fDataValid && Message->BufferLength > 0)
{
RpcpMemoryCopy(NewBuffer,
Message->Buffer,
Message->BufferLength) ;
}
RpcpFarFree(Message->Buffer) ;
Message->Buffer = NewBuffer ;
Message->BufferLength = NewSize ;
CurrentBufferLength = SizeToAlloc ;
}
return RPC_S_OK ;
}
RPC_STATUS
LRPC_CCALL::ReallocPipeBuffer (
IN PRPC_MESSAGE Message,
IN unsigned int NewSize
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
unsigned int SizeToAlloc ;
void *TempBuffer ;
if (LrpcMessage == 0)
{
LrpcMessage = AllocateMessage();
if (LrpcMessage == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
}
if (GetBufferDo(Message, NewSize, 1) != RPC_S_OK)
return RPC_S_OUT_OF_MEMORY ;
Message->BufferLength = NewSize ;
LrpcMessage->Rpc.RpcHeader.Flags = LRPC_BUFFER_REQUEST;
LrpcMessage->Rpc.Request.CountDataEntries = 1;
LrpcMessage->Rpc.Request.DataEntries[0].Base = PtrToMsgPtr(Message->Buffer);
LrpcMessage->Rpc.Request.DataEntries[0].Size = Message->BufferLength;
LrpcMessage->LpcHeader.CallbackId = 0;
LrpcMessage->LpcHeader.u2.ZeroInit = 0;
LrpcMessage->LpcHeader.u2.s2.DataInfoOffset = sizeof(PORT_MESSAGE)
+ sizeof(LRPC_RPC_HEADER);
LrpcMessage->LpcHeader.u1.s1.DataLength = sizeof(LRPC_RPC_HEADER)
+ sizeof(PORT_DATA_INFORMATION);
return (RPC_S_OK) ;
}
void
LRPC_CCALL::AbortCCall (
)
/*++
Routine Description:
This client call has failed, so we need to abort it. We may called
while nested in one or more callbacks.
--*/
{
LRPC_BINDING_HANDLE * BindingHandle;
CallAbortedFlag = 1;
if (CallStack == 0)
{
ASSERT(CurrentBindingHandle != 0);
BindingHandle = CurrentBindingHandle;
CurrentBindingHandle = 0;
BindingHandle->FreeCCall(this);
}
}
inline RPC_STATUS
LRPC_CCALL::LrpcMessageToRpcMessage (
IN LRPC_MESSAGE *LrpcResponse,
OUT RPC_MESSAGE *Message,
IN HANDLE LpcPort,
IN BOOL IsReplyFromBackConnection OPTIONAL,
OUT DelayedPipeAckData *AckData OPTIONAL
)
/*++
Routine Description:
We will convert from an LRPC_MESSAGE representation of a buffer (and
its length) to an RPC_MESSAGE representation.
Arguments:
LrpcResponse - the response we received from the server
RpcMessage - Returns the RPC_MESSAGE representation.
LpcPort - the association port on which we send data to the server
IsReplyFromBackConnection - non-zero if the reply is from back connection
AckData - if non-NULL, and the received data are pipe data, an
acknowledgement to a pipe response will be delayed, the current
status will be placed here, and it will be indicated the ack was
delayed. Also, if non-NULL, the caller must set
AckData->DelayedAckPipeNeeded to FALSE.
If NULL, any acknowledgement will be sent immediately.
--*/
{
NTSTATUS NtStatus;
SIZE_T NumberOfBytesRead;
#if defined(BUILD_WOW6432)
char CopyMessageBuffer[sizeof(LRPC_COPY_MESSAGE) + 8];
LRPC_COPY_MESSAGE *CopyMessagePtr = (LRPC_COPY_MESSAGE *) AlignPtr8(CopyMessageBuffer);
#else
LRPC_COPY_MESSAGE CopyMessageBuffer;
LRPC_COPY_MESSAGE *CopyMessagePtr = &CopyMessageBuffer;
#endif
RPC_STATUS Status = RPC_S_OK;
RPC_STATUS Status2;
BOOL fPartialResponse;
if (ARGUMENT_PRESENT(AckData))
{
ASSERT(AckData->DelayedAckPipeNeeded == FALSE);
}
if(LrpcResponse->Rpc.RpcHeader.Flags & LRPC_BUFFER_IMMEDIATE)
{
if (LrpcResponse->LpcHeader.u1.s1.DataLength < sizeof(LRPC_RPC_HEADER))
{
ASSERT(LrpcResponse->LpcHeader.u1.s1.DataLength >= sizeof(LRPC_RPC_HEADER));
return RPC_S_PROTOCOL_ERROR;
}
Message->Buffer = LrpcResponse->Rpc.Buffer;
Message->BufferLength =
(unsigned int) LrpcResponse->LpcHeader.u1.s1.DataLength
- sizeof(LRPC_RPC_HEADER);
if ((LrpcResponse->Rpc.RpcHeader.Flags & LRPC_BUFFER_PARTIAL) == 0)
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
}
if (IsReplyFromBackConnection)
{
LpcReplyMessage = LrpcResponse ;
}
}
else if (LrpcResponse->Rpc.RpcHeader.Flags & LRPC_BUFFER_SERVER)
{
if (IsReplyFromBackConnection == 0)
{
UINT BufferLength;
LPC_PVOID ServerBuffer;
ASSERT(LrpcMessage == LrpcResponse);
BufferLength = LrpcResponse->Rpc.Server.Length;
ServerBuffer = LrpcResponse->Rpc.Server.Buffer;
Message->BufferLength = BufferLength;
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
CopyMessagePtr->LpcHeader.u2.ZeroInit = 0;
SanitizeLpcHeader (&CopyMessagePtr->LpcHeader);
CopyMessagePtr->Server.Buffer = ServerBuffer;
CopyMessagePtr->Server.Length = BufferLength;
if (BufferLength >= 0x80000000)
{
ASSERT(BufferLength < 0x80000000);
Message->Buffer = 0;
CopyMessagePtr->RpcStatus = RPC_S_PROTOCOL_ERROR;
}
else {
Message->Buffer = RpcpFarAllocate(BufferLength) ;
if (Message->Buffer == 0)
{
CopyMessagePtr->RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else
{
CopyMessagePtr->RpcStatus = RPC_S_OK;
CopyMessagePtr->Request.CountDataEntries = 1;
CopyMessagePtr->Request.DataEntries[0].Base = PtrToMsgPtr(Message->Buffer);
CopyMessagePtr->Request.DataEntries[0].Size = Message->BufferLength ;
CopyMessagePtr->LpcHeader.u2.s2.DataInfoOffset =
sizeof(PORT_MESSAGE) + sizeof(LRPC_RPC_HEADER);
}
}
CopyMessagePtr->LpcHeader.CallbackId = 0;
CopyMessagePtr->RpcHeader.Flags = LRPC_SYNC_CLIENT ;
CopyMessagePtr->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_COPY_MESSAGE) - sizeof(PORT_MESSAGE);
CopyMessagePtr->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_COPY_MESSAGE);
CopyMessagePtr->RpcHeader.MessageType = LRPC_MSG_COPY;
CopyMessagePtr->RpcHeader.Pad = 0;
// zero out unused fields
RPCP_ZERO_OUT_STRUCT_TAIL (LRPC_RPC_HEADER, &CopyMessagePtr->RpcHeader, ProcedureNumber);
CopyMessagePtr->RpcHeader.PresentContext = 0;
CopyMessagePtr->IsPartial = 0 ;
// Make sure we can't be impersonated on the transport level if
// static identity tracking is being used.
ASSERT(CurrentBindingHandle != NULL);
if (CurrentBindingHandle->GetIdentityTracking() == RPC_C_QOS_IDENTITY_STATIC)
{
CopyMessagePtr->LpcHeader.u2.s2.Type |= LPC_NO_IMPERSONATE;
}
if (Association->LpcClientPort)
{
NtStatus = NtRequestWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) CopyMessagePtr,
(PORT_MESSAGE *) CopyMessagePtr);
}
else
{
NtStatus = STATUS_INVALID_PORT_HANDLE;
}
if ((NT_ERROR(NtStatus))
|| (CopyMessagePtr->RpcStatus != RPC_S_OK))
{
RpcpFarFree(Message->Buffer);
return(RPC_S_OUT_OF_MEMORY);
}
}
else
{
fPartialResponse = FALSE;
if (LrpcResponse->Rpc.RpcHeader.Flags & LRPC_BUFFER_PARTIAL)
{
fPartialResponse = TRUE;
CallMutex.Request() ;
if ((RcvBufferLength >= LRPC_THRESHOLD_SIZE))
{
Choked = 1;
}
CallMutex.Clear() ;
}
else
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
}
Message->BufferLength = (unsigned int)
LrpcResponse->Rpc.Request.DataEntries[0].Size ;
Message->Buffer = RpcpFarAllocate(
Message->BufferLength);
if (Message->Buffer != NULL)
{
NtStatus = NtReadRequestData(LpcPort,
(PORT_MESSAGE*) LrpcResponse,
0,
Message->Buffer,
Message->BufferLength,
&NumberOfBytesRead) ;
if (NT_ERROR(NtStatus))
{
#if DBG
PrintToDebugger("LRPC: NtReadRequestData failed: %x\n", NtStatus) ;
#endif
Status = RPC_S_OUT_OF_MEMORY;
}
else
{
ASSERT(Message->BufferLength == NumberOfBytesRead);
}
}
else
{
Status = RPC_S_OUT_OF_MEMORY;
}
if (ARGUMENT_PRESENT(AckData) && fPartialResponse && (Status == RPC_S_OK))
{
// if pipe and delayed ack was asked for, and
// moreover the operation didn't fail
// just store the relevant data in the caller
// supplied data structure
AckData->DelayedAckPipeNeeded = TRUE;
AckData->CurrentStatus = Status;
}
else
{
Status2 = SendPipeAck(LpcPort,
LrpcResponse,
Status);
FreeMessage(LrpcResponse) ;
// if either operation failed, fail the whole function
// if both operations failed, the first one is considered
// the original failure and the error code from it is
// preserved
if ((Status == RPC_S_OK) && (Status2 != RPC_S_OK))
Status = Status2;
}
if ((Status != RPC_S_OK) && (Message->Buffer))
{
RpcpFarFree(Message->Buffer);
}
}
}
else
{
CORRUPTION_ASSERT((LrpcResponse->Rpc.RpcHeader.Flags & LRPC_BUFFER_IMMEDIATE)
|| (LrpcResponse->Rpc.RpcHeader.Flags & LRPC_BUFFER_SERVER));
return (RPC_S_PROTOCOL_ERROR);
}
return(Status);
}
RPC_STATUS
LRPC_CCALL::SendPipeAck (
IN HANDLE LpcPort,
IN LRPC_MESSAGE *LrpcResponse,
IN RPC_STATUS CurrentStatus
)
/*++
Routine Description:
Sends an acknowledgement to the server.
Arguments:
LpcPort - the port to send the ack to.
LrpcResponse - the response that we received from the server
CurrentStatus - the status up to the moment. It will
be sent to the server.
Return Value:
The result of the operation. RPC_S_OK for success
or RPC_S_* for error.
--*/
{
unsigned char MessageType;
RPC_STATUS RpcStatus = RPC_S_OK;
NTSTATUS NtStatus;
MessageType = LrpcResponse->Rpc.RpcHeader.MessageType ;
LrpcResponse->Ack.MessageType = LRPC_MSG_ACK ;
LrpcResponse->Ack.Shutup = (short) Choked ;
LrpcResponse->Ack.RpcStatus = CurrentStatus;
LrpcResponse->LpcHeader.u1.s1.DataLength = sizeof(LRPC_ACK_MESSAGE)
- sizeof(PORT_MESSAGE) ;
LrpcResponse->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_ACK_MESSAGE) ;
// setup the reply message
NtStatus = NtReplyPort(LpcPort,
(PORT_MESSAGE *) LrpcResponse) ;
LrpcResponse->Rpc.RpcHeader.MessageType = MessageType ;
if (NT_ERROR(NtStatus))
{
#if DBG
PrintToDebugger("LRPC: NtReplyPort failed: %x\n", NtStatus) ;
#endif
RpcStatus = RPC_S_OUT_OF_MEMORY;
}
return RpcStatus;
}
BOOL
LRPC_CCALL::TryWaitForCallToBecomeUnlocked (
BOOL *fUnlocked
)
/*++
Routine Description:
Checks if a call is unlocked. If the lock count
becomes 0, or if the lock count becomes 1 and this thread
is the last one with a lock, the call is considered unlocked.
In this case *fUnlocked is set to TRUE. Otherwise it is set to FALSE.
Return Value:
If fUnlocked == TRUE:
TRUE - the call has 1 outstanding lock count and the
LastProcessResponseTID is our TID. This means that we're
called from ProcessResponse (indirectly - through COM as
they complete the call on the thread that issues the
notification). If we return TRUE, we have already taken
the lock down and the caller should not remove any locks
FALSE - the call has no outstanding locks
If fUnlocked == FALSE
Undefined
--*/
{
ULONG CurrentThreadId = GetCurrentThreadId();
if (ResponseLockCount.GetInteger() == 0)
{
*fUnlocked = TRUE;
return FALSE;
}
else if ((ResponseLockCount.GetInteger() == 1)
&& (LastProcessResponseTID == CurrentThreadId))
{
// If our caller has an outstanding lock and we free the call
// with the lock held, zero out the count on their behalf.
// In our caller we will notify the ultimate caller so that it
// doesn't double take away the lock.
ResponseLockCount.SetInteger(0);
// the only outstanding lock is by us in our caller - process
// response. Indicate to the caller that only our lock is
// active and it has been taken down.
*fUnlocked = TRUE;
return TRUE;
}
*fUnlocked = FALSE;
// The return value is meaningless. Arbitrarily, return FALSE.
return FALSE;
}
void
LRPC_CCALL::FreeCCall (
)
/*++
Routine Description:
We are done with this client call. We need to notify the binding
handle we are done.
--*/
{
LRPC_BINDING_HANDLE * BindingHandle;
THREAD *Thread;
ASSERT(CurrentBindingHandle != 0);
BindingHandle = CurrentBindingHandle;
CurrentBindingHandle = 0;
if (CurrentSecurityContext)
{
CurrentSecurityContext->RemoveReference();
CurrentSecurityContext = 0;
}
// if async, and there is EEInfo,
// transfer the EEInfo from the call to
// the thread
if (EEInfo)
{
Thread = RpcpGetThreadPointer();
RpcpPurgeEEInfoFromThreadIfNecessary(Thread);
Thread->SetEEInfo(EEInfo);
EEInfo = NULL;
}
BindingHandle->FreeCCall(this);
}
void
LRPC_CCALL::ActuallyFreeBuffer (
IN void * Buffer
)
/*++
Routine Description:
Actually free a message buffer.
Arguments:
Buffer - Supplies the message buffer to be freed.
--*/
{
if (LpcReplyMessage && (Buffer == LpcReplyMessage->Rpc.Buffer))
{
FreeMessage(LpcReplyMessage) ;
LpcReplyMessage = 0;
}
else
{
if ((Buffer != LrpcMessage->Rpc.Buffer)
&& ((CachedLrpcMessage == 0)
|| (Buffer != CachedLrpcMessage->Rpc.Buffer)))
{
RpcpFarFree(Buffer);
}
}
}
RPC_STATUS
LRPC_CCALL::MakeServerCopyResponse (
)
/*++
Routine Description:
NtReadRequestData only works if the client has made a request. The client
wants to send a large buffer back as a response. We need to make a request
to the server so that it will copy the data.
Return Value:
RPC_S_OK - The server successfully copied the data.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete the
operation.
--*/
{
#if defined(BUILD_WOW6432)
char PushMessageBuffer[sizeof(LRPC_PUSH_MESSAGE) + 8];
LRPC_PUSH_MESSAGE *PushMessagePtr = (LRPC_PUSH_MESSAGE *) AlignPtr8(PushMessageBuffer);
#else
LRPC_PUSH_MESSAGE PushMessageBuffer;
LRPC_PUSH_MESSAGE *PushMessagePtr = &PushMessageBuffer;
#endif
NTSTATUS NtStatus;
SanitizeLpcHeader (&PushMessagePtr->LpcHeader);
PushMessagePtr->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_PUSH_MESSAGE);
PushMessagePtr->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_PUSH_MESSAGE) - sizeof(PORT_MESSAGE);
PushMessagePtr->LpcHeader.ClientId = ClientIdToMsgClientId(ClientId);
PushMessagePtr->LpcHeader.MessageId = MessageId;
PushMessagePtr->LpcHeader.CallbackId = CallbackId ;
PushMessagePtr->LpcHeader.u2.s2.Type = LPC_REQUEST;
PushMessagePtr->RpcHeader.MessageType = LRPC_MSG_PUSH;
// initialize unused fields. Zero out from the pad in RpcHeader to
// the end of the containing LRPC_PUSH_MESSAGE
RPCP_ZERO_OUT_STRUCT_TAIL (LRPC_PUSH_MESSAGE, PushMessagePtr, RpcHeader.Pad);
PushMessagePtr->Response.CountDataEntries = 1;
PushMessagePtr->Response.DataEntries[0] =
LrpcMessage->Rpc.Request.DataEntries[0];
PushMessagePtr->LpcHeader.u2.s2.DataInfoOffset = sizeof(PORT_MESSAGE)
+ sizeof(LRPC_RPC_HEADER);
// Make sure we can't be impersonated on the transport level.
// A legitimate Lrpc server will never do this.
PushMessagePtr->LpcHeader.u2.s2.Type |= LPC_NO_IMPERSONATE;
if (Association->LpcClientPort)
{
NtStatus = NtRequestWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) PushMessagePtr,
(PORT_MESSAGE *) PushMessagePtr);
}
else
{
NtStatus = STATUS_INVALID_PORT_HANDLE;
}
if (NT_ERROR(NtStatus))
{
// Assume that when the client tries to send the response it will
// fail as well, so just claim that everything worked.
#if DBG
if ((NtStatus != STATUS_NO_MEMORY)
&& (NtStatus != STATUS_INSUFFICIENT_RESOURCES))
{
PrintToDebugger("RPC : NtRequestWaitReplyPort : %lx\n", NtStatus);
ASSERT(0);
}
#endif // DBG
return(RPC_S_OK);
}
VALIDATE(PushMessagePtr->RpcStatus)
{
RPC_S_OK,
RPC_S_OUT_OF_MEMORY
} END_VALIDATE;
return(PushMessagePtr->RpcStatus);
}
BINDING_HANDLE *
LrpcCreateBindingHandle (
)
/*++
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;
RPC_STATUS Status = RPC_S_OK;
Status = InitializeLrpcIfNecessary() ;
if (Status != RPC_S_OK)
{
return 0 ;
}
BindingHandle = new LRPC_BINDING_HANDLE(&Status);
if (Status != RPC_S_OK)
{
delete BindingHandle;
return(0);
}
return(BindingHandle);
}
void
LRPC_CASSOCIATION::LrpcDeleteLingeringAssociations (
void
)
/*++
Routine Description:
Will attempt to clean up lingering LRPC associations.
Return Value:
--*/
{
BOOL fMutexTaken;
LRPC_CASSOCIATION *CurrentAssociation;
LRPC_CASSOCIATION *NextAssociation;
LRPC_CASSOCIATION *FirstAssociation;
DictionaryCursor cursor;
DWORD CurrentTickCount;
int Diff;
// if there are no lrpc associations, return
if (!GlobalLrpcServer)
return;
fMutexTaken = LrpcMutexTryRequest();
if (!fMutexTaken)
{
// we couldn't cleanup anything - restore the flag
if (!GarbageCollectionRequested)
GarbageCollectionRequested = TRUE;
return;
}
FirstAssociation = NULL;
CurrentTickCount = GetTickCount();
// need to walk the dictionary and clean up all associations with
// expired timeouts
LrpcAssociationDict->Reset(cursor);
while ((CurrentAssociation = LrpcAssociationDict->Next(cursor)) != 0)
{
if (CurrentAssociation->Linger.fAssociationLingered)
{
// this will work even for wrapped tick count
Diff = (int)(CurrentTickCount - CurrentAssociation->Linger.Timestamp);
if (Diff > 0)
{
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LRPC association gc'ed %d ms after expire\n",
GetCurrentProcessId(), GetCurrentProcessId(), Diff);
#endif
// enlink the expired associations to a list - we'll clean it up
// later
CurrentAssociation->NextAssociation = FirstAssociation;
FirstAssociation = CurrentAssociation;
LrpcAssociationDict->Delete(CurrentAssociation->AssociationDictKey);
// indicate to the other threads (needed once we release the mutex)
// that this association is being cleaned up and they cannot call
// Delete on it
CurrentAssociation->AssociationDictKey = -1;
LrpcLingeredAssociations --;
}
else
{
// this item hasn't expired yet - update the first gc time, and
// raise the GarbageCollectionRequested flag if necessary
if ((int)(CurrentAssociation->Linger.Timestamp - NextOneTimeCleanup) < 0)
{
// there is a race between this thread and threads calling
// GarbageCollectionNeeded. Those threads may overwrite the
// value we're about to write, which can result in delayed
// garbage collection for this value - that's ok.
NextOneTimeCleanup = CurrentAssociation->Linger.Timestamp;
}
if (!GarbageCollectionRequested)
GarbageCollectionRequested = TRUE;
}
}
}
LrpcMutexClear();
// destroy the associations at our leasure
CurrentAssociation = FirstAssociation;
while (CurrentAssociation != NULL)
{
NextAssociation = CurrentAssociation->NextAssociation;
CurrentAssociation->Delete();
CurrentAssociation = NextAssociation;
}
}
int
InitializeRpcProtocolLrpc (
)
/*++
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.
--*/
{
if (LrpcAssociationDict == 0)
{
LrpcAssociationDict = new LRPC_CASSOCIATION_DICT;
if (LrpcAssociationDict == 0)
{
return(1);
}
}
return(0);
}
RPC_STATUS
LrpcMapRpcStatus (
IN RPC_STATUS Status
)
/*++
Routine Description:
Some NTSTATUS codes need to be mapped into RPC_STATUS codes before being
returned as a fault code. We take care of doing that mapping in this
routine.
--*/
{
switch (Status)
{
case STATUS_INTEGER_DIVIDE_BY_ZERO :
return(RPC_S_ZERO_DIVIDE);
case STATUS_ACCESS_VIOLATION :
case STATUS_ILLEGAL_INSTRUCTION :
return(RPC_S_ADDRESS_ERROR);
case STATUS_FLOAT_DIVIDE_BY_ZERO :
return(RPC_S_FP_DIV_ZERO);
case STATUS_FLOAT_UNDERFLOW :
return(RPC_S_FP_UNDERFLOW);
case STATUS_FLOAT_OVERFLOW :
return(RPC_S_FP_OVERFLOW);
}
return(Status);
}