Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4254 lines
113 KiB

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
wmsgclnt.cxx
Abstract:
Implementation of the RPC on LPC (WMSG) 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
05-06-96 Merged WMSG and LRPC into a single protocol
--*/
#include <precomp.hxx>
#include <rpcqos.h>
#include <sdict2.hxx>
#include <wmsgpack.hxx>
#include <hndlsvr.hxx>
#include <wmsgsvr.hxx>
#include <wmsgclnt.hxx>
#include <epmap.h>
extern MSG_CACHE *MessageCache ;
RPC_STATUS
InitializeWMsgIfNeccassary(
int ActuallyInitialize
) ;
RPC_STATUS RPC_ENTRY
I_RpcBlockingFunc(
HANDLE hSyncEvent
)
{
MSG wMsg ;
DWORD RetVal ;
while (1)
{
RetVal = GlobalWMsgServer->MsgWaitForMultipleObjects(
1,
&hSyncEvent,
FALSE,
INFINITE,
QS_POSTMESSAGE | QS_SENDMESSAGE
);
if (RetVal == WAIT_OBJECT_0+1)
{
while (GlobalWMsgServer->PeekMessageW(&wMsg, NULL, 0, 0, PM_REMOVE))
{
GlobalWMsgServer->TranslateMessage(&wMsg);
GlobalWMsgServer->DispatchMessageW(&wMsg);
}
}
else if (RetVal == WAIT_OBJECT_0)
{
return (RPC_S_OK) ;
}
else
{
return (RPC_S_OUT_OF_MEMORY) ;
}
}
}
RPC_STATUS RPC_ENTRY
I_RpcAsyncSendReceive(
IN OUT PRPC_MESSAGE Message,
IN OPTIONAL void *Context,
IN HWND hWnd
)
{
RPC_STATUS retval ;
AssertRpcInitialized();
ASSERT(!RpcpCheckHeap());
MESSAGE_OBJECT *MObject = (MESSAGE_OBJECT *) Message->Handle;
ASSERT( MObject->InvalidHandle(BINDING_HANDLE_TYPE
| SCONNECTION_TYPE | CCONNECTION_TYPE) == 0 );
ASSERT( Message->Buffer != 0 );
retval = MObject->AsyncSendReceive(Message, Context, hWnd);
ASSERT(!RpcpCheckHeap());
// Insure that the buffer is aligned on an eight byte boundary.
ASSERT(retval != RPC_S_OK || Pad8(Message->Buffer) == 0);
return(retval);
}
RPC_STATUS RPC_ENTRY
I_RpcBindingSetAsync(
IN RPC_BINDING_HANDLE Binding,
IN RPC_BLOCKING_FN BlockingFn
)
{
RPC_STATUS status;
InitializeIfNecessary();
ASSERT(!RpcpCheckHeap());
if (((GENERIC_OBJECT *) Binding)->InvalidHandle(BINDING_HANDLE_TYPE))
{
return (RPC_S_INVALID_BINDING) ;
}
return ((BINDING_HANDLE *) Binding)->SetAsync(BlockingFn) ;
}
WMSG_BINDING_HANDLE::WMSG_BINDING_HANDLE (
OUT RPC_STATUS * RpcStatus
) : BindingMutex(RpcStatus)
/*++
Routine Description:
We just allocate an WMSG_BINDING_HANDLE and initialize things so that
we can use it later.
Arguments:
RpcStatus - Returns the result of initializing the binding mutex.
--*/
{
CurrentAssociation = 0;
DceBinding = 0;
BindingReferenceCount = 1;
BlockingFuncInitialized = 0;
BlockingFunction = 0;
AuthInfoInitialized = 0;
}
WMSG_BINDING_HANDLE::~WMSG_BINDING_HANDLE (
)
/*++
--*/
{
WMSG_CASSOCIATION *Association;
if ( SecAssociation.Size() != 0 )
{
SecAssociation.Reset();
while ( ( Association = SecAssociation.Next()) != 0 )
{
if ( Association != 0 )
{
// take away from the bindinghandle dictionary
RemoveAssociation(Association);
// take away from the global dict
Association->RemoveReference();
}
}
}
delete DceBinding;
}
RPC_STATUS
WMSG_BINDING_HANDLE::SetAsync(
IN RPC_BLOCKING_FN BlockingFn
)
{
RPC_STATUS Status ;
Status = InitializeWMsgIfNeccassary(0) ;
if (Status != RPC_S_OK)
{
return Status ;
}
BindingMutex.Request() ;
BlockingFunction = BlockingFn ;
BlockingFuncInitialized = 1;
BindingMutex.Clear();
return (RPC_S_OK) ;
}
RPC_STATUS
WMSG_BINDING_HANDLE::GetBuffer (
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
Arguments:
Message - Supplies the length of the buffer required, and returns the
new buffer.
Return Value:
--*/
{
WMSG_CCALL * CCall;
RPC_STATUS RpcStatus;
int RetryCount = 0;
static long nInitialized = -1 ;
WMSG_CASSOCIATION *Association ;
for (;;)
{
for (;;)
{
RpcStatus = AllocateCCall(&CCall, (RPC_CLIENT_INTERFACE PAPI *)
Message->RpcInterfaceInformation);
if ( RpcStatus != RPC_S_SERVER_UNAVAILABLE )
{
break;
}
if ( *InquireEpLookupHandle() == 0 )
{
break;
}
// If we reach here, it means that we are iterating through the
// list of endpoints obtained from the endpoint mapper.
RequestGlobalMutex();
if ( BindingReferenceCount == 1 )
{
if ( SecAssociation.Size() != 0 )
{
DceBinding = CurrentAssociation->DuplicateDceBinding();
if(DceBinding == 0 )
{
ClearGlobalMutex();
return(RPC_S_OUT_OF_MEMORY);
}
CurrentAssociation = 0;
DceBinding->MakePartiallyBound();
// remove references
SecAssociation.Reset();
while( (Association = SecAssociation.Next()) != 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();
}
}
}
}
else
{
RetryCount++;
if (RetryCount>2)
{
ClearGlobalMutex();
break;
}
}
ClearGlobalMutex();
}
if ( RpcStatus == RPC_S_OK )
{
break;
}
if ( InqComTimeout() != RPC_C_BINDING_INFINITE_TIMEOUT )
{
return(RpcStatus);
}
if ( ( RpcStatus != RPC_S_SERVER_UNAVAILABLE )
&& ( RpcStatus != RPC_S_SERVER_TOO_BUSY ) )
{
return(RpcStatus);
}
}
RpcStatus = CCall->GetBuffer(Message);
if ( RpcStatus != RPC_S_OK )
{
CCall->AbortCCall();
ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY);
return(RpcStatus);
}
return(RPC_S_OK);
}
RPC_STATUS
WMSG_BINDING_HANDLE::BindingCopy (
OUT BINDING_HANDLE * PAPI * 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 RpcStatus = RPC_S_OK;
WMSG_BINDING_HANDLE * NewBindingHandle;
CLIENT_AUTH_INFO * AuthInfo;
UNUSED(MaintainContext);
NewBindingHandle = new WMSG_BINDING_HANDLE(&RpcStatus);
if ( NewBindingHandle == 0 )
{
return(RPC_S_OUT_OF_MEMORY);
}
if ( RpcStatus != RPC_S_OK )
{
delete NewBindingHandle;
return(RpcStatus);
}
if ((AuthInfo = InquireAuthInformation()) != 0)
{
RpcStatus = NewBindingHandle->SetAuthInformation(
AuthInfo->ServerPrincipalName,
AuthInfo->AuthenticationLevel,
AuthInfo->AuthenticationService,
AuthInfo->AuthIdentity,
AuthInfo->AuthorizationService,
0,
AuthInfo->ImpersonationType,
AuthInfo->IdentityTracking,
AuthInfo->Capabilities
);
if (RpcStatus != RPC_S_OK)
{
ASSERT (RpcStatus == RPC_S_OUT_OF_MEMORY);
delete NewBindingHandle;
return(RPC_S_OUT_OF_MEMORY);
}
}
RequestGlobalMutex() ;
if ( SecAssociation.Size() == 0 )
{
NewBindingHandle->DceBinding = DceBinding->DuplicateDceBinding();
if ( NewBindingHandle->DceBinding == 0 )
{
ClearGlobalMutex() ;
delete NewBindingHandle;
return(RPC_S_OUT_OF_MEMORY);
}
}
else
{
NewBindingHandle->CurrentAssociation = CurrentAssociation->DuplicateAssociation();
if( NewBindingHandle->AddAssociation(NewBindingHandle->CurrentAssociation) == -1)
{
ClearGlobalMutex() ;
delete NewBindingHandle;
return (RPC_S_OUT_OF_MEMORY);
}
}
ClearGlobalMutex() ;
*DestinationBinding = (BINDING_HANDLE *) NewBindingHandle;
return(RPC_S_OK);
}
RPC_STATUS
WMSG_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.
--*/
{
BindingMutex.Request();
BindingReferenceCount -= 1;
if ( BindingReferenceCount == 0 )
{
BindingMutex.Clear();
delete this;
}
else
{
BindingMutex.Clear();
}
return(RPC_S_OK);
}
void
WMSG_BINDING_HANDLE::PrepareBindingHandle (
IN void * TransportInformation,
IN DCE_BINDING * DceBinding
)
/*++
Routine Description:
This method will be called just before a new binding handle is returned
to the user. We just stack the binding information so that we can use
it later when the first remote procedure call is made. At that time,
we will actually bind to the interface.
Arguments:
TransportInformation - Unused.
DceBinding - Supplies the binding information for this binding handle.
--*/
{
UNUSED(TransportInformation);
this->DceBinding = DceBinding;
}
RPC_STATUS
WMSG_BINDING_HANDLE::ToStringBinding (
OUT RPC_CHAR PAPI * PAPI * 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
WMSG_BINDING_HANDLE::ResolveBinding (
IN RPC_CLIENT_INTERFACE PAPI * 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 RpcStatus;
if ( CurrentAssociation == 0 )
{
BindingMutex.Request();
RpcStatus = DceBinding->ResolveEndpointIfNecessary(
RpcClientInterface, InqPointerAtObjectUuid(),
InquireEpLookupHandle(), FALSE, InqComTimeout());
BindingMutex.Clear();
return(RpcStatus);
}
return(RPC_S_OK);
}
RPC_STATUS
WMSG_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.
--*/
{
WMSG_CASSOCIATION *Association ;
RequestGlobalMutex();
if ( CurrentAssociation != 0 )
{
if ( BindingReferenceCount != 1 )
{
ClearGlobalMutex();
return(RPC_S_WRONG_KIND_OF_BINDING);
}
DceBinding = CurrentAssociation->DuplicateDceBinding();
if(DceBinding == 0 )
{
ClearGlobalMutex();
return(RPC_S_OUT_OF_MEMORY);
}
CurrentAssociation = 0;
SecAssociation.Reset();
while( (Association = SecAssociation.Next()) != 0)
{
RemoveAssociation(Association);
Association->RemoveReference();
}
}
DceBinding->MakePartiallyBound();
if ( *InquireEpLookupHandle() != 0 )
{
EpFreeLookupHandle(*InquireEpLookupHandle());
*InquireEpLookupHandle() = 0;
}
ClearGlobalMutex();
return(RPC_S_OK);
}
void
WMSG_BINDING_HANDLE::FreeCCall (
IN WMSG_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.
--*/
{
BindingMutex.Request();
if ((CCall->InqAssociation())->FreeCCall(CCall))
{
BindingReferenceCount -= 1;
}
// donot touch the association beyond this. It could be freed.
if ( BindingReferenceCount == 0 )
{
BindingMutex.Clear();
delete this;
}
else
{
BindingMutex.Clear();
}
}
RPC_STATUS
WMSG_BINDING_HANDLE::AllocateCCall (
OUT WMSG_CCALL ** CCall,
IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation
)
/*++
Routine Description:
This method will allocate an WMSG_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 WMSG_CCALL allocated for this interface and
thread. Otherwise, we need to ask the association to allocate a
WMSG_CCALL for us.
Arguments:
CCall - Returns the allocated WMSG_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 RpcStatus;
RPC_CHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD ComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1;
BOOL Boolean;
BOOL FoundSameAuthInfo = FALSE;
WMSG_CASSOCIATION * Association;
WMSG_CASSOCIATION *MyAssociation = NULL;
BindingMutex.Request();
// 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 )
{
Boolean = GetComputerNameW(ComputerName, &ComputerNameLength);
#if DBG
if ( Boolean != TRUE )
{
PrintToDebugger("RPC : GetComputerNameW : %d\n", GetLastError());
}
#endif // DBG
ASSERT( Boolean == TRUE );
if ( RpcpStringCompare(DceBinding->InqNetworkAddress(),
ComputerName) != 0 )
{
BindingMutex.Clear();
return(RPC_S_SERVER_UNAVAILABLE);
}
}
RpcStatus = DceBinding->ResolveEndpointIfNecessary(
RpcInterfaceInformation, InqPointerAtObjectUuid(),
InquireEpLookupHandle(), FALSE, InqComTimeout());
if ( RpcStatus != RPC_S_OK )
{
BindingMutex.Clear();
return(RpcStatus);
}
}
else
{
RequestGlobalMutex();
SecAssociation.Reset();
while ( (Association = SecAssociation.Next()) != 0 )
{
if(Association->IsSupportedAuthInfo(InquireAuthInformation()) == TRUE)
{
MyAssociation = Association ;
FoundSameAuthInfo = TRUE;
break;
}
}
ClearGlobalMutex();
}
if( FoundSameAuthInfo == FALSE)
{
RequestGlobalMutex();
// we have some association in the dictionary, check for security level
if (DceBinding == 0 )
{
SecAssociation.Reset();
Association = SecAssociation.Next();
DceBinding= Association->DuplicateDceBinding(); // it will get delete when Assoc goes
if(DceBinding == 0 )
{
ClearGlobalMutex();
return(RPC_S_OUT_OF_MEMORY);
}
}
MyAssociation = FindOrCreateWMSGAssociation(
DceBinding,
InquireAuthInformation()
);
if (CurrentAssociation == 0)
{
CurrentAssociation = MyAssociation ;
}
if ( MyAssociation == 0 )
{
ClearGlobalMutex();
BindingMutex.Clear();
return(RPC_S_OUT_OF_MEMORY);
}
if((AddAssociation(MyAssociation)) == -1)
{
delete MyAssociation;
if (CurrentAssociation == MyAssociation)
{
CurrentAssociation = 0;
}
ClearGlobalMutex();
BindingMutex.Clear();
return (RPC_S_OUT_OF_MEMORY);
}
// The association now owns the DceBinding.
DceBinding = 0;
ClearGlobalMutex();
}
// 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 ( ActiveCalls.Size() != 0 )
{
ActiveCalls.Reset();
while ( ( *CCall = ActiveCalls.Next()) != 0 )
{
if ( (*CCall)->IsThisMyActiveCall(GetThreadIdentifier(),
RpcInterfaceInformation) != 0 )
{
BindingMutex.Clear();
return(RPC_S_OK);
}
}
}
BindingReferenceCount += 1;
BindingMutex.Clear();
ASSERT(MyAssociation) ;
if (BlockingFunction)
{
if (GlobalWMsgServer->IsWMsgEndpointInitialized() == 0)
{
BindingMutex.Request();
BindingReferenceCount -= 1;
ASSERT( BindingReferenceCount != 0 );
BindingMutex.Clear();
return (RPC_S_NOT_LISTENING) ;
}
RpcStatus = MyAssociation->CreateBackConnection() ;
if (RpcStatus != RPC_S_OK)
{
BindingMutex.Request();
BindingReferenceCount -= 1;
ASSERT( BindingReferenceCount != 0 );
BindingMutex.Clear();
return RpcStatus ;
}
}
RpcStatus = MyAssociation->AllocateCCall(CCall, RpcInterfaceInformation);
if ( RpcStatus == RPC_S_OK )
{
(*CCall)->ActivateCall(this, RpcInterfaceInformation);
}
else
{
BindingMutex.Request();
BindingReferenceCount -= 1;
ASSERT( BindingReferenceCount != 0 );
BindingMutex.Clear();
}
return(RpcStatus);
}
RPC_STATUS
WMSG_BINDING_HANDLE::SetAuthInformation (
IN RPC_CHAR PAPI * 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
)
/*++
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.
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 RpcStatus;
SEC_WINNT_AUTH_IDENTITY *ntssp;
HANDLE hToken;
unsigned long MappedAuthenticationLevel;
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);
}
if(AuthenticationService != RPC_C_AUTHN_WINNT)
{
return(RPC_S_INVALID_ARG) ;
}
#if 0
if(ARGUMENT_PRESENT(AuthIdentity))
{
ntssp = ( SEC_WINNT_AUTH_IDENTITY * ) AuthIdentity ;
if(strlen((const char *) ntssp->User) != ntssp->UserLength)
{
return (RPC_S_INVALID_AUTH_IDENTITY);
}
if(strlen((const char *) ntssp->Domain) != ntssp->DomainLength)
{
return (RPC_S_INVALID_AUTH_IDENTITY);
}
if(strlen((const char *) ntssp->Password) != ntssp->PasswordLength)
{
return (RPC_S_INVALID_AUTH_IDENTITY);
}
// This code works, however, we decided not to use it because calling logonUser requires
// User right of "Acting as part of the OS". If we want to do it, we should call a service
// to do this.
if(!LogonUser((char *) ntssp->User, (char *) ntssp->Domain,
(char *) ntssp->Password,LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, &hToken))
{
#ifdef DEBUGRPC
PrintToDebugger("Error on LogonUser with error %d\n", GetLastError());
#endif
return (RPC_S_INVALID_AUTH_IDENTITY);
}
if(!ImpersonateLoggedOnUser( *hToken))
{
#ifdef DEBUGRPC
PrintToDebugger("Error on LogonUser with error %d\n", GetLastError());
#endif
CloseHandle(*hToken);
return (RPC_S_OUT_OF_MEMORY);
}
}
CloseHandle(*hToken);
#endif
ClientAuthInfo.AuthenticationLevel = MappedAuthenticationLevel;
ClientAuthInfo.AuthenticationService = AuthenticationService;
ClientAuthInfo.AuthIdentity = AuthIdentity;
ClientAuthInfo.AuthorizationService = AuthorizationService;
ClientAuthInfo.IdentityTracking = IdentityTracking;
ClientAuthInfo.Capabilities = Capabilities;
if (MappedAuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE)
{
ClientAuthInfo.ImpersonationType = RPC_C_IMP_LEVEL_ANONYMOUS;
}
else
{
ClientAuthInfo.ImpersonationType = ImpersonationType;
}
AuthInfoInitialized = 1;
return(RPC_S_OK);
}
unsigned long
WMSG_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
WMSG_BINDING_HANDLE::AddAssociation (
IN WMSG_CASSOCIATION * Association
)
/*++
Routine Description:
This supplied remote procedure call needs to be put into the dictionary
of association
--*/
{
int err;
RequestGlobalMutex();
err = SecAssociation.Insert(Association) ;
ClearGlobalMutex();
return(err);
}
inline void
WMSG_BINDING_HANDLE::RemoveAssociation (
IN WMSG_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
--*/
{
RequestGlobalMutex();
SecAssociation.DeleteItemByBruteForce(Association);
ClearGlobalMutex();
}
static WMSG_CASSOCIATION_DICT * WMSGAssociationDict = 0;
WMSG_CASSOCIATION::WMSG_CASSOCIATION (
IN DCE_BINDING * DceBinding,
IN CLIENT_AUTH_INFO *pClientAuthInfo,
OUT RPC_STATUS * RpcStatus
) : AssociationMutex(RpcStatus)
/*++
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.
RpcStatus - Returns the result of creating the association mutex.
--*/
{
this->DceBinding = DceBinding;
AssociationReferenceCount = 1;
LpcClientPort = 0;
LpcReceivePort = 0;
AssociationDeleted = 0 ;
PendingCallCount = 0 ;
Address = 0 ;
BackConnectionCreated = 0;
pAuthInfo = 0;
if(*RpcStatus == RPC_S_OK)
{
pAuthInfo = new CLIENT_AUTH_INFO(pClientAuthInfo,RpcStatus);
if (pAuthInfo == NULL)
{
*RpcStatus = RPC_S_OUT_OF_MEMORY;
return ;
}
}
if (*RpcStatus == RPC_S_OK)
{
CachedCCall = new WMSG_CCALL(RpcStatus);
if (*RpcStatus == RPC_S_OK)
{
if ( CachedCCall == 0 )
{
delete pAuthInfo ;
*RpcStatus = RPC_S_OUT_OF_MEMORY ;
pAuthInfo = 0;
return;
}
CachedCCall->SetAssociation(this);
CachedCCall->ConnectionKey = ActiveCCalls.Insert(CachedCCall) ;
if (CachedCCall->ConnectionKey == -1)
{
delete pAuthInfo ;
delete CachedCCall ;
*RpcStatus = RPC_S_OUT_OF_MEMORY ;
pAuthInfo = 0;
return ;
}
CachedCCallFlag = 1;
}
}
AssociationType = ASSOCIATION_TYPE_CLIENT ;
}
WMSG_CASSOCIATION::~WMSG_CASSOCIATION (
)
{
WMSG_BINDING * Binding;
WMSG_CCALL * CCall ;
if (DceBinding != 0)
{
delete DceBinding;
}
Bindings.Reset();
while ( (Binding = Bindings.Next()) != 0 )
{
Bindings.Delete(Binding->PresentationContext);
delete Binding;
}
// Abort All the CCALLs on this associatoin
ActiveCCalls.Reset() ;
while ((CCall = ActiveCCalls.Next()) != 0)
{
// BUGBUG: if call is in progress abort it, otherwise delete it
// but.. there is a race condition in doing that...
delete CCall ;
}
CloseLpcClientPort();
if(pAuthInfo != 0)
{
delete pAuthInfo;
}
}
RPC_STATUS
WMSG_CASSOCIATION::CreateBackConnection (
)
{
WMSG_BIND_BACK_MESSAGE WMSGMessage ;
WMSG_MESSAGE WMSGReply ;
NTSTATUS NtStatus ;
RPC_STATUS RpcStatus = RPC_S_OK;
if (BackConnectionCreated == 0)
{
AssociationMutex.Request() ;
if (BackConnectionCreated)
{
AssociationMutex.Clear() ;
return RPC_S_OK ;
}
if (LpcClientPort == 0)
{
RpcStatus = OpenLpcPort(TRUE) ;
if (RpcStatus != RPC_S_OK)
{
AssociationMutex.Clear() ;
return RpcStatus ;
}
}
else
{
// check if we are actually listening on an endpoint
// if we are not, fail the call
if (GlobalWMsgServer->IsWMsgEndpointInitialized == 0)
{
AssociationMutex.Clear() ;
return RPC_S_NOT_LISTENING ;
}
WMSGMessage.LpcHeader.u1.s1.DataLength =
sizeof(WMSG_BIND_BACK_MESSAGE) - sizeof(PORT_MESSAGE);
WMSGMessage.LpcHeader.u1.s1.TotalLength =
sizeof(WMSG_BIND_BACK_MESSAGE);
WMSGMessage.LpcHeader.u2.ZeroInit = 0;
WMSGMessage.MessageType = WMSG_MSG_BIND_BACK;
WMSGMessage.pAssoc = (PVOID) this ;
GlobalWMsgServer->GetWMsgEndpoint(
(RPC_CHAR *) WMSGMessage.szPortName) ;
NtStatus = NtRequestWaitReplyPort(LpcClientPort,
(PORT_MESSAGE *) &WMSGMessage,
(PORT_MESSAGE *) &WMSGReply) ;
if ( NT_ERROR(NtStatus) )
{
AssociationMutex.Clear() ;
return (RPC_S_OUT_OF_MEMORY) ;
}
ASSERT(WMSGReply.Ack.MessageType == WMSG_MSG_ACK) ;
if (WMSGReply.Ack.RpcStatus != RPC_S_OK)
{
AssociationMutex.Clear() ;
return WMSGReply.Ack.RpcStatus ;
}
}
BackConnectionCreated = 1 ;
AssociationMutex.Clear() ;
}
return RpcStatus ;
}
void
WMSG_CASSOCIATION::RemoveReference (
int DeleteFromDictionary
)
{
int fDelete = 0;
GlobalMutexRequest();
AssociationReferenceCount -= 1;
if ( AssociationReferenceCount == 0 )
{
fDelete = 1;
WMSGAssociationDict->Delete(AssociationDictKey);
if (Address && DeleteFromDictionary)
{
Address->DeleteCAssoc(DictionaryKey) ;
}
}
GlobalMutexClear();
if (fDelete)
{
AssociationMutex.Request() ;
if (PendingCallCount > 0)
{
AssociationDeleted = 1 ;
}
else
{
AssociationMutex.Clear() ;
delete this;
return ;
}
AssociationMutex.Clear() ;
}
}
RPC_STATUS
WMSG_CASSOCIATION::AllocateCCall (
OUT WMSG_CCALL ** CCall,
IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation
)
/*++
Routine Description:
This method will allocate an WMSG_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 WMSG_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:
--*/
{
WMSG_BINDING * Binding;
RPC_STATUS RpcStatus;
int RetryCount;
AssociationMutex.Request();
Bindings.Reset();
while ( (Binding = Bindings.Next()) != 0 )
{
if ( Binding->CompareWithRpcInterfaceInformation(
RpcInterfaceInformation) == 0 )
{
RpcStatus = ActuallyAllocateCCall(CCall, Binding);
if (RpcStatus == RPC_S_OK)
{
PendingCallCount++ ;
}
AssociationMutex.Clear();
return(RpcStatus);
}
}
AssociationMutex.Clear();
RetryCount = 0;
do
{
RpcStatus = ActuallyDoBinding(RpcInterfaceInformation, &Binding);
if (RpcStatus != RPC_S_SERVER_UNAVAILABLE) break;
// The server appears to have gone away, close the port and retry.
RetryCount++;
AbortAssociation();
} while(RetryCount < 3);
if ( RpcStatus == RPC_S_OK )
{
ASSERT(Binding != 0);
AssociationMutex.Request();
RpcStatus = ActuallyAllocateCCall(CCall, Binding);
if (RpcStatus == RPC_S_OK)
{
PendingCallCount++ ;
}
AssociationMutex.Clear();
}
return(RpcStatus);
}
RPC_STATUS
WMSG_CASSOCIATION::ActuallyAllocateCCall (
OUT WMSG_CCALL ** CCall,
IN WMSG_BINDING * Binding
)
/*++
Routine Description:
We need to allocate a WMSG_CCALL object for the call. We also need
to initialize it so that it specified the correct bound interface.
Arguments:
CCall - Returns the allocated WMSG_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 WMSG_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 WMSG_CALL object.
Notes:
The global mutex will be held when this routine is called.
--*/
{
RPC_STATUS RpcStatus = RPC_S_OK ;
if (CachedCCallFlag != 0)
{
*CCall = CachedCCall ;
CachedCCallFlag = 0;
}
else
{
FreeCCalls.Reset() ;
while ((*CCall = FreeCCalls.Next()) != 0)
{
if ((*CCall)->SupportedPContext(Binding))
{
FreeCCalls.Delete((*CCall)->FreeConnectionKey) ;
(*CCall)->CallFlags &= ~CALL_FREE ;
return (RPC_S_OK) ;
}
}
*CCall = new WMSG_CCALL(&RpcStatus);
if ( *CCall == 0 )
{
return(RPC_S_OUT_OF_MEMORY);
}
if (RpcStatus != RPC_S_OK)
{
delete *CCall ;
return RpcStatus ;
}
(*CCall)->SetAssociation(this);
(*CCall)->ConnectionKey = ActiveCCalls.Insert(*CCall) ;
if ((*CCall)->ConnectionKey == -1)
{
delete *CCall ;
return (RPC_S_OUT_OF_MEMORY) ;
}
}
(*CCall)->SetPresentationContext(Binding);
return(RPC_S_OK);
}
RPC_STATUS
WMSG_CASSOCIATION::ActuallyDoBinding (
IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation,
OUT WMSG_BINDING ** Binding
)
/*++
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:
--*/
{
WMSG_MESSAGE WMSGMessage;
NTSTATUS NtStatus;
RPC_STATUS RpcStatus;
int DictKey ;
// To start with, see if we have an LPC port; if we dont, open one
// up.
AssociationMutex.Request();
if ( LpcClientPort == 0 )
{
// we now need to bind explicitly
RpcStatus = OpenLpcPort(FALSE);
}
// Otherwise, just go ahead and send the bind request message to the
// server, and then wait for the bind response.
WMSGMessage.LpcHeader.u1.s1.DataLength = sizeof(WMSG_BIND_MESSAGE)
- sizeof(PORT_MESSAGE);
WMSGMessage.LpcHeader.u1.s1.TotalLength = sizeof(WMSG_BIND_MESSAGE);
WMSGMessage.LpcHeader.u2.ZeroInit = 0;
WMSGMessage.Bind.MessageType = WMSG_MSG_BIND;
WMSGMessage.Bind.BindExchange.InterfaceId =
RpcInterfaceInformation->InterfaceId;
WMSGMessage.Bind.BindExchange.TransferSyntax =
RpcInterfaceInformation->TransferSyntax;
NtStatus = NtRequestWaitReplyPort(LpcClientPort,
(PORT_MESSAGE *) &WMSGMessage,
(PORT_MESSAGE *) &WMSGMessage) ;
if ( NT_SUCCESS(NtStatus) )
{
ASSERT( WMSGMessage.Bind.MessageType == WMSG_BIND_ACK );
if ( WMSGMessage.Bind.BindExchange.RpcStatus == RPC_S_OK )
{
*Binding = new WMSG_BINDING(RpcInterfaceInformation);
if ( *Binding == 0 )
{
AssociationMutex.Clear();
return(RPC_S_OUT_OF_MEMORY);
}
(*Binding)->PresentationContext =
WMSGMessage.Bind.BindExchange.PresentationContext;
DictKey = Bindings.Insert(*Binding) ;
if ( DictKey != (*Binding)->PresentationContext )
{
if (DictKey != -1)
{
Bindings.Delete(DictKey) ;
}
delete *Binding;
AssociationMutex.Clear();
return(RPC_S_OUT_OF_MEMORY);
}
}
AssociationMutex.Clear();
RpcStatus = WMSGMessage.Bind.BindExchange.RpcStatus ;
ASSERT (RpcStatus != RPC_S_SERVER_UNAVAILABLE &&
RpcStatus != RPC_S_ACCESS_DENIED) ;
return (RpcStatus) ;
}
AssociationMutex.Clear();
return(RPC_S_SERVER_UNAVAILABLE);
}
RPC_STATUS
WMSG_CASSOCIATION::UnblockCConnection(
IN WMSG_MESSAGE *WMSGMsg,
IN HANDLE LpcPort
)
{
int ConnectionKey ;
WMSG_CCALL *CCall ;
RPC_STATUS RpcStatus ;
ConnectionKey = WMSGMsg->Rpc.RpcHeader.ConnectionKey ;
AssociationMutex.Request() ;
CCall = ActiveCCalls.Find(ConnectionKey) ;
if (CCall == 0)
{
AssociationMutex.Clear() ;
MessageCache->FreeMessage(WMSGMsg) ;
return (RPC_S_OUT_OF_MEMORY) ;
}
AssociationMutex.Clear() ;
RpcStatus = CCall->Unblock(WMSGMsg, LpcPort) ;
return RpcStatus ;
}
inline RPC_STATUS
WMSG_CCALL::Unblock(
IN WMSG_MESSAGE *WMSGMsg,
IN HANDLE LpcPort
)
{
unsigned char MessageType ;
NTSTATUS NtStatus ;
int retval ;
ConnMutex.Request() ;
ASSERT((CallFlags & (~CALL_STATES)) == 0) ;
if (CallFlags & CALL_SERVER_ABORTED)
{
MessageCache->FreeMessage(WMSGMsg) ;
}
else
{
LpcReplyMessage = WMSGMsg ;
MessageType = WMSGMsg->Rpc.RpcHeader.MessageType ;
ASSERT(MessageType == WMSG_MSG_RESPONSE ||
MessageType == WMSG_MSG_FAULT) ;
if (MessageType == WMSG_MSG_RESPONSE)
{
if ((CallFlags & CALL_CLIENT_CANCELLED) &&
WMSGMsg->Rpc.RpcHeader.Flags & WMSG_BUFFER_SERVER)
{
WMSGMsg->Ack.MessageType = WMSG_MSG_ACK ;
WMSGMsg->LpcHeader.u1.s1.DataLength = sizeof(WMSG_ACK_MESSAGE)
- sizeof(PORT_MESSAGE) ;
WMSGMsg->LpcHeader.u1.s1.TotalLength =
sizeof(WMSG_ACK_MESSAGE) ;
// setup the reply message
NtStatus = NtReplyPort(LpcPort, (PORT_MESSAGE *) WMSGMsg) ;
if ( NT_ERROR(NtStatus) )
{
#if DBG
PrintToDebugger("WMSG: NtReplyPort failed: 0x%X\n", NtStatus) ;
#endif
}
}
else
{
// read the rest of the data if neccassary
WMSGMessageToRpcMessage(LpcReplyMessage,
RpcReplyMessage, LpcPort, FALSE) ;
}
}
if (CallFlags & CALL_CLIENT_CANCELLED)
{
MessageCache->FreeMessage(WMSGMsg) ;
Association->FreeStaleCCall(this) ;
CallFlags |= CALL_UNBLOCKED ;
LpcReplyMessage = 0;
ConnMutex.Clear() ;
return (RPC_S_OK) ;
}
}
CallFlags |= CALL_UNBLOCKED ;
if ((MsgFlags & RPCFLG_INPUT_SYNCHRONOUS) == 0 && hWnd)
{
ASSERT(hWnd != NULL) ;
Association->AddReference() ;
ConnMutex.Clear() ;
GlobalWMsgServer->PostMessageW(hWnd,
WM_MSG_CALL_COMPLETE,
(WPARAM) WMSG_MAGIC_VALUE, (LPARAM) this) ;
}
else
{
ConnMutex.Clear() ;
retval = SetEvent(hSyncEvent) ;
ASSERT(retval != 0) ;
}
return (RPC_S_OK) ;
}
void
WMSG_CCALL::CancelCall(
)
{
ConnMutex.Request() ;
if ((CallFlags & CALL_UNBLOCKED) == 0)
{
CallFlags |= CALL_SERVER_ABORTED ;
if ((MsgFlags & RPCFLG_INPUT_SYNCHRONOUS) == 0 && hWnd)
{
if (CallFlags & CALL_CLIENT_CANCELLED)
{
Association->FreeStaleCCall(this) ;
}
else
{
ASSERT(hWnd != NULL) ;
Association->AddReference() ;
GlobalWMsgServer->PostMessageW(hWnd, WM_MSG_CALL_COMPLETE,
(WPARAM) WMSG_MAGIC_VALUE, (LPARAM) this) ;
}
}
else
{
if (SetEvent(hSyncEvent) && (CallFlags & CALL_CLIENT_CANCELLED))
{
Association->FreeStaleCCall(this) ;
}
}
}
ConnMutex.Clear() ;
}
inline BOOL
WMSG_CCALL::WaitForReply (
IN RPC_BLOCKING_FN BlockingFunction,
IN void *Context,
IN RPC_STATUS *RpcStatus
)
{
int Status = TRUE;
if (BlockingFunction)
{
while (1)
{
*RpcStatus = (*BlockingFunction)(hWnd, Context, hSyncEvent) ;
if (*RpcStatus == RPC_S_OK)
{
if (CallFlags & CALL_SERVER_ABORTED)
{
Status = FALSE ;
}
else
{
if ((CallFlags & CALL_UNBLOCKED) == 0)
{
continue;
}
ASSERT(LpcReplyMessage->Rpc.RpcHeader.MessageType
!= WMSG_MSG_REQUEST) ;
}
break;
}
else if (*RpcStatus == RPC_S_CALL_PENDING)
{
if (hWnd)
{
if (CallFlags & CALL_COMPLETE)
{
ASSERT(LpcReplyMessage->Rpc.RpcHeader.MessageType
!= WMSG_MSG_REQUEST) ;
break ;
}
else if (CallFlags & CALL_SERVER_ABORTED)
{
Status = FALSE ;
break;
}
}
}
else
{
ConnMutex.Request() ;
if ((MsgFlags & RPCFLG_INPUT_SYNCHRONOUS) == 0 && hWnd)
{
if (CallFlags & CALL_COMPLETE)
{
*RpcStatus = RPC_S_OK ;
}
else
{
CallFlags |= CALL_CLIENT_CANCELLED ;
Status = FALSE ;
}
}
else
{
if (CallFlags & CALL_UNBLOCKED)
{
*RpcStatus = RPC_S_OK ;
}
else
{
CallFlags |= CALL_CLIENT_CANCELLED ;
Status = FALSE ;
}
}
ResetEvent(hSyncEvent) ;
ConnMutex.Clear() ;
break;
}
}
}
else
{
*RpcStatus = I_RpcBlockingFunc(hSyncEvent) ;
if (*RpcStatus == RPC_S_OK)
{
if (CallFlags & CALL_SERVER_ABORTED)
{
Status = FALSE ;
}
else
{
ASSERT(LpcReplyMessage->Rpc.RpcHeader.MessageType
!= WMSG_MSG_REQUEST) ;
}
}
}
return Status ;
}
inline NTSTATUS
WMSG_CCALL::AsyncSendReceiveHelper(
IN OUT WMSG_MESSAGE **WMSGMsg,
IN OUT PRPC_MESSAGE Message,
IN RPC_BLOCKING_FN BlockingFunction,
IN void *Context,
IN RPC_STATUS *RpcStatus
)
{
NTSTATUS NtStatus;
WMSG_MESSAGE ReplyMessage ;
unsigned char MessageType ;
DWORD RetVal ;
void *SavedBuffer = 0;
int Flags ;
*RpcStatus = RPC_S_OK ;
CallFlags &= ~(CALL_STATES) ;
RpcReplyMessage = Message ;
MsgFlags = Message->RpcFlags ;
ASSERT(LpcReplyMessage == 0) ;
(*WMSGMsg)->Rpc.RpcHeader.ConnectionKey = (short) ConnectionKey ;
Flags = (*WMSGMsg)->Rpc.RpcHeader.Flags ;
MessageType = (*WMSGMsg)->Rpc.RpcHeader.MessageType ;
if ((MessageType == WMSG_MSG_REQUEST) &&
((*WMSGMsg)->Rpc.RpcHeader.Flags & WMSG_BUFFER_REQUEST))
{
SavedBuffer = Message->Buffer ;
NtStatus = NtRequestWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) *WMSGMsg,
(PORT_MESSAGE *) &ReplyMessage) ;
if (!NT_SUCCESS(NtStatus))
{
return (NtStatus) ;
}
// BUGBUG: Check the reply message to see if it was garbage
ASSERT(ReplyMessage.Ack.MessageType == WMSG_MSG_ACK) ;
}
else
{
ASSERT ((MessageType == WMSG_MSG_BIND) ||
(MessageType == WMSG_MSG_FAULT) ||
(MessageType == WMSG_MSG_CLOSE) ||
((MessageType == WMSG_MSG_REQUEST) &&
((*WMSGMsg)->Rpc.RpcHeader.Flags & WMSG_BUFFER_IMMEDIATE))) ;
NtStatus = NtRequestPort(Association->LpcClientPort,
(PORT_MESSAGE *) *WMSGMsg) ;
if (!NT_SUCCESS(NtStatus))
{
return (NtStatus) ;
}
}
if (!(Flags & DISPATCH_ASYNC))
{
if (WaitForReply(BlockingFunction, Context, RpcStatus))
{
MessageCache->FreeMessage(*WMSGMsg) ;
*WMSGMsg = LpcReplyMessage ;
LpcReplyMessage = 0;
}
else
{
NtStatus = STATUS_NO_MEMORY ;
}
}
if (SavedBuffer)
{
RpcpFarFree(SavedBuffer) ;
}
return NtStatus ;
}
SECURITY_IMPERSONATION_LEVEL
GetImpType (
IN unsigned long ImpersonationType
)
{
switch (ImpersonationType)
{
case RPC_C_IMP_LEVEL_ANONYMOUS:
return SecurityAnonymous ;
case RPC_C_IMP_LEVEL_IDENTIFY:
return SecurityIdentification ;
case RPC_C_IMP_LEVEL_IMPERSONATE:
return SecurityImpersonation ;
case RPC_C_IMP_LEVEL_DELEGATE:
return SecurityDelegation ;
}
ASSERT(0) ;
return SecurityImpersonation ;
}
RPC_STATUS
WMSG_CASSOCIATION::OpenLpcPort (
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 RpcStatus;
WMSG_BIND_EXCHANGE BindExchange;
unsigned long BindExchangeLength = sizeof(WMSG_BIND_EXCHANGE);
// Look at the network options and initialize the security quality
// of service appropriately.
// take BindingSetAuthInfo over String Options because StringOptions is static
if (pAuthInfo != 0 &&
(pAuthInfo->AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) )
{
SecurityQualityOfService.EffectiveOnly = TRUE;
if (pAuthInfo->IdentityTracking == RPC_C_QOS_IDENTITY_STATIC)
{
SecurityQualityOfService.ContextTrackingMode =
SECURITY_STATIC_TRACKING;
}
else
{
SecurityQualityOfService.ContextTrackingMode =
SECURITY_DYNAMIC_TRACKING;
}
SecurityQualityOfService.ImpersonationLevel =
GetImpType(pAuthInfo->ImpersonationType) ;
}
else
{
if ( DceBinding->InqNetworkOptions()[0] != 0 )
{
RpcStatus = I_RpcParseSecurity(DceBinding->InqNetworkOptions(),
&SecurityQualityOfService);
if ( RpcStatus != RPC_S_OK )
{
ASSERT( RpcStatus == RPC_S_INVALID_NETWORK_OPTIONS );
return(RpcStatus);
}
}
else
{
SecurityQualityOfService.EffectiveOnly = TRUE;
SecurityQualityOfService.ContextTrackingMode =
SECURITY_DYNAMIC_TRACKING;
if (pAuthInfo)
{
SecurityQualityOfService.ImpersonationLevel =
GetImpType(pAuthInfo->ImpersonationType) ;
}
else
{
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation ;
}
}
}
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
// Allocate and initialize the port name. We need to stick the
// WMSG_DIRECTORY_NAME on the front of the endpoint. This is for
// security reasons (so that anyone can create WMSG endpoints).
LpcPortName = new RPC_CHAR[RpcpStringLength(DceBinding->InqEndpoint())
+ RpcpStringLength(WMSG_DIRECTORY_NAME) + 1];
if ( LpcPortName == 0 )
{
return(RPC_S_OUT_OF_MEMORY);
}
RpcpMemoryCopy(LpcPortName, WMSG_DIRECTORY_NAME,
RpcpStringLength(WMSG_DIRECTORY_NAME) * sizeof(RPC_CHAR));
RpcpMemoryCopy(LpcPortName + RpcpStringLength(WMSG_DIRECTORY_NAME),
DceBinding->InqEndpoint(),
(RpcpStringLength(DceBinding->InqEndpoint()) + 1)
* sizeof(RPC_CHAR));
RtlInitUnicodeString(&UnicodeString, LpcPortName);
BindExchange.ConnectType = WMSG_CONNECT_REQUEST ;
BindExchange.pAssoc = (PVOID) this ;
if (fBindBack)
{
BindExchange.fBindBack = 1 ;
GlobalWMsgServer->GetWMsgEndpoint(
(RPC_CHAR *) BindExchange.szPortName) ;
}
else
{
BindExchange.fBindBack = 0;
}
NtStatus = NtConnectPort(&LpcClientPort, &UnicodeString,
&SecurityQualityOfService, NULL, NULL, NULL,
&BindExchange, &BindExchangeLength);
delete LpcPortName;
if ( NT_SUCCESS(NtStatus) )
{
ASSERT( BindExchangeLength == sizeof(WMSG_BIND_EXCHANGE) );
return(BindExchange.RpcStatus);
}
if ( NtStatus == STATUS_PORT_CONNECTION_REFUSED )
{
if (BindExchange.RpcStatus == 0)
{
// This is bogus, LPC should always return the
// reject message from the server...
return(RPC_S_SERVER_UNAVAILABLE);
}
return(BindExchange.RpcStatus);
}
if ( NtStatus == STATUS_NO_MEMORY )
{
return(RPC_S_OUT_OF_MEMORY);
}
if ( ( NtStatus == STATUS_INSUFFICIENT_RESOURCES )
|| ( NtStatus == STATUS_QUOTA_EXCEEDED ) )
{
return(RPC_S_OUT_OF_RESOURCES);
}
if ( NtStatus == STATUS_OBJECT_PATH_INVALID )
{
return(RPC_S_INVALID_ENDPOINT_FORMAT);
}
if ( NtStatus == STATUS_ACCESS_DENIED )
{
return (RPC_S_ACCESS_DENIED);
}
#if DBG
if ( NtStatus != STATUS_OBJECT_NAME_NOT_FOUND )
{
PrintToDebugger("WMSG : NtConnectPort : %lx\n", NtStatus);
}
#endif // DBG
ASSERT( NtStatus == STATUS_OBJECT_NAME_NOT_FOUND );
return(RPC_S_SERVER_UNAVAILABLE);
}
BOOL
WMSG_CASSOCIATION::FreeCCall (
IN WMSG_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.
--*/
{
int fDelete = 0 ;
AssociationMutex.Request();
if ((CCall->IsAsync == 0) || !(CCall->CallFlags & CALL_CLIENT_CANCELLED))
{
if (CCall == CachedCCall)
{
CachedCCallFlag = 1 ;
}
else
{
CCall->FreeMessage() ;
CCall->CallFlags |= CALL_FREE ;
if ((CCall->FreeConnectionKey = FreeCCalls.Insert(CCall)) == -1)
{
ASSERT(0) ;
#if DBG
PrintToDebugger("WMSG: FreeCCall, Insert failed\n") ;
#endif
}
}
PendingCallCount-- ;
if (PendingCallCount == 0 && AssociationDeleted)
{
fDelete = 1;
}
AssociationMutex.Clear();
if (fDelete)
{
delete this ;
}
return TRUE ;
}
AssociationMutex.Clear();
return FALSE ;
}
void
WMSG_CASSOCIATION::FreeStaleCCall (
IN WMSG_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.
--*/
{
CCall->CallFlags &= ~CALL_CLIENT_CANCELLED ;
CCall->FreeCCall() ;
}
WMSG_CASSOCIATION *
FindOrCreateWMSGAssociation (
IN DCE_BINDING * DceBinding,
IN CLIENT_AUTH_INFO *pClientAuthInfo
)
/*++
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.
Return Value:
An association which supports the requested binding will be returned.
Otherwise, zero will be returned, indicating insufficient memory.
--*/
{
WMSG_CASSOCIATION * Association;
RPC_STATUS RpcStatus = RPC_S_OK;
// First, we check for an existing association.
WMSGAssociationDict->Reset();
while ( (Association = WMSGAssociationDict->Next()) != 0 )
{
if ( Association->CompareWithDceBinding(DceBinding) == 0 &&
(Association->IsSupportedAuthInfo(pClientAuthInfo) == TRUE ))
{
Association->AssociationReferenceCount += 1;
delete DceBinding;
return(Association);
}
}
Association = new WMSG_CASSOCIATION(DceBinding,
pClientAuthInfo, &RpcStatus);
if ( ( Association != 0 ) && ( RpcStatus == RPC_S_OK ))
{
Association->AssociationDictKey =
WMSGAssociationDict->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.
--*/
{
WMSG_CASSOCIATION * Association;
if ( WMSGAssociationDict != 0 )
{
WMSGAssociationDict->Reset();
while ( (Association = WMSGAssociationDict->Next()) != 0 )
{
Association->RemoveReference() ;
}
}
}
void
WMSG_CASSOCIATION::AbortAssociation (
)
/*++
Routine Description:
This association needs to be aborted because a the server side of the
lpc port has been closed.
--*/
{
WMSG_BINDING * Binding;
WMSG_CCALL *CCall ;
AssociationMutex.Request();
CloseLpcClientPort();
Bindings.Reset();
while ( (Binding = Bindings.Next()) != 0 )
{
Bindings.Delete(Binding->PresentationContext);
delete Binding;
}
ActiveCCalls.Reset() ;
while ((CCall = ActiveCCalls.Next()) != 0)
{
CCall->CancelCall() ;
}
AssociationMutex.Clear();
}
void
WMSG_CASSOCIATION::CloseLpcClientPort (
)
/*++
Routine Description:
The LpcClientPort will be closed (and a close message sent to the server).
--*/
{
NTSTATUS NtStatus;
if ( LpcClientPort != 0 )
{
NtStatus = NtClose(LpcClientPort);
#if DBG
if ( !NT_SUCCESS(NtStatus) )
{
PrintToDebugger("RPC : NtClose : %lx\n", NtStatus);
}
#endif // DBG
if (LpcReceivePort)
{
NtStatus = NtClose(LpcReceivePort) ;
#if DBG
if ( !NT_SUCCESS(NtStatus) )
{
PrintToDebugger("RPC : NtClose : %lx\n", NtStatus);
}
#endif
ASSERT( NT_SUCCESS(NtStatus) );
}
LpcClientPort = 0;
LpcReceivePort = 0;
BackConnectionCreated = 0;
}
}
BOOL
WMSG_CASSOCIATION::IsSupportedAuthInfo(
IN CLIENT_AUTH_INFO * ClientAuthInfo
)
{
if (!ClientAuthInfo)
{
if (pAuthInfo->ImpersonationType == RPC_C_IMP_LEVEL_IMPERSONATE)
{
return TRUE ;
}
return FALSE;
}
if ( (ClientAuthInfo->AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE
&& pAuthInfo->AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE)
|| (pAuthInfo->AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE
&& ClientAuthInfo->AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) )
{
return(FALSE);
}
ASSERT(ClientAuthInfo->AuthenticationService == RPC_C_AUTHN_WINNT);
if ( ClientAuthInfo->AuthorizationService
!= pAuthInfo->AuthorizationService )
{
return(FALSE);
}
if ( ClientAuthInfo->IdentityTracking != pAuthInfo->IdentityTracking )
{
return(FALSE);
}
if (ClientAuthInfo->ImpersonationType != pAuthInfo->ImpersonationType)
{
return (FALSE) ;
}
return(TRUE);
}
WMSG_CCALL::WMSG_CCALL (
IN OUT RPC_STATUS PAPI *RpcStatus
) : ConnMutex(RpcStatus)
/*++
--*/
{
CurrentBindingHandle = 0;
Association = 0;
CallAbortedFlag = 0;
WMSGMessage = 0;
RecursionCount = 0;
CallFlags = 0 ;
CallStack = 0;
CachedWMSGMessage = 0;
FirstFrag = 1;
FirstReceive = 1;
CurrentBufferLength = 0;
LpcReplyMessage = 0;
SCallKey = 0;
BufferComplete = 0;
ConnectionKey = -1;
FreeConnectionKey = -1;
hSyncEvent = CreateEvent(NULL, FALSE, FALSE, NULL) ;
if (hSyncEvent == 0)
{
*RpcStatus = RPC_S_OUT_OF_MEMORY ;
}
}
WMSG_CCALL::~WMSG_CCALL (
)
/*++
--*/
{
if (WMSGMessage)
{
MessageCache->FreeMessage(WMSGMessage) ;
}
if (CachedWMSGMessage)
{
MessageCache->FreeMessage(CachedWMSGMessage) ;
}
if (ConnectionKey != -1)
{
// the association mutex is currently held
Association->ActiveCCalls.Delete(ConnectionKey) ;
}
if ((FreeConnectionKey != -1) && (CallFlags & CALL_FREE))
{
Association->FreeCCalls.Delete(FreeConnectionKey) ;
}
CloseHandle(hSyncEvent) ;
}
RPC_STATUS
WMSG_CCALL::GetBuffer (
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
We will allocate a buffer which will be used to either send a request
or receive a response.
Arguments:
Message - Supplies the length of the buffer that is needed. The buffer
will be returned.
Return Value:
RPC_S_OK - A buffer has been successfully allocated. It will be of at
least the required length.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate that
large a buffer.
--*/
{
IsAsync = 0 ;
if (WMSGMessage == 0)
{
WMSGMessage = MessageCache->AllocateMessage() ;
if (WMSGMessage == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
}
if (Message->RpcFlags & RPC_BUFFER_PARTIAL)
{
CurrentBufferLength = (Message->BufferLength < MINIMUM_PARTIAL_BUFFLEN)
? MINIMUM_PARTIAL_BUFFLEN:Message->BufferLength ;
Message->Buffer = RpcpFarAllocate(CurrentBufferLength);
if ( Message->Buffer == 0 )
{
CurrentBufferLength = 0 ;
return(RPC_S_OUT_OF_MEMORY);
}
}
else if ( Message->BufferLength <= MAXIMUM_MESSAGE_BUFFER )
{
CurrentBufferLength = MAXIMUM_MESSAGE_BUFFER ;
ASSERT( ((unsigned long) WMSGMessage->Rpc.Buffer) % 8 == 0 );
Message->Buffer = WMSGMessage->Rpc.Buffer;
Message->Handle = this;
WMSGMessage->Rpc.RpcHeader.Flags = WMSG_BUFFER_IMMEDIATE;
WMSGMessage->LpcHeader.u2.ZeroInit = 0;
WMSGMessage->LpcHeader.u1.s1.DataLength = Align4(Message->BufferLength)
+ sizeof(WMSG_RPC_HEADER);
return(RPC_S_OK);
}
else
{
CurrentBufferLength = Message->BufferLength ;
Message->Buffer = RpcpFarAllocate(Message->BufferLength);
if ( Message->Buffer == 0 )
{
return(RPC_S_OUT_OF_MEMORY);
}
}
Message->Handle = this;
WMSGMessage->Rpc.RpcHeader.Flags = WMSG_BUFFER_REQUEST;
WMSGMessage->Rpc.Request.CountDataEntries = 1;
WMSGMessage->Rpc.Request.DataEntries[0].Base = Message->Buffer;
WMSGMessage->Rpc.Request.DataEntries[0].Size = Message->BufferLength;
WMSGMessage->LpcHeader.CallbackId = 0;
WMSGMessage->LpcHeader.u2.ZeroInit = 0;
WMSGMessage->LpcHeader.u2.s2.DataInfoOffset = sizeof(PORT_MESSAGE)
+ sizeof(WMSG_RPC_HEADER);
WMSGMessage->LpcHeader.u1.s1.DataLength = sizeof(WMSG_RPC_HEADER)
+ sizeof(PORT_DATA_INFORMATION);
return(RPC_S_OK);
}
RPC_STATUS
WMSG_CCALL::AsyncSendReceive(
IN OUT PRPC_MESSAGE Message,
IN OPTIONAL void *Context,
HWND hWnd
)
{
NTSTATUS NtStatus;
RPC_STATUS ExceptionCode, RpcStatus;
void * OriginalMessageBuffer;
int ActiveCallSetupFlag = 0;
void * SavedBuffer;
if ( CallAbortedFlag != 0 )
{
return(RPC_S_CALL_FAILED_DNE);
}
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
IsAsync = 1 ;
this->hWnd = hWnd ;
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
WMSGMessage->LpcHeader.u1.s1.TotalLength = sizeof(PORT_MESSAGE)
+ WMSGMessage->LpcHeader.u1.s1.DataLength;
WMSGMessage->Rpc.RpcHeader.MessageType = WMSG_MSG_REQUEST;
WMSGMessage->Rpc.RpcHeader.ProcedureNumber = Message->ProcNum;
WMSGMessage->Rpc.RpcHeader.PresentationContext = PresentationContext;
if (Message->RpcFlags & RPCFLG_INPUT_SYNCHRONOUS)
{
WMSGMessage->Rpc.RpcHeader.Flags |= DISPATCH_INPUT_SYNC ;
}
if (Message->RpcFlags & RPCFLG_ASYNCHRONOUS)
{
WMSGMessage->Rpc.RpcHeader.Flags |= DISPATCH_ASYNC ;
}
if ( ( CurrentBindingHandle->InqIfNullObjectUuid() == 0 ) )
{
CurrentBindingHandle->InquireObjectUuid(
(RPC_UUID *) &(WMSGMessage->Rpc.RpcHeader.ObjectUuid));
WMSGMessage->Rpc.RpcHeader.ObjectUuidFlag = 1;
}
else
{
WMSGMessage->Rpc.RpcHeader.ObjectUuidFlag = 0;
}
NtStatus = AsyncSendReceiveHelper(
&WMSGMessage,
Message,
CurrentBindingHandle->BlockingFunction,
Context,
&RpcStatus
) ;
if (RpcStatus == RPC_E_CALL_CANCELED)
{
return (RpcStatus) ;
}
if ( NT_ERROR(NtStatus) )
{
if ( NtStatus == STATUS_NO_MEMORY )
{
FreeCCall();
return(RPC_S_OUT_OF_MEMORY);
}
if ( NtStatus == STATUS_INSUFFICIENT_RESOURCES )
{
FreeCCall();
return(RPC_S_OUT_OF_RESOURCES);
}
#if DBG
if ( ( NtStatus != STATUS_INVALID_PORT_HANDLE )
&& ( NtStatus != STATUS_INVALID_HANDLE )
&& ( NtStatus != STATUS_PORT_DISCONNECTED )
&& ( NtStatus != STATUS_LPC_REPLY_LOST) )
{
PrintToDebugger("RPC : AsyncSendReceive : %lx\n", NtStatus);
}
#endif // DBG
ASSERT( ( NtStatus == STATUS_INVALID_PORT_HANDLE )
|| ( NtStatus == STATUS_INVALID_HANDLE )
|| ( NtStatus == STATUS_PORT_DISCONNECTED )
|| ( NtStatus == STATUS_LPC_REPLY_LOST ) );
if ( (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.
FreeCCall();
return (RPC_S_CALL_FAILED_DNE) ;
}
FreeCCall();
// In a callback and/or couldn't retry.
return (RPC_S_CALL_FAILED);
}
if (Message->RpcFlags & RPCFLG_ASYNCHRONOUS)
{
FreeCCall() ;
return (RpcStatus) ;
}
// The message was sent and we got a reply okay.
if ( WMSGMessage->Fault.RpcHeader.MessageType == WMSG_MSG_FAULT )
{
RpcStatus = WMSGMessage->Fault.RpcStatus;
}
if ( WMSGMessage->Rpc.RpcHeader.MessageType == WMSG_MSG_RESPONSE )
{
RpcStatus = RPC_S_OK ;
}
if ( RpcStatus != RPC_S_OK)
{
FreeCCall();
}
return(RpcStatus);
}
RPC_STATUS
WMSG_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, RpcStatus;
void * OriginalMessageBuffer;
WMSG_MESSAGE *SavedWMSGMessage = 0;
WMSG_MESSAGE *TmpWMSGMessage = 0;
int ActiveCallSetupFlag = 0;
void * SavedBuffer;
if ( CallAbortedFlag != 0 )
{
return(RPC_S_CALL_FAILED_DNE);
}
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
if ( CallStack > 0 )
{
WMSGMessage->LpcHeader.u2.s2.Type = LPC_REQUEST;
WMSGMessage->LpcHeader.ClientId = ClientId;
WMSGMessage->LpcHeader.MessageId = MessageId;
WMSGMessage->LpcHeader.CallbackId = CallbackId;
}
WMSGMessage->LpcHeader.u1.s1.TotalLength = sizeof(PORT_MESSAGE)
+ WMSGMessage->LpcHeader.u1.s1.DataLength;
if (CurrentBindingHandle->BlockingFuncInitialized)
{
WMSGMessage->Rpc.RpcHeader.MessageType = WMSG_MSG_REQUEST;
}
else
{
WMSGMessage->Rpc.RpcHeader.MessageType = WMSG_LRPC_REQUEST;
}
WMSGMessage->Rpc.RpcHeader.ProcedureNumber = Message->ProcNum;
WMSGMessage->Rpc.RpcHeader.PresentationContext = PresentationContext;
WMSGMessage->Rpc.RpcHeader.Flags |= WMSG_SYNC_CLIENT ;
if ( ( CurrentBindingHandle->InqIfNullObjectUuid() == 0 )
&& ( CallStack == 0 ) )
{
CurrentBindingHandle->InquireObjectUuid(
(RPC_UUID *) &(WMSGMessage->Rpc.RpcHeader.ObjectUuid));
WMSGMessage->Rpc.RpcHeader.ObjectUuidFlag = 1;
}
else
{
WMSGMessage->Rpc.RpcHeader.ObjectUuidFlag = 0;
}
NtStatus = NtRequestWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) WMSGMessage, (PORT_MESSAGE *) WMSGMessage);
if ( NT_ERROR(NtStatus) )
{
if ( NtStatus == STATUS_NO_MEMORY )
{
ActuallyFreeBuffer(Message->Buffer);
AbortCCall();
return(RPC_S_OUT_OF_MEMORY);
}
if ( NtStatus == STATUS_INSUFFICIENT_RESOURCES )
{
ActuallyFreeBuffer(Message->Buffer);
AbortCCall();
return(RPC_S_OUT_OF_RESOURCES);
}
#if DBG
if ( ( NtStatus != STATUS_INVALID_PORT_HANDLE )
&& ( NtStatus != STATUS_INVALID_HANDLE )
&& ( NtStatus != STATUS_PORT_DISCONNECTED )
&& ( NtStatus != STATUS_LPC_REPLY_LOST) )
{
PrintToDebugger("RPC : NtRequestWaitReplyPort : %lx\n", NtStatus);
}
#endif // DBG
ASSERT( ( NtStatus == STATUS_INVALID_PORT_HANDLE )
|| ( NtStatus == STATUS_INVALID_HANDLE )
|| ( NtStatus == STATUS_PORT_DISCONNECTED )
|| ( NtStatus == STATUS_LPC_REPLY_LOST ) );
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.
SetRecursionCount(0);
return (RPC_S_CALL_FAILED_DNE);
}
SavedBuffer = Message->Buffer;
Message->Handle = (RPC_BINDING_HANDLE) CurrentBindingHandle;
RpcStatus = CurrentBindingHandle->GetBuffer(Message);
if (RpcStatus == RPC_S_OK)
{
ASSERT(Message->Buffer != SavedBuffer);
RpcpMemoryCopy(Message->Buffer, SavedBuffer,
Message->BufferLength);
// This CCALL is should be freed,
// a new one was allocated in GetBuffer and is now being used.
ASSERT(Message->Handle != this);
ActuallyFreeBuffer(SavedBuffer);
AbortCCall();
((WMSG_CCALL *)(Message->Handle))->SetRecursionCount(RecursionCount+1);
RpcStatus = ((WMSG_CCALL *)Message->Handle)->SendReceive(Message);
if (RpcStatus == RPC_S_OK)
{
((WMSG_CCALL *)(Message->Handle))->SetRecursionCount(0);
}
}
else
{
ActuallyFreeBuffer(SavedBuffer);
AbortCCall();
}
// If GetBuffer failed we'll now return an error and the
// stub will call FreeBuffer which will cleanup. Otherwise,
// we've already cleaned up this CCALL and the stubs
// will call FreeBuffer to cleanup the new CCALL.
if (RpcStatus == RPC_S_SERVER_UNAVAILABLE)
{
// Since we're retrying, if the server has gone missing,
// it just means that the call failed.
RpcStatus = RPC_S_CALL_FAILED_DNE;
}
return(RpcStatus);
}
ActuallyFreeBuffer(Message->Buffer);
AbortCCall();
// In a callback and/or couldn't retry.
return (RPC_S_CALL_FAILED);
}
// The message was sent and we got a reply okay.
ActuallyFreeBuffer(Message->Buffer);
for (;;)
{
if ( WMSGMessage->Rpc.RpcHeader.MessageType == WMSG_MSG_FAULT )
{
RpcStatus = WMSGMessage->Fault.RpcStatus;
break;
}
if ( WMSGMessage->Rpc.RpcHeader.MessageType == WMSG_MSG_RESPONSE )
{
RpcStatus = WMSGMessageToRpcMessage(WMSGMessage, Message,
Association->LpcClientPort,TRUE);
break;
}
ASSERT( WMSGMessage->Rpc.RpcHeader.MessageType == WMSG_MSG_CALLBACK );
CallStack += 1;
RpcStatus = RPC_S_OK;
if ( ( CallStack == 1 )
&& ( ActiveCallSetupFlag == 0 ) )
{
ClientId = WMSGMessage->LpcHeader.ClientId;
MessageId = WMSGMessage->LpcHeader.MessageId;
CallbackId = WMSGMessage->LpcHeader.CallbackId;
ActiveCallsKey = CurrentBindingHandle->AddActiveCall(this);
if ( ActiveCallsKey == -1 )
{
RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else
{
ActiveCallSetupFlag = 1;
}
}
if (SavedWMSGMessage == 0)
{
// First callback, we may need to allocated a new WMSG_MESSAGE.
if (CachedWMSGMessage == 0)
{
CachedWMSGMessage = MessageCache->AllocateMessage() ;
}
if (CachedWMSGMessage == 0)
RpcStatus = RPC_S_OUT_OF_MEMORY;
}
if ( RpcStatus == RPC_S_OK )
{
RpcStatus = WMSGMessageToRpcMessage(WMSGMessage, Message,
Association->LpcClientPort,TRUE);
}
if ( RpcStatus != RPC_S_OK )
{
ActuallyFreeBuffer(Message->Buffer);
WMSGMessage->Fault.RpcHeader.MessageType = WMSG_MSG_FAULT;
WMSGMessage->Fault.RpcStatus = WMSGMapRpcStatus(RpcStatus);
WMSGMessage->LpcHeader.u1.s1.DataLength = sizeof(WMSG_FAULT_MESSAGE)
- sizeof(PORT_MESSAGE);
WMSGMessage->LpcHeader.u1.s1.TotalLength =
sizeof(WMSG_FAULT_MESSAGE);
WMSGMessage->LpcHeader.ClientId = ClientId;
WMSGMessage->LpcHeader.MessageId = MessageId;
WMSGMessage->LpcHeader.CallbackId = CallbackId;
NtStatus = NtReplyWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) WMSGMessage);
}
else
{
OriginalMessageBuffer = Message->Buffer;
Message->TransferSyntax = 0;
Message->ProcNum = WMSGMessage->Rpc.RpcHeader.ProcedureNumber;
if (SavedWMSGMessage == 0)
{
// First callback
ASSERT(CachedWMSGMessage != 0);
SavedWMSGMessage = WMSGMessage;
WMSGMessage = CachedWMSGMessage;
CachedWMSGMessage = 0;
}
else
{
// >First callback, WMSGMessag and SavedWMSGMessages swap roles
TmpWMSGMessage = SavedWMSGMessage;
SavedWMSGMessage = WMSGMessage;
WMSGMessage = TmpWMSGMessage;
}
RpcStatus = DispatchCallback((PRPC_DISPATCH_TABLE)
RpcInterfaceInformation->DispatchTable, Message,
&ExceptionCode);
if (OriginalMessageBuffer != SavedWMSGMessage->Rpc.Buffer)
{
ActuallyFreeBuffer(OriginalMessageBuffer);
}
if ( RpcStatus != RPC_S_OK )
{
ASSERT( ( RpcStatus == RPC_P_EXCEPTION_OCCURED )
|| ( RpcStatus == RPC_S_PROCNUM_OUT_OF_RANGE ) );
if ( RpcStatus == RPC_P_EXCEPTION_OCCURED )
{
RpcStatus = WMSGMapRpcStatus(ExceptionCode);
}
WMSGMessage->Fault.RpcStatus = RpcStatus;
WMSGMessage->LpcHeader.u1.s1.DataLength =
sizeof(WMSG_FAULT_MESSAGE) - sizeof(PORT_MESSAGE);
WMSGMessage->LpcHeader.u1.s1.TotalLength =
sizeof(WMSG_FAULT_MESSAGE);
WMSGMessage->Fault.RpcHeader.MessageType = WMSG_MSG_FAULT;
}
else
{
WMSGMessage->Rpc.RpcHeader.MessageType = WMSG_MSG_RESPONSE;
if ( WMSGMessage->Rpc.RpcHeader.Flags & WMSG_BUFFER_REQUEST )
{
RpcStatus = MakeServerCopyResponse();
if ( RpcStatus != RPC_S_OK )
{
break;
}
}
}
WMSGMessage->LpcHeader.ClientId = ClientId;
WMSGMessage->LpcHeader.MessageId = MessageId;
WMSGMessage->LpcHeader.CallbackId = CallbackId;
NtStatus = NtReplyWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) WMSGMessage);
}
CallStack -= 1;
if ( NT_ERROR(NtStatus) )
{
if ( NtStatus == STATUS_NO_MEMORY )
{
RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else if ( NtStatus == STATUS_INSUFFICIENT_RESOURCES )
{
RpcStatus = RPC_S_OUT_OF_RESOURCES;
}
else
{
Association->AbortAssociation();
#if DBG
if ( ( NtStatus != STATUS_INVALID_PORT_HANDLE )
&& ( NtStatus != STATUS_INVALID_HANDLE )
&& ( NtStatus != STATUS_PORT_DISCONNECTED )
&& ( NtStatus != STATUS_LPC_REPLY_LOST) )
{
PrintToDebugger("RPC : NtRequestWaitReplyPort : %lx\n", NtStatus);
}
#endif // DBG
ASSERT( ( NtStatus == STATUS_INVALID_PORT_HANDLE )
|| ( NtStatus == STATUS_INVALID_HANDLE )
|| ( NtStatus == STATUS_PORT_DISCONNECTED )
|| ( NtStatus == STATUS_LPC_REPLY_LOST) );
RpcStatus = RPC_S_CALL_FAILED;
}
break;
}
}
if (SavedWMSGMessage != 0)
{
if (CachedWMSGMessage != 0)
{
MessageCache->FreeMessage(CachedWMSGMessage) ;
}
CachedWMSGMessage = SavedWMSGMessage;
}
if ( ActiveCallSetupFlag != 0 )
{
CurrentBindingHandle->RemoveActiveCall(ActiveCallsKey);
}
if ( RpcStatus != RPC_S_OK )
{
if ( CallStack == 0 )
{
FreeCCall();
}
}
return(RpcStatus);
}
RPC_STATUS
WMSG_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.
--*/
{
NTSTATUS NtStatus;
RPC_STATUS ExceptionCode, RpcStatus;
void * OriginalMessageBuffer;
WMSG_MESSAGE *TmpWMSGMessage = 0;
void * SavedBuffer;
int Partial = (Message->RpcFlags & RPC_BUFFER_PARTIAL) ;
WMSG_MESSAGE *WMSGReplyMessage ;
int RemainingLength = 0;
if (FirstFrag)
{
WMSGMessage->Rpc.RpcHeader.MessageType = WMSG_LRPC_REQUEST;
}
else
{
WMSGMessage->Rpc.RpcHeader.MessageType = WMSG_PARTIAL_REQUEST;
}
if (Partial)
{
if (Message->BufferLength < MINIMUM_PARTIAL_BUFFLEN)
{
return (RPC_S_SEND_INCOMPLETE);
}
WMSGMessage->Rpc.RpcHeader.Flags |= WMSG_BUFFER_PARTIAL ;
if (NOT_MULTIPLE_OF_EIGHT(Message->BufferLength))
{
RemainingLength = Message->BufferLength & LOW_BITS ;
Message->BufferLength &= ~LOW_BITS ;
}
}
if ( CallAbortedFlag != 0 )
{
return(RPC_S_CALL_FAILED_DNE);
}
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
if ( CallStack > 0 )
{
return (RPC_S_CALL_FAILED);
}
WMSGMessage->LpcHeader.u1.s1.TotalLength = sizeof(PORT_MESSAGE)
+ WMSGMessage->LpcHeader.u1.s1.DataLength;
WMSGMessage->Rpc.RpcHeader.ConnectionKey = SCallKey ;
WMSGMessage->Rpc.Request.DataEntries[0].Size = Message->BufferLength;
WMSGMessage->Rpc.RpcHeader.ProcedureNumber = Message->ProcNum;
WMSGMessage->Rpc.RpcHeader.PresentationContext = PresentationContext;
WMSGMessage->Rpc.RpcHeader.Flags |= WMSG_SYNC_CLIENT ;
if ( ( CurrentBindingHandle->InqIfNullObjectUuid() == 0 ) )
{
ASSERT(CallStack == 0) ;
CurrentBindingHandle->InquireObjectUuid(
(RPC_UUID *) &(WMSGMessage->Rpc.RpcHeader.ObjectUuid));
WMSGMessage->Rpc.RpcHeader.ObjectUuidFlag = 1;
}
else
{
WMSGMessage->Rpc.RpcHeader.ObjectUuidFlag = 0;
}
NtStatus = NtRequestWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) WMSGMessage, (PORT_MESSAGE *) WMSGMessage);
if ( NT_ERROR(NtStatus) )
{
if ( NtStatus == STATUS_NO_MEMORY )
{
return(RPC_S_OUT_OF_MEMORY);
}
if ( NtStatus == STATUS_INSUFFICIENT_RESOURCES )
{
return(RPC_S_OUT_OF_RESOURCES);
}
#if DBG
if ( ( NtStatus != STATUS_INVALID_PORT_HANDLE )
&& ( NtStatus != STATUS_INVALID_HANDLE )
&& ( NtStatus != STATUS_PORT_DISCONNECTED )
&& ( NtStatus != STATUS_LPC_REPLY_LOST) )
{
PrintToDebugger("RPC : NtRequestWaitReplyPort : %lx\n", NtStatus);
}
#endif // DBG
ASSERT( ( NtStatus == STATUS_INVALID_PORT_HANDLE )
|| ( NtStatus == STATUS_INVALID_HANDLE )
|| ( NtStatus == STATUS_PORT_DISCONNECTED )
|| ( NtStatus == STATUS_LPC_REPLY_LOST ) );
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.
SetRecursionCount(0);
return (RPC_S_CALL_FAILED_DNE);
}
SavedBuffer = Message->Buffer;
Message->Handle = (RPC_BINDING_HANDLE) CurrentBindingHandle;
RpcStatus = CurrentBindingHandle->GetBuffer(Message);
if (RpcStatus == RPC_S_OK)
{
ASSERT(Message->Buffer != SavedBuffer);
RpcpMemoryCopy(Message->Buffer, SavedBuffer,
Message->BufferLength);
// This CCALL is should be freed,
// a new one was allocated in GetBuffer and is now being used.
ASSERT(Message->Handle != this);
ActuallyFreeBuffer(SavedBuffer);
AbortCCall();
((WMSG_CCALL *)(Message->Handle))->SetRecursionCount(RecursionCount+1);
RpcStatus = ((WMSG_CCALL *)Message->Handle)->Send(Message);
if (RpcStatus == RPC_S_OK)
{
((WMSG_CCALL *)(Message->Handle))->SetRecursionCount(0);
}
}
// If GetBuffer failed we'll now return an error and the
// stub will call FreeBuffer which will cleanup. Otherwise,
// we've already cleaned up this CCALL and the stubs
// will call FreeBuffer to cleanup the new CCALL.
if (RpcStatus == RPC_S_SERVER_UNAVAILABLE)
{
// Since we're retrying, if the server has gone missing,
// it just means that the call failed.
RpcStatus = RPC_S_CALL_FAILED_DNE;
}
return(RpcStatus);
}
// In a callback and/or couldn't retry.
return (RPC_S_CALL_FAILED);
}
else
{
FirstFrag = 0;
}
if (WMSGMessage->Rpc.RpcHeader.MessageType == WMSG_MSG_ACK)
{
SCallKey = WMSGMessage->Ack.ConnectionKey ;
if (WMSGMessage->Ack.RpcStatus == RPC_S_OK &&
RemainingLength)
{
RpcpMemoryMove( Message->Buffer,
(char PAPI *) Message->Buffer + Message->BufferLength,
RemainingLength) ;
Message->BufferLength = RemainingLength ;
return (RPC_S_SEND_INCOMPLETE) ;
}
return WMSGMessage->Ack.RpcStatus ;
}
if ( WMSGMessage->Rpc.RpcHeader.MessageType == WMSG_MSG_RESPONSE)
{
ASSERT(Partial == 0) ;
ActuallyFreeBuffer(Message->Buffer);
CurrentBufferLength = 0;
SCallKey = WMSGMessage->Rpc.RpcHeader.ConnectionKey ;
RpcStatus = WMSGMessageToRpcMessage(WMSGMessage,
Message, Association->LpcClientPort, TRUE);
// we have no out pipes
ASSERT(Message->RpcFlags & RPC_BUFFER_COMPLETE) ;
}
else if ( WMSGMessage->Rpc.RpcHeader.MessageType == WMSG_MSG_FAULT )
{
ActuallyFreeBuffer(Message->Buffer);
CurrentBufferLength = 0;
RpcStatus = WMSGMessage->Fault.RpcStatus;
}
if ( RpcStatus != RPC_S_OK )
{
ASSERT(CallStack == 0) ;
FreeCCall();
}
return(RpcStatus);
}
RPC_STATUS
WMSG_CCALL::Receive (
IN PRPC_MESSAGE Message,
IN unsigned int Size
)
{
RPC_STATUS RpcStatus ;
NTSTATUS NtStatus ;
int size = 0 ;
unsigned long Partial = Message->RpcFlags & RPC_BUFFER_PARTIAL ;
unsigned long Extra = Message->RpcFlags & RPC_BUFFER_EXTRA ;
int RequestedLength ;
int ActualBufferLength = 0;
int BufferLength ;
int BufferValid = 0;
if (BufferComplete)
{
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)
{
if (Extra)
{
ActualBufferLength = Message->BufferLength ;
BufferLength = Message->BufferLength+Size ;
BufferValid = 1;
}
else
{
BufferLength = Size ;
}
}
else
{
if (Extra)
{
ActualBufferLength = Message->BufferLength ;
BufferLength = Message->BufferLength + MINIMUM_PARTIAL_BUFFLEN ;
BufferValid = 1;
}
else
{
BufferLength = MINIMUM_PARTIAL_BUFFLEN ;
}
}
RpcStatus = GetBufferDo(Message, BufferLength, BufferValid) ;
if (RpcStatus != RPC_S_OK)
{
FreeCCall();
return RpcStatus ;
}
RequestedLength = Message->BufferLength - ActualBufferLength;
while (1)
{
// by the time we reach here. the SCallKey should be valid.
ASSERT(SCallKey > 0) ;
WMSGMessage->Rpc.RpcHeader.ConnectionKey = SCallKey ;
WMSGMessage->Rpc.RpcHeader.MessageType = WMSG_PARTIAL_OUT ;
WMSGMessage->Partial.Request.CountDataEntries = 1;
WMSGMessage->Partial.Request.DataEntries[0].Base =
(char *) Message->Buffer + ActualBufferLength;
WMSGMessage->Partial.Request.DataEntries[0].Size = RequestedLength ;
WMSGMessage->LpcHeader.CallbackId = 0;
WMSGMessage->LpcHeader.u2.ZeroInit = 0;
WMSGMessage->LpcHeader.u2.s2.DataInfoOffset = sizeof(PORT_MESSAGE)
+ sizeof(WMSG_RPC_HEADER);
WMSGMessage->LpcHeader.u1.s1.DataLength =
sizeof(WMSG_PARTIAL_MESSAGE) - sizeof(PORT_MESSAGE);
WMSGMessage->LpcHeader.u1.s1.TotalLength =
sizeof(WMSG_PARTIAL_MESSAGE);
NtStatus = NtRequestWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) WMSGMessage,
(PORT_MESSAGE *) WMSGMessage) ;
if ( NT_ERROR(NtStatus) )
{
FreeCCall();
if ( NtStatus == STATUS_NO_MEMORY )
{
return(RPC_S_OUT_OF_MEMORY);
}
if ( NtStatus == STATUS_INSUFFICIENT_RESOURCES )
{
return(RPC_S_OUT_OF_RESOURCES);
}
#if DBG
if ( ( NtStatus != STATUS_INVALID_PORT_HANDLE )
&& ( NtStatus != STATUS_INVALID_HANDLE )
&& ( NtStatus != STATUS_INVALID_CID )
&& ( NtStatus != STATUS_PORT_DISCONNECTED )
&& (NtStatus != STATUS_LPC_REPLY_LOST ) )
{
PrintToDebugger("RPC : NtRequestWaitReplyPort : %lx\n", NtStatus);
}
#endif // DBG
ASSERT( ( NtStatus == STATUS_INVALID_PORT_HANDLE )
|| ( NtStatus == STATUS_INVALID_HANDLE )
|| ( NtStatus == STATUS_INVALID_CID )
|| ( NtStatus == STATUS_PORT_DISCONNECTED )
|| ( NtStatus == STATUS_LPC_REPLY_LOST) );
return(RPC_S_CALL_FAILED);
}
if (WMSGMessage->Rpc.RpcHeader.MessageType == WMSG_MSG_ACK)
{
ASSERT(WMSGMessage->Ack.ValidDataSize <= RequestedLength) ;
ActualBufferLength += WMSGMessage->Ack.ValidDataSize ;
RequestedLength -= WMSGMessage->Ack.ValidDataSize ;
RpcStatus = WMSGMessage->Ack.RpcStatus ;
if (RpcStatus != RPC_S_OK)
{
FreeCCall();
return RpcStatus ;
}
if (WMSGMessage->Ack.Flags & ACK_BUFFER_COMPLETE)
{
Message->BufferLength = ActualBufferLength ;
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
BufferComplete = 1;
}
else
{
if (Partial == 0)
{
// BUGBUG: Ineffecient
RpcStatus = GetBufferDo(Message,
ActualBufferLength + MINIMUM_PARTIAL_BUFFLEN , 1) ;
if (RpcStatus != RPC_S_OK)
{
FreeCCall();
return RpcStatus ;
}
RequestedLength += MINIMUM_PARTIAL_BUFFLEN ;
}
}
}
else
{
ASSERT(WMSGMessage->Rpc.RpcHeader.MessageType ==
WMSG_MSG_RESPONSE) ;
Message->BufferLength = ActualBufferLength ;
RpcStatus = WMSGMessageToRpcMessage(WMSGMessage, Message,
Association->LpcClientPort, TRUE, Extra, TRUE) ;
if (RpcStatus != RPC_S_OK)
{
FreeCCall();
return RpcStatus ;
}
ASSERT(Message->RpcFlags & RPC_BUFFER_COMPLETE) ;
}
if (Message->RpcFlags & RPC_BUFFER_COMPLETE)
{
break;
}
if (Partial && ActualBufferLength >= Size)
{
Message->BufferLength = ActualBufferLength ;
break;
}
Extra = 1;
}
ASSERT(Message->RpcFlags & RPC_BUFFER_COMPLETE
|| (NOT_MULTIPLE_OF_EIGHT(Message->BufferLength) == 0)) ;
return RPC_S_OK ;
}
void
WMSG_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
WMSG_CCALL::FreePipeBuffer (
IN PRPC_MESSAGE Message
)
{
RpcpFarFree(Message->Buffer) ;
}
RPC_STATUS
WMSG_CCALL::GetBufferDo (
IN OUT PRPC_MESSAGE Message,
IN int NewSize,
IN int fDataValid
)
{
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
WMSG_CCALL::ReallocPipeBuffer (
IN PRPC_MESSAGE Message,
IN unsigned int NewSize
)
{
unsigned int SizeToAlloc ;
void *TempBuffer ;
if (WMSGMessage == 0)
{
WMSGMessage = MessageCache->AllocateMessage();
if (WMSGMessage == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
}
if (GetBufferDo(Message, NewSize, 1) != RPC_S_OK)
return RPC_S_OUT_OF_MEMORY ;
Message->BufferLength = NewSize ;
WMSGMessage->Rpc.RpcHeader.Flags = WMSG_BUFFER_REQUEST;
WMSGMessage->Rpc.Request.CountDataEntries = 1;
WMSGMessage->Rpc.Request.DataEntries[0].Base = Message->Buffer;
WMSGMessage->Rpc.Request.DataEntries[0].Size = Message->BufferLength;
WMSGMessage->LpcHeader.CallbackId = 0;
WMSGMessage->LpcHeader.u2.ZeroInit = 0;
WMSGMessage->LpcHeader.u2.s2.DataInfoOffset = sizeof(PORT_MESSAGE)
+ sizeof(WMSG_RPC_HEADER);
WMSGMessage->LpcHeader.u1.s1.DataLength = sizeof(WMSG_RPC_HEADER)
+ sizeof(PORT_DATA_INFORMATION);
return (RPC_S_OK) ;
}
void
WMSG_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.
--*/
{
WMSG_BINDING_HANDLE * BindingHandle;
CallAbortedFlag = 1;
if (CallStack == 0)
{
ASSERT( CurrentBindingHandle != 0 );
BindingHandle = CurrentBindingHandle;
CurrentBindingHandle = 0;
BindingHandle->FreeCCall(this);
}
}
inline RPC_STATUS
WMSG_CCALL::WMSGMessageToRpcMessage (
IN WMSG_MESSAGE *WMSGMsg,
OUT RPC_MESSAGE * RpcMessage,
IN HANDLE LpcPort,
IN BOOL IsClientFT,
IN unsigned long Extra,
IN int BufferValid
)
/*++
Routine Description:
We will convert from an WMSG_MESSAGE representation of a buffer (and
its length) to an RPC_MESSAGE representation.
Arguments:
RpcMessage - Returns the RPC_MESSAGE representation.
--*/
{
NTSTATUS NtStatus;
unsigned long NumberOfBytesRead;
WMSG_MESSAGE ReplyMessage ;
unsigned char MessageType ;
WMSG_COPY_MESSAGE CopyMessage;
int IsPartial = 0;
int TotalLength ;
char *RecvBuffer;
int Offset = 0;
if(WMSGMsg->Rpc.RpcHeader.Flags & WMSG_BUFFER_IMMEDIATE)
{
RpcMessage->Buffer = WMSGMsg->Rpc.Buffer;
ASSERT(WMSGMsg->LpcHeader.u1.s1.DataLength >= sizeof(WMSG_RPC_HEADER));
RpcMessage->BufferLength =
(unsigned int) WMSGMsg->LpcHeader.u1.s1.DataLength
- sizeof(WMSG_RPC_HEADER);
RpcMessage->RpcFlags |= RPC_BUFFER_COMPLETE ;
BufferComplete = 1;
}
else if (WMSGMsg->Rpc.RpcHeader.Flags & WMSG_BUFFER_SERVER)
{
if (IsClientFT)
{
TotalLength = WMSGMessage->Rpc.Server.Length ;
RecvBuffer = 0;
RpcMessage->RpcFlags |= RPC_BUFFER_COMPLETE ;
BufferComplete = 1;
if (BufferValid == 0)
{
RpcMessage->Buffer = RpcpFarAllocate(TotalLength) ;
RecvBuffer = (char *) RpcMessage->Buffer ;
RpcMessage->BufferLength = TotalLength ;
}
else
{
if (Extra)
{
int OldLength = RpcMessage->BufferLength ;
if (GetBufferDo(RpcMessage,
TotalLength+RpcMessage->BufferLength, 1) == RPC_S_OK)
{
RecvBuffer = (char *) RpcMessage->Buffer + OldLength;
}
}
else
{
if (GetBufferDo(RpcMessage, TotalLength, 0) == RPC_S_OK)
{
RecvBuffer = (char *) RpcMessage->Buffer ;
}
}
}
CopyMessage.LpcHeader.u2.ZeroInit = 0;
if ( RecvBuffer == 0 )
{
CopyMessage.RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else
{
CopyMessage.RpcStatus = RPC_S_OK;
CopyMessage.Server.Buffer = WMSGMsg->Rpc.Server.Buffer;
CopyMessage.Server.Length = WMSGMsg->Rpc.Server.Length;
CopyMessage.Request.CountDataEntries = 1;
CopyMessage.Request.DataEntries[0].Base = RecvBuffer;
CopyMessage.Request.DataEntries[0].Size = TotalLength ;
CopyMessage.LpcHeader.u2.s2.DataInfoOffset =
sizeof(PORT_MESSAGE) + sizeof(WMSG_RPC_HEADER);
}
CopyMessage.LpcHeader.CallbackId = 0;
CopyMessage.RpcHeader.Flags = WMSG_SYNC_CLIENT ;
CopyMessage.LpcHeader.u1.s1.DataLength = sizeof(WMSG_COPY_MESSAGE)
- sizeof(PORT_MESSAGE);
CopyMessage.LpcHeader.u1.s1.TotalLength = sizeof(WMSG_COPY_MESSAGE);
CopyMessage.RpcHeader.MessageType = WMSG_MSG_COPY;
CopyMessage.IsPartial = 0 ;
NtStatus = NtRequestWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) &CopyMessage,
(PORT_MESSAGE *) &CopyMessage);
if ( ( NT_ERROR(NtStatus) )
|| ( CopyMessage.RpcStatus != RPC_S_OK ) )
{
RpcpFarFree(RpcMessage->Buffer);
return(RPC_S_OUT_OF_MEMORY);
}
}
else
{
RpcMessage->BufferLength = (unsigned int)
WMSGMsg->Rpc.Request.DataEntries[0].Size ;
RpcMessage->Buffer = RpcpFarAllocate(
RpcMessage->BufferLength);
NtStatus = NtReadRequestData(LpcPort, (PORT_MESSAGE*) WMSGMsg,
0, RpcMessage->Buffer, RpcMessage->BufferLength,
&NumberOfBytesRead) ;
if ( NT_ERROR(NtStatus) )
{
#if DBG
PrintToDebugger("WMSG: NtReadRequestData failed\n") ;
#endif
RpcpFarFree(RpcMessage->Buffer);
return(RPC_S_OUT_OF_MEMORY);
}
ASSERT( RpcMessage->BufferLength == NumberOfBytesRead );
MessageType = WMSGMsg->Rpc.RpcHeader.MessageType ;
WMSGMsg->Ack.MessageType = WMSG_MSG_ACK ;
WMSGMsg->LpcHeader.u1.s1.DataLength = sizeof(WMSG_ACK_MESSAGE)
- sizeof(PORT_MESSAGE) ;
WMSGMsg->LpcHeader.u1.s1.TotalLength =
sizeof(WMSG_ACK_MESSAGE) ;
// setup the reply message
NtStatus = NtReplyPort(LpcPort,
(PORT_MESSAGE *) WMSGMsg) ;
WMSGMsg->Rpc.RpcHeader.MessageType = MessageType ;
if ( NT_ERROR(NtStatus) )
{
#if DBG
PrintToDebugger("WMSG: NtReplyPort failed\n") ;
#endif
RpcpFarFree(RpcMessage->Buffer);
return(RPC_S_OUT_OF_MEMORY);
}
}
}
else
{
ASSERT( ( WMSGMsg->Rpc.RpcHeader.Flags
& WMSG_BUFFER_IMMEDIATE )
|| ( WMSGMsg->Rpc.RpcHeader.Flags
& WMSG_BUFFER_SERVER ) );
}
return(RPC_S_OK);
}
void
WMSG_CCALL::FreeCCall (
)
/*++
Routine Description:
We are done with this client call. We need to notify the binding
handle we are done.
--*/
{
WMSG_BINDING_HANDLE * BindingHandle;
ASSERT( CurrentBindingHandle != 0 );
BindingHandle = CurrentBindingHandle;
CurrentBindingHandle = 0;
BindingHandle->FreeCCall(this);
}
void
WMSG_CCALL::ActuallyFreeBuffer (
IN void * Buffer
)
/*++
Routine Description:
Actually free a message buffer.
Arguments:
Buffer - Supplies the message buffer to be freed.
--*/
{
if (IsAsync)
{
if ( !( WMSGMessage->Rpc.RpcHeader.Flags & WMSG_BUFFER_IMMEDIATE ) )
{
RpcpFarFree(Buffer);
}
}
else
{
if ((Buffer != WMSGMessage->Rpc.Buffer)
&& ((CachedWMSGMessage == 0) ||
(Buffer != CachedWMSGMessage->Rpc.Buffer)))
{
RpcpFarFree(Buffer);
}
}
}
RPC_STATUS
WMSG_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.
--*/
{
WMSG_PUSH_MESSAGE WMSGPushMessage;
NTSTATUS NtStatus;
WMSGPushMessage.LpcHeader.u1.s1.TotalLength = sizeof(WMSG_PUSH_MESSAGE);
WMSGPushMessage.LpcHeader.u1.s1.DataLength = sizeof(WMSG_PUSH_MESSAGE)
- sizeof(PORT_MESSAGE);
WMSGPushMessage.LpcHeader.ClientId = ClientId;
WMSGPushMessage.LpcHeader.MessageId = MessageId;
WMSGPushMessage.LpcHeader.CallbackId = CallbackId ;
WMSGPushMessage.LpcHeader.u2.s2.Type = LPC_REQUEST;
WMSGPushMessage.RpcHeader.MessageType = WMSG_MSG_PUSH;
WMSGPushMessage.Response.CountDataEntries = 1;
WMSGPushMessage.Response.DataEntries[0] =
WMSGMessage->Rpc.Request.DataEntries[0];
WMSGPushMessage.LpcHeader.u2.s2.DataInfoOffset = sizeof(PORT_MESSAGE)
+ sizeof(WMSG_RPC_HEADER);
NtStatus = NtRequestWaitReplyPort(Association->LpcClientPort,
(PORT_MESSAGE *) &WMSGPushMessage,
(PORT_MESSAGE *) &WMSGPushMessage);
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);
}
#endif // DBG
ASSERT( ( NtStatus != STATUS_NO_MEMORY )
&& ( NtStatus != STATUS_INSUFFICIENT_RESOURCES ) );
return(RPC_S_OK);
}
ASSERT( ( WMSGPushMessage.RpcStatus == RPC_S_OK )
|| ( WMSGPushMessage.RpcStatus == RPC_S_OUT_OF_MEMORY ) );
return(WMSGPushMessage.RpcStatus);
}
BINDING_HANDLE *
WmsgCreateBindingHandle (
)
/*++
Routine Description:
We just need to create a new WMSG_BINDING_HANDLE. This routine is a
proxy for the new constructor to isolate the other modules.
--*/
{
WMSG_BINDING_HANDLE * BindingHandle;
RPC_STATUS RpcStatus = RPC_S_OK;
RpcStatus = InitializeWMsgIfNeccassary(0) ;
if (RpcStatus != RPC_S_OK)
{
return 0 ;
}
BindingHandle = new WMSG_BINDING_HANDLE(&RpcStatus);
if ( RpcStatus != RPC_S_OK )
{
delete BindingHandle;
return(0);
}
return(BindingHandle);
}
int
InitializeRpcProtocolWmsg (
)
/*++
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.
--*/
{
WMSGAssociationDict = new WMSG_CASSOCIATION_DICT;
if ( WMSGAssociationDict == 0 )
{
return(1);
}
return(0);
}
RPC_STATUS
WMSGMapRpcStatus (
IN RPC_STATUS RpcStatus
)
/*++
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 (RpcStatus)
{
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(RpcStatus);
}
RPC_STATUS
I_RpcParseSecurity (
IN RPC_CHAR * NetworkOptions,
OUT SECURITY_QUALITY_OF_SERVICE * SecurityQualityOfService
)
/*++
Routine Description:
Parse a string of security options and build into the binary format
required by the operating system. The network options must follow
the following syntax. Case is not sensitive.
security=
[anonymous|identification|impersonation|delegation]
[dynamic|static]
[true|false]
All three fields must be present. To specify impersonation
with dynamic tracking and effective only, use the following
string for the network options.
"security=impersonation dynamic true"
Arguments:
NetworkOptions - Supplies the string containing the network options
to be parsed.
SecurityQualityOfService - Returns the binary format of the network
options.
Return Value:
RPC_S_OK - The network options have been correctly parsed into binary
format.
RPC_S_INVALID_NETWORK_OPTIONS - The network options are invalid and
cannot be parsed.
--*/
{
ASSERT( NetworkOptions[0] != 0 );
// We need to parse the security information from the network
// options, and then stuff it into the object attributes. To
// begin with, we check for "security=" at the beginning of
// the network options.
if ( _wcsnicmp(NetworkOptions, RPC_CONST_STRING("security="),
sizeof("security=") - 1) != 0 )
{
return(RPC_S_INVALID_NETWORK_OPTIONS);
}
NetworkOptions += sizeof("security=") - 1;
// Ok, now we need to determine if the next field is one of
// Anonymous, Identification, Impersonation, or Delegation.
if ( _wcsnicmp(NetworkOptions, RPC_CONST_STRING("anonymous"),
sizeof("anonymous") - 1) == 0 )
{
SecurityQualityOfService->ImpersonationLevel = SecurityAnonymous;
NetworkOptions += sizeof("anonymous") - 1;
}
else if ( _wcsnicmp(NetworkOptions, RPC_CONST_STRING("identification"),
sizeof("identification") - 1) == 0 )
{
SecurityQualityOfService->ImpersonationLevel = SecurityIdentification;
NetworkOptions += sizeof("identification") - 1;
}
else if ( _wcsnicmp(NetworkOptions, RPC_CONST_STRING("impersonation"),
sizeof("impersonation") - 1) == 0 )
{
SecurityQualityOfService->ImpersonationLevel = SecurityImpersonation;
NetworkOptions += sizeof("impersonation") - 1;
}
else if ( _wcsnicmp(NetworkOptions, RPC_CONST_STRING("delegation"),
sizeof("delegation") - 1) == 0 )
{
SecurityQualityOfService->ImpersonationLevel = SecurityDelegation;
NetworkOptions += sizeof("delegation") - 1;
}
else
{
return(RPC_S_INVALID_NETWORK_OPTIONS);
}
if ( *NetworkOptions != RPC_CONST_CHAR(' ') )
{
return(RPC_S_INVALID_NETWORK_OPTIONS);
}
NetworkOptions++;
// Next comes the context tracking field; it must be one of
// dynamic or static.
if ( _wcsnicmp(NetworkOptions, RPC_CONST_STRING("dynamic"),
sizeof("dynamic") - 1) == 0 )
{
SecurityQualityOfService->ContextTrackingMode =
SECURITY_DYNAMIC_TRACKING;
NetworkOptions += sizeof("dynamic") - 1;
}
else if ( _wcsnicmp(NetworkOptions, RPC_CONST_STRING("static"),
sizeof("static") - 1) == 0 )
{
SecurityQualityOfService->ContextTrackingMode =
SECURITY_STATIC_TRACKING;
NetworkOptions += sizeof("static") - 1;
}
else
{
return(RPC_S_INVALID_NETWORK_OPTIONS);
}
if ( *NetworkOptions != RPC_CONST_CHAR(' ') )
{
return(RPC_S_INVALID_NETWORK_OPTIONS);
}
NetworkOptions++;
// Finally, comes the effective only flag. This must be one of
// true or false.
if ( _wcsnicmp(NetworkOptions, RPC_CONST_STRING("true"),
sizeof("true") - 1) == 0 )
{
SecurityQualityOfService->EffectiveOnly = TRUE;
NetworkOptions += sizeof("true") - 1;
}
else if ( _wcsnicmp(NetworkOptions, RPC_CONST_STRING("false"),
sizeof("false") - 1) == 0 )
{
SecurityQualityOfService->EffectiveOnly = FALSE;
NetworkOptions += sizeof("false") - 1;
}
else
{
return(RPC_S_INVALID_NETWORK_OPTIONS);
}
if ( *NetworkOptions != 0 )
{
return(RPC_S_INVALID_NETWORK_OPTIONS);
}
SecurityQualityOfService->Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
return(RPC_S_OK);
}