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.
 
 
 
 
 
 

1768 lines
39 KiB

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
spcclnt.cxx
Abstract:
Author:
Revision History:
--*/
#include <rpc.h>
#include <sysinc.h>
#include <rpcerrp.h>
#include <rpcdcep.h>
#include <rpcqos.h>
#include <util.hxx>
#include <sdict.hxx>
#include <rpcuuid.hxx>
#include <binding.hxx>
#include <handle.hxx>
#include <critsec.hxx>
#include <wmsgheap.hxx>
#include <wmsgthrd.hxx>
#include <wmsgpack.hxx>
#include <wmsgport.hxx>
#include <wmsgproc.hxx>
#include <wmsgsys.hxx>
#include <wmsgpack.hxx>
#include <wmsgclnt.hxx>
#include <epmap.h>
#define TIMER_INTERVAL 1000
#define TIMER_MAX_TIMEOUTS 3
#define WM_DDE_FIRST 0x03E0
#define WM_DDE_LAST (WM_DDE_FIRST+8)
// Maximum time we will wait for a response on message wait for multiple.
// Note: this must be less than infinite because MsgWaitForMultipleObjects
// will not wake up on messages posted before it is called.
#define MAX_TICKS_TO_WAIT 1000
static WMSG_ASSOC_GROUP_DICT * AssocGroups = NULL;
VOID
WmsgClientThreadCleanup(
THREAD_IDENTIFIER ThreadId
)
{
WMSG_ASSOC_GROUP * AssocGroup;
WMSG_CASSOCIATION * Association;
GlobalMutexRequest();
if (AssocGroups != NULL) {
AssocGroups->Reset();
while ( (AssocGroup = AssocGroups->Next() ) != 0 ) {
AssocGroup->CritSec.Enter();
AssocGroup->Associations.Reset();
while ( (Association = AssocGroup->Associations.Next()) != 0) {
if (Association->ThreadId == ThreadId) {
AssocGroup->Associations.Delete(Association->AssociationDictKey);
delete Association;
}
}
AssocGroup->CritSec.Leave();
}
}
GlobalMutexClear();
}
LRESULT
WmsgClientAsyncProc(
UINT MsgType,
LPARAM lParam,
void * Context
)
{
WMSG_CASSOCIATION * Association = (WMSG_CASSOCIATION *) Context;
ASSERT(Association != NULL);
return Association->AsyncProc(MsgType, lParam);
}
RPC_STATUS __RPC_USER
DefaultBlockingHook(
IN void __RPC_FAR *RpcWindowHandle,
IN void __RPC_FAR *Context
)
{
MSG msg;
RPC_STATUS ret;
#ifdef DEBUGRPC_DETAIL
PrintToDebugger("RPCRT4: DefaultBlockHook NEW X1 on window %x PID=%x TID=%x,\n",
(HWND)RpcWindowHandle, GetCurrentProcessId(), GetCurrentThreadId());
#endif
if (!PeekMessage(&msg, (HWND)RpcWindowHandle, 0, 0, PM_REMOVE))
{
MsgWaitForMultipleObjects(0, 0, FALSE, TIMER_INTERVAL, QS_POSTMESSAGE | QS_SENDMESSAGE);
if ( !PeekMessage(&msg, (HWND)RpcWindowHandle, 0, 0, PM_NOYIELD | PM_REMOVE)
&& !PeekMessage(&msg, NULL, WM_DDE_FIRST, WM_DDE_LAST, PM_NOYIELD | PM_REMOVE))
{
return RPC_S_CALL_IN_PROGRESS;
}
}
if (msg.message == WM_QUIT)
{
return(RPC_S_CALL_FAILED);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
#ifdef DEBUGRPC_DETAIL
PrintToDebugger("RPCRT4: DefaultBlockHook Dispatched msg on window %x, %d\n",
(HWND)RpcWindowHandle, msg.message);
#endif
return RPC_S_OK;
}
WMSG_BINDING_HANDLE::WMSG_BINDING_HANDLE (
)
/*++
Routine Description:
We just allocate an WMSG_BINDING_HANDLE and initialize things so that
we can use it later.
Arguments:
--*/
{
DceBinding = NULL;
AssocGroup = NULL;
BlockingHook = DefaultBlockingHook;
}
WMSG_BINDING_HANDLE::~WMSG_BINDING_HANDLE (
)
/*++
--*/
{
if (DceBinding != NULL) {
delete DceBinding;
DceBinding = 0;
}
if (AssocGroup != NULL) {
AssocGroup->Dereference();
AssocGroup = 0;
}
}
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:
--*/
{
RPC_STATUS RpcStatus;
WMSG_CASSOCIATION * Association = NULL;
// Attach binding handle to association group, if not already attached.
if (AssocGroup == NULL) {
RpcStatus = ResolveBinding((RPC_CLIENT_INTERFACE *)
Message->RpcInterfaceInformation);
if (RpcStatus != RPC_S_OK) {
return (RpcStatus);
}
}
ASSERT(AssocGroup != NULL);
RpcStatus = AllocateAssociation(&Association,
(RPC_CLIENT_INTERFACE *)
Message->RpcInterfaceInformation);
if ( RpcStatus != RPC_S_OK) {
return (RpcStatus);
}
ASSERT(Association != NULL);
ASSERT(Association->CurrentBindingHandle == this);
// Get a real buffer.
RpcStatus = Association->GetBuffer(Message);
if ( RpcStatus != RPC_S_OK ) {
Association->AbortAssociation();
ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY);
return(RpcStatus);
}
ASSERT(Message->Buffer != NULL);
return(RPC_S_OK);
}
RPC_STATUS
WMSG_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.
--*/
{
WMSG_BINDING_HANDLE * NewBindingHandle = NULL;
UNUSED(MaintainContext);
// Create a brand new binding handle.
NewBindingHandle = new WMSG_BINDING_HANDLE();
if ( NewBindingHandle == NULL ) {
return(RPC_S_OUT_OF_MEMORY);
}
CritSec.Enter();
// Copy DceBinding out of existing binding handle.
NewBindingHandle->DceBinding = DceBinding->DuplicateDceBinding();
// If existing handle attached to association group, attach new handle to
// same group.
if (AssocGroup != NULL) {
NewBindingHandle->AssocGroup = AssocGroup;
AssocGroup->AddRef();
}
NewBindingHandle->BlockingHook = BlockingHook;
CritSec.Leave();
*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.
--*/
{
delete this;
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);
ASSERT(DceBinding != NULL);
this->DceBinding = DceBinding;
return;
}
RPC_STATUS
WMSG_BINDING_HANDLE::ToStringBinding (
OUT RPC_CHAR * * StringBinding
)
/*++
Routine Description:
We need to convert the binding handle into a string binding. If the
handle is unbound, use the DceBinding directly, otherwise, get it from
the association.
Arguments:
StringBinding - Returns the string representation of the binding
handle.
Return Value:
RPC_S_OK - The binding handle has successfully been converted into a
string binding.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate the
string.
--*/
{
ASSERT(DceBinding != NULL);
*StringBinding = DceBinding->StringBindingCompose(
InqPointerAtObjectUuid());
if ( *StringBinding == NULL ) {
return(RPC_S_OUT_OF_MEMORY);
}
return(RPC_S_OK);
}
RPC_STATUS
WMSG_BINDING_HANDLE::ResolveBinding (
IN RPC_CLIENT_INTERFACE * Interface
)
/*++
Routine Description:
We need to try and resolve the endpoint for this binding handle
if necessary (the binding handle is partially-bound). If there is
isn't a association allocated, call the binding management routines
to do it.
Arguments:
Interface - Supplies interface information to be used
in resolving the endpoint.
Return Value:
RPC_S_OK - This binding handle is a full resolved binding handle.
RPC_S_NO_ENDPOINT_FOUND - The endpoint can not be resolved.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to resolve
the endpoint.
EPT_S_NOT_REGISTERED - There are no more endpoints to be found
for the specified combination of interface, network address,
and lookup handle.
EPT_S_CANT_PERFORM_OP - The operation failed due to misc. error e.g.
unable to bind to the EpMapper.
--*/
{
RPC_STATUS RpcStatus;
ASSERT(DceBinding != NULL);
// If Endpoint not specified in DceBinding, get it from the Interface
// specification (if possible), otherwise, use the endpoint mapper.
RpcStatus = DceBinding->ResolveEndpointIfNecessary(Interface,
InqPointerAtObjectUuid(),
InquireEpLookupHandle(),
FALSE,
InqComTimeout()
);
if (RpcStatus != RPC_S_OK) {
return (RpcStatus);
}
// If binding handle was attached to association group, dereference
// it (AssocGroup), since it is probably different now.
if (AssocGroup != NULL) {
AssocGroup->Dereference();
AssocGroup = NULL;
}
// Look for new DceBinding in all assocication groups.
GlobalMutexRequest();
ASSERT(AssocGroups != NULL);
AssocGroups->Reset();
while ( (AssocGroup = AssocGroups->Next() ) != 0 ) {
if ( AssocGroup->DceBinding->Compare(DceBinding) == 0) {
AssocGroup->AddRef();
GlobalMutexClear();
return(RPC_S_OK);
}
}
GlobalMutexClear();
// Association group not found, create a new one.
AssocGroup = new WMSG_ASSOC_GROUP(DceBinding);
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.
--*/
{
DceBinding->MakePartiallyBound();
// Destroy endpoint mapper context, if set.
if ( *InquireEpLookupHandle() != 0 ) {
EpFreeLookupHandle(*InquireEpLookupHandle());
*InquireEpLookupHandle() = 0;
}
return(RPC_S_OK);
}
RPC_STATUS
WMSG_BINDING_HANDLE::AllocateAssociation (
OUT WMSG_CASSOCIATION ** ppAssociation,
IN PRPC_CLIENT_INTERFACE Interface
)
/*++
Routine Description:
Arguments:
Interface - Supplies information describing the
interface to which we wish to bind.
Return Value:
--*/
{
RPC_STATUS RpcStatus;
RPC_CHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD ComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1;
BOOL Boolean;
WMSG_CASSOCIATION * Association;
ASSERT (AssocGroup != NULL);
// Before we even bother to find or create an association, lets
// check to make sure that we are on the same machine as the server.
ASSERT(DceBinding != 0);
ASSERT( DceBinding->InqNetworkAddress() != 0 );
if ( DceBinding->InqNetworkAddress()[0] != 0 ) {
Boolean = GetComputerName((char *)ComputerName, &ComputerNameLength);
#if DEBUGRPC
if ( Boolean != TRUE ) {
PrintToDebugger("WMSG-C: GetComputerName : %d\n", GetLastError());
}
#endif
ASSERT( Boolean == TRUE );
if ( RpcpStringCompare(DceBinding->InqNetworkAddress(),
ComputerName) != 0 ) {
return(RPC_S_SERVER_UNAVAILABLE);
}
}
// Allocate association from association group.
RpcStatus = AssocGroup->AllocateAssociation(&Association);
if (RpcStatus != RPC_S_OK) {
return(RpcStatus);
}
ASSERT( Association != NULL
&& (Association->CurrentBindingHandle == (WMSG_BINDING_HANDLE *)1));
Association->CurrentBindingHandle = this;
*ppAssociation = Association;
return(RPC_S_OK);
}
void
WMSG_BINDING_HANDLE::SetBlockingHook(
RPC_BLOCKING_FUNCTION BlockingHook
)
{
ASSERT(this->BlockingHook != 0);
ASSERT(BlockingHook != 0);
this->BlockingHook = BlockingHook;
}
WMSG_CASSOCIATION::WMSG_CASSOCIATION (
WMSG_ASSOC_GROUP *AssocGroup
)
/*++
Routine Description:
This association will be initialized, so that it is ready to be
placed into the dictionary of associations.
Arguments:
AssocGroup - Supplies the client assoc group this association is a member of.
--*/
{
Port = new WMSG_DATA_PORT;
this->AssocGroup = AssocGroup;
CurrentBindingHandle = 0;
ThreadId = GetCurrentThreadId();
IBinding = NULL;
}
WMSG_CASSOCIATION::~WMSG_CASSOCIATION (
)
{
AbortAssociation();
}
RPC_STATUS
WMSG_CASSOCIATION::ExchangeBind (
IN PRPC_MESSAGE Message,
OUT unsigned char * ContextId
)
/*++
Routine Description:
Arguments:
Interface - Supplies information describing the interface
to which we wish to bind.
Return Value:
--*/
{
RPC_STATUS RpcStatus;
UCHAR * PortName;
WMSG_PACKET * BindPacket = NULL;
BOOL fInputSyncCall = ((Message->RpcFlags & RPCFLG_INPUT_SYNCHRONOUS) != 0);
WMSG_CONNECT_PORT * ConnectPort = NULL;
PRPC_CLIENT_INTERFACE Interface =
(PRPC_CLIENT_INTERFACE) Message->RpcInterfaceInformation;
void *ThisCallsContext = 0;
if (Port == NULL) {
Port = new WMSG_DATA_PORT;
}
if (Port == NULL) {
return (RPC_S_OUT_OF_MEMORY);
}
BindPacket = Port->AllocatePacket(BIND);
if (BindPacket == NULL) {
return (RPC_S_OUT_OF_MEMORY);
}
BindPacket->Bind.ClientPort = Port;
BindPacket->Bind.InterfaceId = Interface->InterfaceId;
BindPacket->Bind.TransferSyntax = Interface->TransferSyntax;
BindPacket->Bind.Status = RPC_S_INTERNAL_ERROR;
State = CAS_WAIT_BIND;
if (Port->PeerPort == NULL) {
PortName = AssocGroup->DceBinding->InqEndpoint();
ConnectPort = WmsgSystemReferencePortByName((const char *)PortName);
if (ConnectPort == NULL) {
#ifdef DEBUGRPC
PrintToDebugger("WMSG-C: client CAS %x failed to find %s\n",
this,
PortName);
#endif
return (RPC_S_SERVER_UNAVAILABLE);
}
Port->SetAsyncProc(WmsgClientAsyncProc, this);
RpcStatus = SendPacket(fInputSyncCall, ConnectPort, BindPacket);
} else {
RpcStatus = SendPacket(fInputSyncCall, Port->PeerPort, BindPacket);
}
if (RpcStatus != RPC_S_OK) {
if (RpcStatus != RPC_S_OUT_OF_RESOURCES)
RpcStatus = RPC_S_SERVER_UNAVAILABLE;
goto Cleanup;
}
if (!fInputSyncCall)
{
ThisCallsContext = WmsgGetThreadContext();
while (State == CAS_WAIT_BIND)
{
RpcStatus =
CurrentBindingHandle->BlockingHook(Port->hWnd, ThisCallsContext);
if (RpcStatus == RPC_S_CALL_IN_PROGRESS)
{
RpcStatus = RPC_S_OK;
if (State == CAS_WAIT_BIND)
{
State = CAS_TIMEOUT_BIND;
}
}
if (RpcStatus != RPC_S_OK)
{
State = CAS_CANCEL_BIND;
CritSec.Enter();
AssocGroup->CritSec.Enter();
AssocGroup->Associations.Delete(AssociationDictKey);
AssocGroup->CritSec.Leave();
CritSec.Leave();
return(RpcStatus);
}
if (State == CAS_TIMEOUT_BIND)
{
if (!IsWindow(ConnectPort ? ConnectPort->hWnd : Port->PeerPort->hWnd))
{
Port->FreePacket(BindPacket);
BindPacket = NULL;
AbortAssociation();
RpcStatus = RPC_S_SERVER_UNAVAILABLE;
goto Cleanup;
}
else
{
State = CAS_WAIT_BIND;
}
}
}
}
ASSERT(State == CAS_BOUND
&& ResponsePacket == BindPacket);
if (ConnectPort != NULL)
{
if (Port->PeerPort)
{
#ifdef DEBUGRPC
PrintToDebugger("WMSG-C: Client CAS %x connected Port %x to Server Port %x (%s)\n", this, Port, Port->PeerPort, PortName);
#endif
}
else
{
ASSERT(BindPacket->Bind.Status != RPC_S_OK);
}
}
if (State != CAS_BOUND)
{
RpcStatus = RPC_S_INTERNAL_ERROR;
goto Cleanup;
}
if (BindPacket->Bind.Status != RPC_S_OK)
{
RpcStatus = BindPacket->Bind.Status;
goto Cleanup;
}
RpcStatus = RPC_S_OK;
Cleanup:
if (ConnectPort != NULL) {
ConnectPort->Dereference();
ConnectPort = NULL;
}
if (RpcStatus == RPC_S_OK) {
*ContextId = BindPacket->Bind.PresentationContext;
}
if (BindPacket != NULL) {
ASSERT(BindPacket->Type() == BIND);
Port->FreePacket(BindPacket);
}
if (RpcStatus != RPC_S_OK)
{
CurrentBindingHandle = 0;
}
State = CAS_READY;
return (RpcStatus);
}
void
ShutdownWmsgClient (
)
/*++
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_ASSOC_GROUP * Group;
if (AssocGroups != NULL) {
AssocGroups->Reset();
while ((Group = AssocGroups->Next()) != 0) {
delete Group;
}
}
}
void
WMSG_CASSOCIATION::AbortAssociation (
)
/*++
Routine Description:
This association needs to be aborted because a the server side of the
wmsg port has been closed.
--*/
{
WMSG_IBINDING * IBinding;
CritSec.Enter();
ClosePort();
// Delete all negotiated interfaces.
IBindings.Reset();
while ( (IBinding = IBindings.Next()) != 0 ) {
IBindings.Delete(IBinding->ContextId);
delete IBinding;
}
CritSec.Leave();
}
void
WMSG_CASSOCIATION::ClosePort (
)
/*++
Routine Description:
The Port will be closed (and a close message sent to the server).
--*/
{
if (Port)
{
if ( Port->PeerPort != NULL )
{
PostMessage(Port->PeerPort->hWnd,
WMSG_CLOSE, NULL, (LPARAM)Port->PeerPort);
}
Port->Disconnect();
Port->Dereference();
Port = NULL;
}
}
RPC_STATUS
WMSG_CASSOCIATION::GetBuffer (
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
We will allocate a buffer which will be used to either send a request
or receive a response.
Arguments:
Message - Supplies the length of the buffer that is needed. The buffer
will be returned.
Return Value:
RPC_S_OK - A buffer has been successfully allocated. It will be of at
least the required length.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate that
large a buffer.
--*/
{
Message->Handle = this;
if (Port == NULL)
{
// Happens when the CAS has previously been aborted.
Port = new WMSG_DATA_PORT;
if (Port == NULL)
return(RPC_S_OUT_OF_MEMORY);
}
Message->Buffer = Port->GetBuffer(Message->BufferLength);
if (Message->Buffer == NULL) {
return (RPC_S_OUT_OF_MEMORY);
}
return (RPC_S_OK);
}
RPC_STATUS
WMSG_CASSOCIATION::SendPacket (
BOOL fInputSyncCall,
WMSG_PORT * DestinationPort,
WMSG_PACKET * Packet
)
{
Packet->Common.DestinationPort = DestinationPort;
if ( ! fInputSyncCall)
{
if (PostMessage(DestinationPort->hWnd,
WMSG_RPCMSG,
(WPARAM) 0,
(LPARAM) Packet) == FALSE )
{
return (RPC_S_CALL_FAILED_DNE);
}
}
else
{
if ( SendMessage(DestinationPort->hWnd,
WMSG_RPCMSG,
(WPARAM) 0,
(LPARAM) Packet) == FALSE )
{
// Can't cleanup here, server may be using this packet and buffer
return(RPC_S_CALL_FAILED);
}
// Server's reply is now in the input packet. Dispatch it
// as if the server had posted it to us.
AsyncProc(WMSG_RPCMSG, (LPARAM) Packet);
}
return (RPC_S_OK);
}
RPC_STATUS
WMSG_CASSOCIATION::SendAsyncRequest (
WMSG_PACKET * Packet
)
{
WMSG_CONNECT_PORT *ConnectPort;
UCHAR *PortName;
RPC_STATUS RpcStatus = RPC_S_OK;
ASSERT( AssocGroup != 0
&& AssocGroup->DceBinding != NULL);
ASSERT(Packet->Type() == ASYNC_REQUEST);
PortName = AssocGroup->DceBinding->InqEndpoint();
ConnectPort = WmsgSystemReferencePortByName((const char *)PortName);
Packet->Common.DestinationPort = ConnectPort;
if (ConnectPort != NULL)
{
if (PostMessage(ConnectPort->hWnd,
WMSG_RPCMSG,
(WPARAM) 0,
(LPARAM) Packet) == FALSE)
{
RpcStatus = RPC_S_CALL_FAILED_DNE;
}
ConnectPort->Dereference();
}
else
{
RpcStatus = RPC_S_SERVER_UNAVAILABLE;
}
if (RpcStatus != RPC_S_OK)
{
Port->FreeBuffer(Packet->Request.GlobalBuf);
Packet->Request.GlobalBuf = 0;
Port->FreePacket(Packet);
}
return (RpcStatus);
}
RPC_STATUS
WMSG_CASSOCIATION::SendReceive (
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
Arguments:
Message - Supplies the request and returns the response of a remote
procedure call.
Return Value:
RPC_S_OK - The remote procedure call completed successful.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
remote procedure call.
RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to complete
the remote procedure call.
--*/
{
RPC_STATUS RpcStatus;
RPC_STATUS CancelStatus;
WMSG_PACKET *RequestPacket;
void *ThisCallsContext = 0;
BOOL fAsyncCall = ((Message->RpcFlags & RPCFLG_ASYNCHRONOUS) != 0);
BOOL fInputSyncCall = ((Message->RpcFlags & RPCFLG_INPUT_SYNCHRONOUS) != 0);
ASSERT(Message != NULL);
ASSERT(fAsyncCall == 0 || fInputSyncCall == 0);
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
RequestPacket = Port->AllocatePacket(REQUEST);
if (RequestPacket == 0)
{
Port->FreeBuffer(Message->Buffer);
Message->Buffer = 0;
CurrentBindingHandle = 0;
return(RPC_S_OUT_OF_MEMORY);
}
RequestPacket->Request.ProcedureNumber = Message->ProcNum;
RequestPacket->Request.Flags = Message->RpcFlags;
ASSERT(CurrentBindingHandle != NULL);
if (CurrentBindingHandle->InqIfNullObjectUuid() == 0)
{
CurrentBindingHandle->InquireObjectUuid(
(RPC_UUID *)&(RequestPacket->Request.ObjectUuid));
RequestPacket->Request.ObjectUuidFlag = 1;
} else {
RequestPacket->Request.ObjectUuidFlag = 0;
}
ASSERT(Message->Buffer != NULL);
RequestPacket->Request.GlobalBufSize = Message->BufferLength;
RequestPacket->Request.GlobalBuf = Message->Buffer;
RpcStatus = SelectInterface(Message, fAsyncCall);
if (RpcStatus != RPC_S_OK)
{
if (Port)
{
// If the server crashed the connection has closed and
// the port, packet and buffer are already deleted.
Port->FreeBuffer(Message->Buffer);
RequestPacket->Request.GlobalBuf = 0;
Port->FreePacket(RequestPacket);
}
Message->Buffer = 0;
if (State != CAS_CANCEL_BIND)
{
// If the bind was cancelled, then don't release this CAS
// until the bind packet has been processed.
CurrentBindingHandle = 0;
}
return(RpcStatus);
}
// If this is the first async call to an interface, then we
// just send it to the connect port.
if (fAsyncCall && IBinding == 0)
{
PRPC_CLIENT_INTERFACE Interface;
Interface = (RPC_CLIENT_INTERFACE *)Message->RpcInterfaceInformation;
RequestPacket->AsyncRequest.InterfaceId = Interface->InterfaceId;
RequestPacket->AsyncRequest.TransferSyntax = Interface->TransferSyntax;
RequestPacket->AsyncRequest.Common.Type = ASYNC_REQUEST;
RequestPacket->AsyncRequest.ProcessId = GetCurrentProcessId();
RpcStatus =
SendAsyncRequest(RequestPacket);
// SendAsyncRequest either cleans up the buffer and packet or
// they're owned by the server now.
State = CAS_READY;
Message->Buffer = 0;
Message->BufferLength = 0;
if (RpcStatus != RPC_S_OK)
{
CurrentBindingHandle = 0;
}
return(RpcStatus);
}
// Normal call path, send to the servers data port.
ASSERT(IBinding != NULL);
RequestPacket->Request.PresentationContext = IBinding->ContextId;
State = CAS_WAIT_RESPONSE;
ASSERT(Port != 0);
ASSERT(Port->PeerPort != NULL);
RpcStatus = SendPacket(fInputSyncCall, Port->PeerPort, RequestPacket);
if (RpcStatus != RPC_S_OK)
{
ASSERT( RpcStatus == RPC_S_CALL_FAILED_DNE
|| RpcStatus == RPC_S_CALL_FAILED
|| RpcStatus == RPC_S_OUT_OF_RESOURCES);
// If the call failed (!dne), then we must rely on the server
// and on the connection cleanup to delete the buffer(s) and packet.
if ( RpcStatus == RPC_S_CALL_FAILED_DNE
|| RpcStatus == RPC_S_OUT_OF_RESOURCES)
{
Port->FreeBuffer(RequestPacket->Request.GlobalBuf);
RequestPacket->Request.GlobalBuf = 0;
Port->FreePacket(RequestPacket);
Message->Buffer = 0;
}
CurrentBindingHandle = 0;
return (RpcStatus);
}
if (fAsyncCall)
{
// Async calls don't wait for the response.
State = CAS_READY;
Message->Buffer = 0;
Message->BufferLength = 0;
return(RPC_S_OK);
}
if (! fInputSyncCall)
{
// For regular calls, we must block until the server's response has
// been dispatched. Dispatching the response will change the state.
ThisCallsContext = WmsgGetThreadContext();
while (State == CAS_WAIT_RESPONSE)
{
RpcStatus =
CurrentBindingHandle->BlockingHook(Port->hWnd, ThisCallsContext);
// the return value RPC_S_CALL_IN_PROGRESS means that a timeout on
// blocking function occured.
if (RpcStatus == RPC_S_CALL_IN_PROGRESS)
{
RpcStatus = RPC_S_OK;
if (State == CAS_WAIT_RESPONSE)
{
State = CAS_TIMEOUT_RESPONSE;
}
#ifdef DEBUGRPC_DETAIL
PrintToDebugger("RPCRT4: Timeout on HookFunc for RPC window PID=%x TID=%x, hwnd = %x\n",
GetCurrentProcessId(), GetCurrentThreadId(), Port->PeerPort->hWnd);
#endif
}
if (RpcStatus != RPC_S_OK)
{
State = CAS_CANCEL_RESPONSE;
Message->Buffer = 0;
CritSec.Enter();
AssocGroup->CritSec.Enter();
AssocGroup->Associations.Delete(AssociationDictKey);
AssocGroup->CritSec.Leave();
CritSec.Leave();
return(RpcStatus);
}
if (State == CAS_TIMEOUT_RESPONSE)
{
#ifdef DEBUGRPC_DETAIL
PrintToDebugger("RPCRT4: Timeout on RPC window PID=%x TID=%x, hwnd = %x\n",
GetCurrentProcessId(), GetCurrentThreadId(), Port->PeerPort->hWnd);
#endif
if (!IsWindow(Port->PeerPort->hWnd))
{
AbortAssociation();
CurrentBindingHandle = 0;
return(RPC_S_CALL_FAILED);
}
else
{
State = CAS_WAIT_RESPONSE;
}
}
}
}
ASSERT( State == CAS_FAULT
|| State == CAS_RESPONSE);
if (State == CAS_FAULT) {
State = CAS_READY;
RpcStatus = ResponsePacket->Fault.Status;
Port->FreePacket(ResponsePacket);
CurrentBindingHandle = 0;
return (RpcStatus);
}
if (State != CAS_RESPONSE)
{
ASSERT(0);
State = CAS_PROTOCOL_ERROR;
CurrentBindingHandle = 0;
return (RPC_S_CALL_FAILED);
}
Message->Buffer = ResponsePacket->Response.GlobalBuf;
Message->BufferLength = ResponsePacket->Response.GlobalBufSize;
// The server reuses the input packet for the reply, only delete it once!
ASSERT(RequestPacket == ResponsePacket);
ResponsePacket->Response.GlobalBuf = 0;
Port->FreePacket(ResponsePacket);
State = CAS_READY;
return(RPC_S_OK);
}
void
WMSG_CASSOCIATION::FreeBuffer (
IN PRPC_MESSAGE Message
)
/*++
Routine Description:
We will free the supplied buffer.
Arguments:
Message - Supplies the buffer to be freed.
--*/
{
ASSERT(Port != NULL);
if (Message->RpcFlags & RPCFLG_ASYNCHRONOUS)
{
ASSERT(Message->Buffer == 0);
}
else
{
ASSERT(Message->Buffer != NULL);
Port->FreeBuffer(Message->Buffer);
}
// If a call is cancelled, SendReceive will fail and no freebuffer is made
ASSERT( State != CAS_CANCEL_CONNECT
&& State != CAS_CANCEL_BIND
&& State != CAS_CANCEL_RESPONSE);
ASSERT(State == CAS_READY);
CurrentBindingHandle = 0;
}
int
WMSG_CASSOCIATION::IsIdle(
)
{
CritSec.Enter();
// If association is not allocated, grab it.
// This must be an atomic operation.
if ( CurrentBindingHandle == 0 && ThreadId == GetCurrentThreadId())
{
CurrentBindingHandle = (WMSG_BINDING_HANDLE *)1;
CritSec.Leave();
return (1);
}
CritSec.Leave();
return (0);
}
LRESULT
WMSG_CASSOCIATION::AsyncProc(
UINT MsgType,
LPARAM lParam
)
{
ResponsePacket = (WMSG_PACKET *)lParam;
ASSERT(MsgType == WMSG_RPCMSG);
if (MsgType != WMSG_RPCMSG)
{
State = CAS_PROTOCOL_ERROR;
#ifdef DEBUGRPC
PrintToDebugger("Msg rcvd while in invalid state\n");
PrintToDebugger("Association %x in state %x, message %x, packet %p\n",
this, State, MsgType, ResponsePacket);
ASSERT(0);
#endif
return (FALSE);
}
WMSG_TYPE Type = ResponsePacket->Type();
switch (State)
{
case CAS_WAIT_BIND:
ASSERT(Type == BIND);
if (Type != BIND)
{
State = CAS_PROTOCOL_ERROR;
}
else
{
State = CAS_BOUND;
}
return (TRUE);
case CAS_WAIT_RESPONSE:
if (Type == FAULT)
{
State = CAS_FAULT;
return (TRUE);
}
ASSERT(Type == RESPONSE);
if (Type != RESPONSE)
{
State = CAS_PROTOCOL_ERROR;
}
else
{
State = CAS_RESPONSE;
}
return (TRUE);
case CAS_CANCEL_RESPONSE:
ASSERT( Type == FAULT
|| Type == RESPONSE);
if (Type == RESPONSE)
{
Port->FreeBuffer(ResponsePacket->Response.GlobalBuf);
ResponsePacket->Response.GlobalBuf = 0;
Port->FreePacket(ResponsePacket);
ResponsePacket = 0;
}
delete this;
return(TRUE);
case CAS_CANCEL_BIND:
if (Type != BIND)
{
ASSERT(Type == BIND);
State = CAS_PROTOCOL_ERROR;
}
else
{
// No place to store the bind context now...
Port->FreePacket(ResponsePacket);
State = CAS_READY;
}
// so we can destroy the connection ok.
delete this;
return(TRUE);
case CAS_TIMEOUT_BIND:
// Timer went off and now we've get a response. Process it like normal.
State = CAS_WAIT_BIND;
return(AsyncProc(MsgType, lParam));
case CAS_TIMEOUT_RESPONSE:
// Timer went off and now we've get a response. Process it like normal.
State = CAS_WAIT_RESPONSE;
return(AsyncProc(MsgType, lParam));
default:
#ifdef DEBUGRPC
PrintToDebugger("Msg rcvd while in invalid state\n");
PrintToDebugger("Association %x in state %x, message %x, packet %p\n",
this, State, MsgType, ResponsePacket);
ASSERT(0);
#endif
return (FALSE);
}
ASSERT(!"Should never get here");
return (FALSE);
}
RPC_STATUS
WMSG_CASSOCIATION::SelectInterface(
IN PRPC_MESSAGE Message,
IN BOOL AsyncFlag
)
{
RPC_STATUS RpcStatus;
WMSG_IBINDING * IBindingCursor;
unsigned char ContextId;
PRPC_CLIENT_INTERFACE Interface =
(PRPC_CLIENT_INTERFACE) Message->RpcInterfaceInformation;
// Try and find interface in already negotiated interfaces.
IBindings.Reset();
while ( (IBindingCursor = IBindings.Next()) != 0 ) {
if ( IBindingCursor->Compare(Interface) == 0 ) {
IBinding = IBindingCursor;
return (RPC_S_OK);
}
}
// Used in async case.
if (AsyncFlag)
{
IBinding = 0;
return(RPC_S_OK);
}
// Interface NOT previously negotiated, negotiate new interface
RpcStatus = ExchangeBind(Message, &ContextId);
if (RpcStatus != RPC_S_OK) {
return (RpcStatus);
}
// Create new interface.
IBindingCursor = new WMSG_IBINDING(Interface);
if ( IBindingCursor == NULL ) {
return (RPC_S_OUT_OF_MEMORY);
}
IBindingCursor->ContextId = ContextId;
// Add new interface to association.
if ( IBindings.Insert(IBindingCursor) != ContextId ) {
delete IBindingCursor;
return (RPC_S_OUT_OF_MEMORY);
}
// Set current interface to just negotaited interface.
IBinding = IBindingCursor;
return (RPC_S_OK);
}
WMSG_ASSOC_GROUP::WMSG_ASSOC_GROUP(
DCE_BINDING * DceBinding
)
{
ASSERT(AssocGroups != NULL);
this->DceBinding = DceBinding->DuplicateDceBinding();
ASSERT(this->DceBinding != NULL);
ReferenceCount = 1;
GlobalMutexRequest();
AssocGroupKey = AssocGroups->Insert(this);
GlobalMutexClear();
}
WMSG_ASSOC_GROUP::~WMSG_ASSOC_GROUP(
)
{
int NumAssocs = 0;
WMSG_CASSOCIATION * Association;
ASSERT(AssocGroups != NULL);
GlobalMutexRequest();
AssocGroups->Delete(AssocGroupKey);
GlobalMutexClear();
CritSec.Enter();
Associations.Reset();
while ( (Association = Associations.Next()) != 0 ) {
NumAssocs++;
delete Association;
}
CritSec.Leave();
ASSERT(DceBinding != NULL);
delete DceBinding;
DceBinding = 0;
}
void
WMSG_ASSOC_GROUP::AddRef(
)
{
InterlockedIncrement(&ReferenceCount);
}
void
WMSG_ASSOC_GROUP::Dereference(
)
{
if (InterlockedDecrement(&ReferenceCount) == 0) {
delete this;
}
}
RPC_STATUS
WMSG_ASSOC_GROUP::AllocateAssociation(
WMSG_CASSOCIATION ** ppAssociation
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
RPC_STATUS RpcStatus;
WMSG_CASSOCIATION * Association = NULL;
CritSec.Enter();
// Try to find an idle association in the association group.
Associations.Reset();
while ( (Association = Associations.Next()) != 0 ) {
if (Association->IsIdle()) {
goto cleanup;
}
}
// Create a new association if no previous found.
Association = new WMSG_CASSOCIATION(this);
if (Association == NULL) {
CritSec.Leave();
return (RPC_S_OUT_OF_MEMORY);
}
Association->IsIdle();
// Insert new association in association group.
Association->AssociationDictKey = Associations.Insert(Association);
if ( Association->AssociationDictKey == -1 ) {
delete Association;
CritSec.Leave();
return (RPC_S_OUT_OF_MEMORY);
}
cleanup:
*ppAssociation = Association;
CritSec.Leave();
return (RPC_S_OK);
}
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;
BindingHandle = new WMSG_BINDING_HANDLE();
if (BindingHandle == NULL) {
return (NULL);
}
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.
--*/
{
ASSERT(AssocGroups == NULL);
AssocGroups = new WMSG_ASSOC_GROUP_DICT;
if ( AssocGroups == 0 ) {
#ifdef DEBUGRPC
PrintToDebugger("WMSG-C: Failed to initialize\n");
#endif
return(1);
}
return(0);
}
RPC_STATUS RPC_ENTRY
I_RpcBindingSetAsync(
IN RPC_BINDING_HANDLE Binding,
IN RPC_BLOCKING_FUNCTION BlockingHook
)
{
WMSG_BINDING_HANDLE * BindingHandle;
RPC_STATUS Status;
unsigned int Type;
InitializeIfNecessary();
if (BlockingHook == 0)
{
// BUGBUG: Need to do something here
ASSERT(0);
}
BindingHandle = (WMSG_BINDING_HANDLE *)Binding;
if (BindingHandle->InvalidHandle(BINDING_HANDLE_TYPE))
return(RPC_S_INVALID_BINDING);
Status = BindingHandle->InquireTransportType(&Type);
ASSERT(Status == RPC_S_OK);
if (Type & TRANSPORT_TYPE_WMSG == 0)
return(RPC_S_WRONG_KIND_OF_BINDING);
BindingHandle->SetBlockingHook(BlockingHook);
return(RPC_S_OK);
}
RPC_STATUS RPC_ENTRY
I_RpcAsyncSendReceive(
IN OUT RPC_MESSAGE __RPC_FAR * Message,
IN void __RPC_FAR * Context
)
{
RPC_STATUS Status;
WMSG_BINDING_HANDLE * BindingHandle;
unsigned int Type;
InitializeIfNecessary();
BindingHandle = (WMSG_BINDING_HANDLE *)Message->Handle;
if (BindingHandle->InvalidHandle(CCONNECTION_TYPE))
return(RPC_S_INVALID_BINDING);
// BUGBUG: Can/should we check that this is a WMSG CAS?
Status = WmsgSetThreadContext(Context);
ASSERT(Status == RPC_S_OK);
return(BindingHandle->SendReceive(Message));
}
RPC_STATUS RPC_ENTRY
I_RpcGetThreadWindowHandle(
OUT void __RPC_FAR * __RPC_FAR * WindowHandle
)
{
*WindowHandle = WmsgThreadGetWindowHandle();
return(RPC_S_OK);
}