WindowsXP-SP1/com/rpc/runtime/mtrt/lpcsvr.cxx
2020-09-30 16:53:49 +02:00

7066 lines
195 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (C) Microsoft Corporation, 1992 - 1999
Module Name:
lpcsvr.cxx
Abstract:
Implementation of the RPC on LPC protocol engine for the server.
Revision History:
Mazhar Mohammed: Code fork from spcsvr.cxx, 08/02/95
05-06-96: Merged WMSG and LRPC into a single protocol
Mazhar Mohammed Added Pipes Support
Mazhar Mohammed Added support for Async RPC 08-14-96
Mazhar Mohammed No more WMSG 9/22/97
Kamen Moutafov (kamenm) Jan-2000 Support for multiple transfer syntaxes
Kamen Moutafov (KamenM) Dec 99 - Feb 2000 - Support for cell debugging stuff
Kamen Moutafov (KamenM) Mar-2000 Support for extended error info
--*/
#include <precomp.hxx>
#include <queue.hxx>
#include <hndlsvr.hxx>
#include <lpcpack.hxx>
#include <lpcsvr.hxx>
#include <ProtBind.hxx>
#include <lpcclnt.hxx>
#include <CharConv.hxx>
inline BOOL
RecvLotsaCallsWrapper(
LRPC_ADDRESS * Address
)
{
Address->ReceiveLotsaCalls();
return(FALSE);
}
inline RPC_STATUS
InitializeLrpcIfNecessary(
)
/*++
Routine Description:
description
Arguments:
arg1 - description
--*/
{
int nIndex ;
RPC_STATUS Status ;
if (GlobalLrpcServer == 0)
{
if ((Status = InitializeLrpcServer()) != RPC_S_OK)
{
return Status ;
}
}
return (RPC_S_OK) ;
}
LRPC_SERVER::LRPC_SERVER(
IN OUT RPC_STATUS *Status
) : ServerMutex(Status,
TRUE // pre-allocate semaphore
)
{
Address = NULL ;
EndpointInitialized = 0 ;
}
RPC_STATUS
LRPC_SERVER::InitializeAsync (
)
{
RPC_CHAR Endpoint[20];
RPC_STATUS Status = RPC_S_OK ;
if (EndpointInitialized == 0)
{
swprintf(Endpoint, RPC_CONST_STRING("MSAsyncRPC_%d"),
GetCurrentProcessId()) ;
Status = RpcServerUseProtseqEpW (
RPC_STRING_LITERAL("ncalrpc"), 100, Endpoint, NULL) ;
if (Status != RPC_S_OK)
{
return Status ;
}
Status = GlobalRpcServer->ServerListen(1, 100, 1) ;
if (Status == RPC_S_OK)
{
Status = SetEndpoint(Endpoint) ;
}
}
return Status ;
}
RPC_STATUS
InitializeLrpcServer (
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status = RPC_S_OK ;
GlobalMutexRequest() ;
if (GlobalLrpcServer == 0)
{
GlobalLrpcServer = new LRPC_SERVER(&Status) ;
if (GlobalLrpcServer == 0)
{
#if DBG
PrintToDebugger("LRPC: LRPC_SERVER initialization failed\n") ;
#endif
GlobalMutexClear() ;
return (RPC_S_OUT_OF_MEMORY) ;
}
if (Status != RPC_S_OK)
{
GlobalMutexClear() ;
delete GlobalLrpcServer ;
GlobalLrpcServer = 0 ;
return Status ;
}
}
GlobalMutexClear() ;
return (RPC_S_OK) ;
}
void
SetCommonFaultFields (
IN LRPC_MESSAGE *LrpcMessage,
IN RPC_STATUS Status,
IN int Flags,
IN int AdditionalLength
)
{
LrpcMessage->Fault.RpcHeader.MessageType = LRPC_MSG_FAULT;
LrpcMessage->Fault.RpcStatus = Status;
LrpcMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_FAULT_MESSAGE) - sizeof(PORT_MESSAGE)
- sizeof(LrpcMessage->Fault.Buffer) + (CSHORT)AdditionalLength;
LrpcMessage->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_FAULT_MESSAGE) - sizeof(LrpcMessage->Fault.Buffer)
+ (CSHORT)AdditionalLength;
if ((Flags & LRPC_SYNC_CLIENT) == 0)
{
LrpcMessage->LpcHeader.u2.ZeroInit = 0;
LrpcMessage->LpcHeader.CallbackId = 0 ;
LrpcMessage->LpcHeader.MessageId = 0 ;
LrpcMessage->LpcHeader.u2.s2.DataInfoOffset = 0;
}
}
void
SetCommonFault2Fields (
IN LRPC_MESSAGE *LrpcMessage,
IN RPC_STATUS Status,
IN unsigned int Length,
IN void *Buffer
)
{
LrpcMessage->Fault2.RpcHeader.MessageType = LRPC_MSG_FAULT2;
LrpcMessage->Fault2.RpcStatus = Status;
LrpcMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_FAULT2_MESSAGE) - sizeof(PORT_MESSAGE);
LrpcMessage->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_FAULT2_MESSAGE);
// the Server/DataEntries must have been set in GetBuffer - no
// need to reset them here
LrpcMessage->Fault2.RpcHeader.Flags |= LRPC_EEINFO_PRESENT;
}
void
TrimIfNecessaryAndSetImmediateBuffer (
IN LRPC_MESSAGE *LrpcMessage,
IN RPC_STATUS Status,
IN int Flags,
IN size_t EstimatedEEInfoSize,
IN BOOL fTrimEEInfo,
IN ExtendedErrorInfo *CurrentEEInfo
)
{
size_t NeededLength;
RPC_STATUS RpcStatus;
if (fTrimEEInfo)
{
ASSERT(MAXIMUM_FAULT_MESSAGE >= MinimumTransportEEInfoLength);
TrimEEInfoToLength (MAXIMUM_FAULT_MESSAGE, &NeededLength);
if (NeededLength == 0)
{
SetCommonFaultFields(LrpcMessage, Status, Flags, 0);
return;
}
ASSERT(NeededLength <= MAXIMUM_FAULT_MESSAGE);
EstimatedEEInfoSize = NeededLength;
// fall through to the next if - it will succeed
// as we know the length is trimmed.
}
else
{
ASSERT(EstimatedEEInfoSize <= MAXIMUM_FAULT_MESSAGE);
}
RpcStatus = PickleEEInfo(CurrentEEInfo,
LrpcMessage->Fault.Buffer,
MAXIMUM_FAULT_MESSAGE);
if (RpcStatus != RPC_S_OK)
{
SetCommonFaultFields(LrpcMessage, Status, Flags, 0);
return;
}
SetCommonFaultFields(LrpcMessage, Status, Flags, EstimatedEEInfoSize);
LrpcMessage->Fault.RpcHeader.Flags |= LRPC_EEINFO_PRESENT;
}
void
SetFaultPacket (
IN LRPC_MESSAGE *LrpcMessage,
IN RPC_STATUS Status,
IN int Flags,
IN LRPC_SCALL *CurrentCall OPTIONAL
)
/*++
Routine Description:
Initialize a fault packet
Arguments:
LrpcMessage - Fault message
Status - Fault status
Flags - Flags from the request message
--*/
{
THREAD *Thread;
ExtendedErrorInfo *CurrentEEInfo;
size_t EstimatedEEInfoSize;
RPC_STATUS RpcStatus;
RPC_MESSAGE RpcMessage;
// we will see whether there is extended error information here
// and try to send it. If we run in out-of-memory, or there is
// no EEInfo, send plain old fault.
Thread = ThreadSelf();
if (Thread && g_fSendEEInfo)
{
CurrentEEInfo = Thread->GetEEInfo();
if (CurrentEEInfo)
{
// if this function runs in out-of-memory, it will
// return 0.
EstimatedEEInfoSize = EstimateSizeOfEEInfo();
if (EstimatedEEInfoSize == 0)
{
SetCommonFaultFields(LrpcMessage, Status, Flags, 0);
return;
}
// if there is no current call, we cannot send arbitrary length
// data, so we must trim the EEInfo
if (CurrentCall == NULL)
{
TrimIfNecessaryAndSetImmediateBuffer(LrpcMessage,
Status,
Flags,
EstimatedEEInfoSize,
TRUE,
CurrentEEInfo);
return;
}
if (EstimatedEEInfoSize <= MAXIMUM_FAULT_MESSAGE)
{
TrimIfNecessaryAndSetImmediateBuffer(LrpcMessage,
Status,
Flags,
EstimatedEEInfoSize,
FALSE,
CurrentEEInfo);
return;
}
ASSERT(CurrentCall != NULL);
// here, the estimated EEInfo size is larger that the available
// space in the fault packet. We have a call, so we must try
// sending a fault2 packet.
RpcMessage.Handle = CurrentCall;
RpcMessage.RpcFlags = CurrentCall->RpcMessage.RpcFlags;
// increase the buffer lenght in case we fall in the window
// b/n MAXIMUM_MESSAGE_BUFFER and the EstimatedEEInfoSize. If we
// do, GetBuffer will return us an immediate buffer, and this is
// not something we want
RpcMessage.BufferLength = max(EstimatedEEInfoSize, MAXIMUM_MESSAGE_BUFFER + 4);
RpcStatus = CurrentCall->LRPC_SCALL::GetBuffer(&RpcMessage, NULL);
if (RpcStatus != RPC_S_OK)
{
// can't send the full data - trim and send
TrimIfNecessaryAndSetImmediateBuffer(LrpcMessage,
Status,
Flags,
EstimatedEEInfoSize,
TRUE,
CurrentEEInfo);
return;
}
ASSERT(CurrentCall->LrpcReplyMessage->Rpc.RpcHeader.Flags != LRPC_BUFFER_IMMEDIATE);
// on success, GetBuffer has allocated a buffer in RpcMessage.Buffer
// and has setup CurrentCall->LrpcReplyMessage for sending to
// the client.
// Fill in the EEInfo
RpcStatus = PickleEEInfo(CurrentEEInfo,
(unsigned char *)RpcMessage.Buffer,
EstimatedEEInfoSize);
if (RpcStatus != RPC_S_OK)
{
if (!CurrentCall->IsClientAsync())
CurrentCall->Association->Buffers.DeleteItemByBruteForce(RpcMessage.Buffer);
RpcpFarFree(RpcMessage.Buffer);
// can't send the full data - trim and send
TrimIfNecessaryAndSetImmediateBuffer(LrpcMessage,
Status,
Flags,
EstimatedEEInfoSize,
TRUE,
CurrentEEInfo);
return;
}
// Send to the client
if (CurrentCall->IsClientAsync())
{
if (!CurrentCall->IsSyncCall())
{
ASSERT(CurrentCall->LrpcAsyncReplyMessage == CurrentCall->LrpcReplyMessage);
}
SetCommonFault2Fields(CurrentCall->LrpcReplyMessage,
Status,
RpcMessage.BufferLength,
RpcMessage.Buffer);
}
else
{
// send the data for sync client
// set fault2 fields
SetCommonFault2Fields(CurrentCall->LrpcReplyMessage,
Status,
RpcMessage.BufferLength,
RpcMessage.Buffer);
// our caller will do the sending
}
return;
}
}
SetCommonFaultFields(LrpcMessage, Status, Flags, 0);
}
void
SetBindAckFault (
IN LRPC_MESSAGE *LrpcMessage,
IN RPC_STATUS Status
)
/*++
Routine Description:
Initialize a fault bind ack packet (bind_nak). It will add extended
error info if there is some, and sending of eeinfo is enabled.
Arguments:
LrpcMessage - Bind message
Status - Fault status
--*/
{
size_t NeededLength;
ExtendedErrorInfo *EEInfo;
ASSERT(IsBufferAligned(LrpcMessage->Bind.BindExchange.Buffer));
ASSERT(MAX_BIND_NAK >= MinimumTransportEEInfoLength);
LrpcMessage->Bind.BindExchange.RpcStatus = Status;
if (g_fSendEEInfo)
{
EEInfo = RpcpGetEEInfo();
if (EEInfo)
{
TrimEEInfoToLength (MAX_BIND_NAK, &NeededLength);
if (NeededLength != 0)
{
Status = PickleEEInfo(EEInfo,
LrpcMessage->Bind.BindExchange.Buffer,
MAX_BIND_NAK);
if (Status == RPC_S_OK)
{
LrpcMessage->Bind.BindExchange.Flags |= EXTENDED_ERROR_INFO_PRESENT;
LrpcMessage->LpcHeader.u1.s1.DataLength = (CSHORT)NeededLength +
BIND_NAK_PICKLE_BUFFER_OFFSET
- sizeof(PORT_MESSAGE);
}
}
}
}
if (!(LrpcMessage->Bind.BindExchange.Flags & EXTENDED_ERROR_INFO_PRESENT))
{
LrpcMessage->LpcHeader.u1.s1.DataLength = sizeof(LRPC_BIND_MESSAGE)
- sizeof(PORT_MESSAGE);
}
}
LRPC_ADDRESS::LRPC_ADDRESS (
OUT RPC_STATUS * Status
) : RPC_ADDRESS(Status),
ThreadsDoingLongWait(0)
/*++
--*/
{
ObjectType = LRPC_ADDRESS_TYPE;
LpcAddressPort = 0;
CallThreadCount = 0;
ActiveCallCount = 0;
ServerListeningFlag = 0;
AssociationCount = 0;
fServerThreadsStarted = 0;
SequenceNumber = 1;
fTickleMessageAvailable = FALSE;
TickleMessage = NULL;
if (IsServerSideDebugInfoEnabled())
{
DebugCell = (DebugEndpointInfo *)AllocateCell(&DebugCellTag);
if (DebugCell == NULL)
{
*Status = RPC_S_OUT_OF_MEMORY;
}
else
{
DebugCell->TypeHeader = 0;
DebugCell->Type = dctEndpointInfo;
DebugCell->ProtseqType = (UCHAR)LRPC_TOWER_ID;
DebugCell->Status = desAllocated;
memset(DebugCell->EndpointName, 0, sizeof(DebugCell->EndpointName));
}
}
else
DebugCell = NULL;
}
RPC_STATUS
LRPC_ADDRESS::ServerStartingToListen (
IN unsigned int MinimumCallThreads,
IN unsigned int MaximumConcurrentCalls
)
/*++
Routine Description:
This routine gets called when RpcServerListen is called by the application.
We need to create the threads we need to receive remote procedure calls.
Arguments:
MinimumCallThreads - Supplies the minimum number of threads which we
must create.
MaximumConcurrentCalls - Unused.
Return Value:
RPC_S_OK - Ok, this address is all ready to start listening for remote
procedure calls.
RPC_S_OUT_OF_THREADS - We could not create enough threads so that we
have at least the minimum number of call threads required (as
specified by the MinimumCallThreads argument).
--*/
{
RPC_STATUS Status;
UNUSED(MaximumConcurrentCalls);
if (fServerThreadsStarted == 0)
{
Status = InitializeServerSideCellHeapIfNecessary();
if (Status != RPC_S_OK)
return Status;
this->MinimumCallThreads = MinimumCallThreads;
AddressMutex.Request();
if (CallThreadCount < this->MinimumCallThreads)
{
Status = Server->CreateThread((THREAD_PROC)&RecvLotsaCallsWrapper,
this);
if (Status != RPC_S_OK)
{
AddressMutex.Clear();
VALIDATE(Status)
{
RPC_S_OUT_OF_THREADS,
RPC_S_OUT_OF_MEMORY
} END_VALIDATE;
return(Status);
}
CallThreadCount += 1;
}
AddressMutex.Clear();
fServerThreadsStarted = 1;
}
ServerListeningFlag = 1;
return(RPC_S_OK);
}
void
LRPC_ADDRESS::ServerStoppedListening (
)
/*++
Routine Description:
We just need to indicate that the server is no longer listening, and
set the minimum call thread count to one.
--*/
{
ServerListeningFlag = 0;
MinimumCallThreads = 1;
}
#ifdef DEBUGRPC
// Hard coded world (aka EveryOne) SID
const SID World = { 1, 1, { 0, 0, 0, 0, 0, 1}, 0};
// Hard coded anonymous SID
const SID AnonymousLogonSid = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_ANONYMOUS_LOGON_RID};
SECURITY_DESCRIPTOR *DefaultPortSD = NULL;
RPC_STATUS
CreateAndGetDefaultPortSDIfNecessary (
OUT SECURITY_DESCRIPTOR **PortSD
)
/*++
Function Name: CreateAndGetDefaultPortSDIfNecessary
Parameters:
PortSD - receives the default port SD on success.
Undefined on failure
Description:
If the default port SD is not created, creates it,
and returns it. If it is already created, it simply
returns it. The function is thread-safe.
Returns:
RPC_S_OK or other codes for error.
--*/
{
DWORD DaclSize;
PACL Dacl;
ULONG LengthOfDacl;
SECURITY_DESCRIPTOR *LocalDefaultSD; // we work on a local copy to make
// this thread safe
if (DefaultPortSD)
{
*PortSD = DefaultPortSD;
return RPC_S_OK;
}
LocalDefaultSD = new SECURITY_DESCRIPTOR;
if ( LocalDefaultSD == 0
|| !InitializeSecurityDescriptor(LocalDefaultSD,
SECURITY_DESCRIPTOR_REVISION) )
{
return(RPC_S_OUT_OF_MEMORY);
}
ASSERT(GetSidLengthRequired(SID_MAX_SUB_AUTHORITIES) <= 0x44);
DaclSize = 2 * sizeof(ACCESS_ALLOWED_ACE) + sizeof(World) + sizeof(AnonymousLogonSid) + 0x44;
LengthOfDacl = DaclSize + sizeof(ACL);
Dacl = (ACL *) new char[LengthOfDacl];
if (NULL == Dacl)
{
return(RPC_S_OUT_OF_MEMORY);
}
ASSERT(IsValidSid((PVOID)&World));
ASSERT(IsValidSid((PVOID)&AnonymousLogonSid));
InitializeAcl(Dacl, LengthOfDacl, ACL_REVISION);
if (!AddAccessAllowedAce(Dacl, ACL_REVISION,
PORT_ALL_ACCESS,
(PVOID)&World))
{
// this should never fail unless we messed up the
// parameters or there is a version mismatch
ASSERT(0);
delete Dacl;
delete LocalDefaultSD;
return(RPC_S_OUT_OF_RESOURCES);
}
if (!AddAccessAllowedAce(Dacl, ACL_REVISION,
PORT_ALL_ACCESS,
(PVOID)&AnonymousLogonSid ))
{
// this should never fail unless we messed up the
// parameters or there is a version mismatch
ASSERT(0);
delete Dacl;
delete LocalDefaultSD;
return(RPC_S_OUT_OF_RESOURCES);
}
if (!SetSecurityDescriptorDacl(LocalDefaultSD, TRUE, Dacl, FALSE))
{
delete Dacl;
delete LocalDefaultSD;
return(RPC_S_OUT_OF_RESOURCES);
}
if (InterlockedCompareExchangePointer((PVOID *)&DefaultPortSD,
LocalDefaultSD,
NULL) != NULL)
{
// somebody beat us to the punch - free our local copy
delete Dacl;
delete LocalDefaultSD;
}
*PortSD = DefaultPortSD;
return RPC_S_OK;
}
#endif
RPC_STATUS
LRPC_ADDRESS::ActuallySetupAddress (
IN RPC_CHAR * Endpoint,
IN void * SecurityDescriptor OPTIONAL
)
/*++
Function Name:ActuallySetupAddress
Parameters:
Description:
Returns:
--*/
{
NTSTATUS NtStatus;
RPC_CHAR * LpcPortName;
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES ObjectAttributes;
RPC_STATUS Status;
#ifdef DEBUGRPC
BOOL Result;
BOOL DaclPresent;
PACL Dacl;
BOOL Ignored;
#endif
// Allocate and initialize the port name. We need to stick the
// LRPC_DIRECTORY_NAME on the front of the endpoint. This is for
// security reasons (so that anyone can create LRPC endpoints).
LpcPortName = new RPC_CHAR[RpcpStringLength(Endpoint)
+ RpcpStringLength(LRPC_DIRECTORY_NAME) + 1];
if (LpcPortName == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
RpcpMemoryCopy(
LpcPortName,
LRPC_DIRECTORY_NAME,
RpcpStringLength(LRPC_DIRECTORY_NAME) *sizeof(RPC_CHAR));
RpcpMemoryCopy(
LpcPortName + RpcpStringLength(LRPC_DIRECTORY_NAME),
Endpoint,
(RpcpStringLength(Endpoint) + 1) *sizeof(RPC_CHAR));
RtlInitUnicodeString(&UnicodeString, LpcPortName);
#ifdef DEBUGRPC
// in checked builds we check the security descriptor for NULL Dacl,
// and if present, we replace it with a default "allow everyone"
// Dacl. This was requested by ChrisW (12/14/2000) from the Security
// Team so that they can get LPC ports out of the picture, and then
// ASSERT on NULL Dacls for other objects
if (SecurityDescriptor)
{
Result = GetSecurityDescriptorDacl(SecurityDescriptor,
&DaclPresent,
&Dacl,
&Ignored // lpbDaclDefaulted
);
if (!Result)
{
// invalid security descriptor is the only reason this could fail
delete LpcPortName;
return RPC_S_INVALID_ENDPOINT_FORMAT;
}
if (DaclPresent && (Dacl == NULL))
{
Status = CreateAndGetDefaultPortSDIfNecessary((SECURITY_DESCRIPTOR **)&SecurityDescriptor);
if (Status != RPC_S_OK)
{
delete LpcPortName;
return Status;
}
// We were able to grab a default port SD - just let it through
}
// else
// {
// the security descriptor supplied by caller has non NULL Dacl - let
// it through
// }
}
#endif
InitializeObjectAttributes(
&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
0,
SecurityDescriptor);
NtStatus = NtCreatePort(
&LpcAddressPort,
&ObjectAttributes,
sizeof(LRPC_BIND_EXCHANGE),
PORT_MAXIMUM_MESSAGE_LENGTH,
0);
delete LpcPortName;
if (NT_SUCCESS(NtStatus))
{
Status = LrpcSetEndpoint(Endpoint);
return(Status);
}
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)
|| (NtStatus == STATUS_OBJECT_PATH_NOT_FOUND)
|| (NtStatus == STATUS_OBJECT_NAME_INVALID)
|| (NtStatus == STATUS_OBJECT_TYPE_MISMATCH)
|| (NtStatus == STATUS_INVALID_OWNER))
{
return(RPC_S_INVALID_ENDPOINT_FORMAT);
}
#if DBG
if (NtStatus != STATUS_OBJECT_NAME_COLLISION)
{
PrintToDebugger("RPC : NtCreatePort : %lx\n", NtStatus);
}
#endif // DBG
ASSERT(NtStatus == STATUS_OBJECT_NAME_COLLISION);
return(RPC_S_DUPLICATE_ENDPOINT);
}
extern RPC_CHAR *
ULongToHexString (
IN RPC_CHAR * String,
IN unsigned long Number
);
RPC_STATUS
LRPC_ADDRESS::ServerSetupAddress (
IN RPC_CHAR * NetworkAddress,
IN RPC_CHAR * *Endpoint,
IN unsigned int PendingQueueSize,
IN void * SecurityDescriptor, OPTIONAL
IN unsigned long EndpointFlags,
IN unsigned long NICFlags,
OUT NETWORK_ADDRESS_VECTOR **ppNetworkAddressVector
)
/*++
Routine Description:
We need to setup the connection port and get ready to receive remote
procedure calls. We will use the name of this machine as the network
address.
Arguments:
Endpoint - Supplies the endpoint to be used will this address.
NetworkAddress - Returns the network address for this server. The
ownership of the buffer allocated to contain the network address
passes to the caller.
SecurityDescriptor - Optionally supplies a security descriptor to
be placed on this address.
PendingQueueSize - Unused.
RpcProtocolSequence - Unused.
Return Value:
RPC_S_OK - We successfully setup this address.
RPC_S_INVALID_SECURITY_DESC - The supplied security descriptor is
invalid.
RPC_S_CANT_CREATE_ENDPOINT - The endpoint format is correct, but
the endpoint can not be created.
RPC_S_INVALID_ENDPOINT_FORMAT - The endpoint is not a valid
endpoint for this particular transport interface.
RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to
setup the address.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to setup
the address.
--*/
{
BOOL Boolean;
RPC_CHAR * String;
RPC_STATUS Status ;
RPC_CHAR DynamicEndpoint[64];
static unsigned int DynamicEndpointCount = 0;
DWORD NetworkAddressLength = MAX_COMPUTERNAME_LENGTH + 1;
ULONG EndpointLength;
UNUSED(PendingQueueSize);
if (*Endpoint)
{
// the maximum allowed length in bytes is the
// string length in bytes (string length * 2) + the NULL
// terminator
EndpointLength = RpcpStringLength(*Endpoint) * 2 + 2;
if (EndpointLength > BIND_BACK_PORT_NAME_LEN)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_INVALID_ENDPOINT_FORMAT,
EEInfoDLLRPC_ADDRESS__ServerSetupAddress10,
*Endpoint,
EndpointLength,
BIND_BACK_PORT_NAME_LEN);
return RPC_S_INVALID_ENDPOINT_FORMAT;
}
}
Status = InitializeLrpcIfNecessary() ;
if (Status != RPC_S_OK)
{
return Status ;
}
ASSERT(GlobalLrpcServer != 0) ;
*ppNetworkAddressVector = (NETWORK_ADDRESS_VECTOR *)
new char[ sizeof(NETWORK_ADDRESS_VECTOR) + sizeof(RPC_CHAR *) + sizeof(RPC_CHAR) * (MAX_COMPUTERNAME_LENGTH + 1)];
if (*ppNetworkAddressVector == NULL)
{
return RPC_S_OUT_OF_MEMORY;
}
(*ppNetworkAddressVector)->Count = 1;
(*ppNetworkAddressVector)->NetworkAddresses[0] = (RPC_CHAR *)
(((char *) *ppNetworkAddressVector) + sizeof(NETWORK_ADDRESS_VECTOR) + sizeof(RPC_CHAR *));
Boolean = GetComputerNameW(
(*ppNetworkAddressVector)->NetworkAddresses[0],
&NetworkAddressLength);
if (Boolean != TRUE)
{
Status = GetLastError();
#if DBG
PrintToDebugger("RPC : GetComputerNameW : %d\n", Status);
#endif // DBG
if (Status == ERROR_NOT_ENOUGH_MEMORY)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else if ((Status == ERROR_NOT_ENOUGH_QUOTA)
|| (Status == ERROR_NO_SYSTEM_RESOURCES))
{
Status = RPC_S_OUT_OF_RESOURCES;
}
else
{
ASSERT(0);
Status = RPC_S_OUT_OF_MEMORY;
}
goto Cleanup;
}
if (*Endpoint)
{
Status = ActuallySetupAddress(*Endpoint, SecurityDescriptor);
}
else
{
for (;;)
{
String = DynamicEndpoint;
*String++ = RPC_CONST_CHAR('L');
*String++ = RPC_CONST_CHAR('R');
*String++ = RPC_CONST_CHAR('P');
*String++ = RPC_CONST_CHAR('C');
String = ULongToHexString(String,
PtrToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
DynamicEndpointCount += 1;
*String++ = RPC_CONST_CHAR('.');
String = ULongToHexString(String, DynamicEndpointCount);
*String = 0;
Status = ActuallySetupAddress(DynamicEndpoint, SecurityDescriptor);
if (Status != RPC_S_DUPLICATE_ENDPOINT)
{
break;
}
}
if (Status == RPC_S_OK)
{
*Endpoint = DuplicateString(DynamicEndpoint);
if (*Endpoint == 0)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else
{
return(RPC_S_OK);
}
}
}
Cleanup:
if (Status != RPC_S_OK)
{
delete *ppNetworkAddressVector;
*ppNetworkAddressVector = 0;
}
return Status;
}
RPC_STATUS
LRPC_ADDRESS::CompleteListen (
)
/*++
Function Name:CompleteListen
Parameters:
Description:
Returns:
--*/
{
LRPC_ADDRESS *LocalAddress;
if (DebugCell)
{
CStackAnsi AnsiEndpoint;
int i;
RPC_STATUS RpcStatus;
i = RpcpStringLength(InqEndpoint()) + 1;
*(AnsiEndpoint.GetPAnsiString()) = (char *)_alloca(i);
RpcStatus = AnsiEndpoint.Attach(InqEndpoint(), i, i * 2);
// note that effectively we ignore the result. That's ok - we don't
// want servers to be unable to start because of code page issues
// in the debug path. If this fails and we ignore it, the worse
// that can happen is to have empty endpoint in the debug cell
// - not a big deal.
if (RpcStatus == RPC_S_OK)
{
strncpy(DebugCell->EndpointName, AnsiEndpoint, sizeof(DebugCell->EndpointName));
}
DebugCell->Status = desActive;
}
do
{
AddressChain = LrpcAddressList;
}
while (InterlockedCompareExchangePointer((PVOID *)&LrpcAddressList, this, LrpcAddressList) != AddressChain);
return(RPC_S_OK);
}
inline LRPC_SASSOCIATION *
LRPC_ADDRESS::ReferenceAssociation (
IN unsigned long AssociationKey
)
/*++
Routine Description:
Given an assocation key, we need to map it into an association. The
association may already have been deleted, in which case, we need to
return zero.
Arguments:
AssociationKey - Supplies the key to be used to map into an association.
Return Value:
If the association still exists, it will be returned; otherwise, zero
will be returned.
--*/
{
LRPC_SASSOCIATION * Association;
LPC_KEY *LpcKey = (LPC_KEY *) &AssociationKey;
USHORT MySequenceNumber;
ASSERT(SERVERKEY(AssociationKey));
MySequenceNumber = LpcKey->SeqNumber & ~SERVER_KEY_MASK;
AddressMutex.Request();
Association = AssociationDictionary.Find(LpcKey->AssocKey);
if (Association == 0
|| Association->SequenceNumber != MySequenceNumber)
{
AddressMutex.Clear();
return(0);
}
Association->AssociationReferenceCount++;
LogEvent(SU_SASSOC, EV_INC, Association, 0,
Association->AssociationReferenceCount, 1, 1);
AddressMutex.Clear();
return(Association);
}
inline LRPC_CASSOCIATION *
LRPC_ADDRESS::ReferenceClientAssoc (
IN unsigned long AssociationKey
)
/*++
Routine Description:
Given an assocation key, we need to map it into an association. The
association may already have been deleted, in which case, we need to
return zero.
Arguments:
AssociationKey - Supplies the key to be used to map into an association.
Return Value:
If the association still exists, it will be returned; otherwise, zero
will be returned.
--*/
{
LRPC_CASSOCIATION * Association;
LPC_KEY *LpcKey = (LPC_KEY *) &AssociationKey;
LrpcMutexRequest();
Association = LrpcAssociationDict->Find(LpcKey->AssocKey);
if (Association == 0
|| Association->SequenceNumber != LpcKey->SeqNumber)
{
LrpcMutexClear();
return(0);
}
Association->AddReference();
LrpcMutexClear();
return(Association);
}
#if defined(_WIN64)
#define BAD_HANDLE_CONST ((HANDLE)0xbaaaaaadbaaaaaad)
#else
#define BAD_HANDLE_CONST (ULongToHandle(0xbaaaaaad))
#endif
inline void
LRPC_ADDRESS::DereferenceAssociation (
IN LRPC_SASSOCIATION * Association
)
/*++
Routine Description:
We are done using this address, so the reference count can be decremented.
If no one is referencing this association, then we can go ahead and
delete it.
Arguments:
Association - Supplies the association whose reference count should be
decremented.
--*/
{
NTSTATUS NtStatus;
AddressMutex.Request();
Association->AssociationReferenceCount -= 1;
ASSERT(Association->AssociationReferenceCount >= 0);
LogEvent(SU_SASSOC, EV_DEC, Association, 0,
Association->AssociationReferenceCount, 1, 1);
if (Association->AssociationReferenceCount <= 0)
{
AssociationDictionary.Delete(Association->DictionaryKey);
AssociationCount--;
AddressMutex.Clear();
if (Association->LpcServerPort)
{
NtStatus = NtClose(Association->LpcServerPort);
Association->LpcServerPort = BAD_HANDLE_CONST;
LogEvent(SU_SASSOC, EV_STOP, Association, Association->LpcServerPort,
Association->AssociationReferenceCount, 1, 1);
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtClose : %lx\n", NtStatus);
ASSERT(0) ;
}
#endif // DBG
}
if (Association->LpcReplyPort)
{
NtStatus = NtClose(Association->LpcReplyPort);
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtClose : %lx\n", NtStatus);
ASSERT(0) ;
}
#endif // DBG
}
delete Association;
}
else
{
AddressMutex.Clear();
}
}
BOOL
LRPC_ADDRESS::DealWithLRPCRequest (
IN LRPC_MESSAGE * LrpcMessage,
IN LRPC_MESSAGE * LrpcReply,
IN LRPC_SASSOCIATION *Association,
OUT LRPC_MESSAGE **LrpcResponse
)
/*++
Routine Description:
Deal with a new LRPC request.
Arguments:
LrpcMessage - request message
LrpcReply - the reply is placed here
Association - the association on which the request arrived
Return Value:
FALSE if the thread should stay, or !FALSE if the thread should go
--*/
{
int retval ;
LRPC_SCALL *SCall;
NTSTATUS NtStatus ;
RPC_STATUS Status;
LRPC_SCALL *NewSCall ;
int Flags = LrpcMessage->Rpc.RpcHeader.Flags ;
if (ServerListeningFlag == 0
&& GlobalRpcServer->InqNumAutoListenInterfaces() == 0)
{
*LrpcResponse = LrpcMessage ;
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_SERVER_TOO_BUSY,
EEInfoDLDealWithLRPCRequest10,
(ULONG)ServerListeningFlag,
(ULONG)GlobalRpcServer->InqNumAutoListenInterfaces());
SetFaultPacket(*LrpcResponse,
RPC_S_SERVER_TOO_BUSY, Flags, NULL);
return 0;
}
Status = Association->AllocateSCall(LrpcMessage,
LrpcReply,
Flags,
&SCall) ;
if (Status != RPC_S_OK)
{
*LrpcResponse = LrpcMessage ;
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLDealWithLRPCRequest20);
SetFaultPacket(*LrpcResponse, Status, Flags, NULL);
return 0 ;
}
ASSERT(SCall);
Status = SCall->LrpcMessageToRpcMessage(LrpcMessage,
&(SCall->RpcMessage));
if (Status != RPC_S_OK)
{
#if DBG
PrintToDebugger("LRPC: LrpcMessageToRpcMessage failed: %d\n",
Status) ;
#endif
*LrpcResponse = LrpcMessage ;
SetFaultPacket(*LrpcResponse, Status, Flags, NULL);
Association->FreeSCall (SCall) ;
return 0;
}
AddressMutex.Request();
if (SCall->Flags & LRPC_CAUSAL)
{
retval = Association->MaybeQueueSCall(SCall) ;
switch (retval)
{
case 0:
break;
case 1:
AddressMutex.Clear();
*LrpcResponse = NULL ;
return 0;
case -1:
AddressMutex.Clear();
*LrpcResponse = LrpcMessage ;
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_OUT_OF_MEMORY,
EEInfoDLDealWithLRPCRequest30);
SetFaultPacket(*LrpcResponse, LRPC_MSG_FAULT, Flags, NULL);
Association->FreeSCall (SCall) ;
return 0;
}
}
ActiveCallCount += 1;
if (ActiveCallCount >= CallThreadCount)
{
Status = Server->CreateThread(
(THREAD_PROC)&RecvLotsaCallsWrapper,
this);
if (Status == RPC_S_OK)
{
CallThreadCount += 1;
}
else
{
// If the above SCall is causal and creating the thread has failed
// then the call has been put into the dictionary and needs
// to be removed. It will be the only scall for the key.
if (SCall->Flags & LRPC_CAUSAL)
Association->ClientThreadDict.Delete(MsgClientIdToClientId(SCall->LrpcRequestMessage->Rpc.LpcHeader.ClientId).UniqueThread) ;
ActiveCallCount -= 1;
ASSERT((int)ActiveCallCount >= 0);
AddressMutex.Clear();
*LrpcResponse = LrpcMessage ;
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLDealWithLRPCRequest40);
SetFaultPacket(*LrpcResponse,
RPC_S_SERVER_TOO_BUSY, Flags, NULL);
Association->FreeSCall(SCall) ;
return 0;
}
}
AddressMutex.Clear();
while (1)
{
LrpcReply->Rpc.RpcHeader.Flags = 0;
SCall->DealWithRequestMessage();
if ((SCall->Flags & LRPC_CAUSAL) == 0)
{
break;
}
NewSCall = Association->GetNextSCall(SCall) ;
if (NewSCall)
{
SCall->SendReply();
SCall = NewSCall ;
while (SCall->Deleted)
{
FreeMessage(SCall->LrpcRequestMessage) ;
NewSCall = Association->GetNextSCall(SCall) ;
AddressMutex.Request();
//
// N.B. If a causally ordered call fails
// in DealWithRequestMessage, this is fine, because
// in SendReply, if we send back a fault,
// we will mark all calls in the SCallDict with Deleted,
// and in this loop, we will skip them.
//
if (NewSCall == 0)
{
if (fKeepThread())
{
retval = 0;
}
else
{
CallThreadCount -= 1;
retval = 1;
}
ActiveCallCount -= 1;
ASSERT((int)ActiveCallCount >= 0);
AddressMutex.Clear();
Association->FreeSCall(SCall) ;
DereferenceAssociation(Association) ;
*LrpcResponse = NULL ;
return retval;
}
AddressMutex.Clear();
Association->FreeSCall(SCall) ;
DereferenceAssociation(Association) ;
SCall = NewSCall ;
}
RpcpPurgeEEInfo();
}
else
{
break;
}
// Make sure that the LrpcReplyMessage is always pointing to the
// one located on this thread's stack. It is possible that we
// will pick up a queued scall that was put into the queue on
// another thread. In this case, the LrpcReplyMessage may point to
// that thread's stack - a recepie for disaster.
// Note that LrpcReply is located on the stack for the current thread.
LrpcReply->Rpc.RpcHeader.CallId = SCall->CallId ;
SCall->LrpcReplyMessage = LrpcReply;
}
AddressMutex.Request();
if (fKeepThread())
{
if (SCall->IsSyncCall() && SCall->IsClientAsync() == 0)
{
ActiveCallCount -= 1;
ASSERT((int)ActiveCallCount >= 0);
AddressMutex.Clear();
*LrpcResponse = SCall->InitMsg();
Association->FreeSCall(SCall) ;
}
else
{
AddressMutex.Clear();
*LrpcResponse = NULL;
SCall->SendReply();
AddressMutex.Request();
ActiveCallCount -= 1;
ASSERT((int)ActiveCallCount >= 0);
AddressMutex.Clear();
}
return 0 ;
}
//
// This thread is extraneous, reply and return this
// thread to the system.
//
ActiveCallCount -= 1;
ASSERT((int)ActiveCallCount >= 0);
CallThreadCount -= 1;
AddressMutex.Clear();
SCall->SendReply();
return 1 ;
}
#define LRPC_LISTEN_TIMEOUT 5*60*1000
inline void
FormatTimeOut(
OUT PLARGE_INTEGER TimeOut,
IN DWORD Milliseconds
)
{
ASSERT(Milliseconds != -1);
TimeOut->QuadPart = UInt32x32To64( Milliseconds, 10000 );
TimeOut->QuadPart *= -1;
}
RPC_STATUS
LRPC_ADDRESS::BeginLongCall(
void
)
{
RPC_STATUS Status = RPC_S_OK;
AddressMutex.Request();
if (ActiveCallCount + 1 >= CallThreadCount)
{
AddressMutex.Clear();
Status = Server->CreateThread(
(THREAD_PROC)&RecvLotsaCallsWrapper,
this);
AddressMutex.Request();
// N.B. We increase the active call count
// regrdless of Status. This is OK, because
// if we return failure, the caller of this function
// is responsible to decrease it
ActiveCallCount ++;
if (Status == RPC_S_OK)
{
CallThreadCount += 1;
}
}
else
{
ActiveCallCount ++;
}
AddressMutex.Clear();
return Status;
}
void LRPC_ADDRESS::HandleInvalidAssociationReference (
IN LRPC_MESSAGE *RequestMessage,
IN OUT LRPC_MESSAGE **ReplyMessage,
IN ULONG AssociationKey
)
{
ASSERT(RequestMessage != NULL);
ASSERT(ReplyMessage != NULL);
// we handle only binds, requests and copies
if ((RequestMessage->Bind.MessageType != LRPC_MSG_REQUEST)
&& (RequestMessage->Bind.MessageType != LRPC_MSG_BIND)
&& (RequestMessage->Bind.MessageType != LRPC_MSG_COPY))
{
*ReplyMessage = NULL;
return;
}
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_CALL_FAILED_DNE,
EEInfoDLLRPC_ADDRESS__HandleInvalidAssociationReference10,
AssociationKey);
if (RequestMessage->Bind.MessageType == LRPC_MSG_BIND)
{
SetBindAckFault(RequestMessage, RPC_S_CALL_FAILED_DNE);
// if this is bind, patch up the fields a bit, as SetFaultPacket
// does not set everything right for the bind case
RequestMessage->Bind.MessageType = LRPC_BIND_ACK;
}
else
{
SetFaultPacket(RequestMessage,
RPC_S_CALL_FAILED_DNE,
RequestMessage->Rpc.RpcHeader.Flags,
NULL);
}
*ReplyMessage = RequestMessage;
}
BOOL
LRPC_ADDRESS::EndLongCall(
void
)
{
AddressMutex.Request();
ActiveCallCount -= 1;
int SpareThreads = CallThreadCount -
(ActiveCallCount + MinimumCallThreads);
if (SpareThreads > 0)
{
ASSERT(CallThreadCount > ActiveCallCount);
AddressMutex.Clear();
return TRUE;
}
AddressMutex.Clear();
return FALSE;
}
void
LRPC_ADDRESS::ReceiveLotsaCalls (
)
/*++
Routine Description:
Here is where we receive remote procedure calls to this address. One
more threads will be executing this routine at once.
--*/
{
NTSTATUS NtStatus;
LRPC_SASSOCIATION * Association;
LRPC_CASSOCIATION *CAssociation;
unsigned long AssociationKey;
char *PaddedMessage;
LRPC_MESSAGE * Reply ;
LRPC_MESSAGE * LrpcMessage = 0;
LRPC_MESSAGE * LrpcReplyMessage = 0;
int AssociationType = 0;
int Flags = 0;
BOOL PartialFlag ;
BOOL fStatus ;
RPC_STATUS Status;
unsigned long ReplyKey = -1;
LARGE_INTEGER LongTimeout;
LARGE_INTEGER ShortTimeout;
PLARGE_INTEGER pliTimeout = &ShortTimeout;
ULONG_PTR Key;
THREAD *ThisThread;
DebugThreadInfo *DebugCell;
FormatTimeOut(&ShortTimeout, gThreadTimeout);
FormatTimeOut(&LongTimeout, LRPC_LISTEN_TIMEOUT);
pliTimeout = &ShortTimeout;
PaddedMessage = (char *) _alloca(PadToNaturalBoundary(sizeof(LRPC_MESSAGE) + 1) + sizeof(LRPC_MESSAGE));
Reply = (LRPC_MESSAGE *) AlignOnNaturalBoundary(PaddedMessage) ;
ThisThread = RpcpGetThreadPointer();
ASSERT(ThisThread);
DebugCell = ThisThread->DebugCell;
if (DebugCell)
{
if (this->DebugCell)
{
GetDebugCellIDFromDebugCell(
(DebugCellUnion *)this->DebugCell,
&this->DebugCellTag,
&ThisThread->DebugCell->Endpoint);
}
}
for (;;)
{
if (LrpcMessage == 0)
{
while ((LrpcMessage = AllocateMessage()) == 0)
{
Sleep(100) ;
}
}
ASSERT(LrpcReplyMessage == 0
|| LrpcReplyMessage->Rpc.RpcHeader.MessageType <= MAX_LRPC_MSG);
if (DebugCell)
{
DebugCell->Status = dtsIdle;
DebugCell->LastUpdateTime = NtGetTickCount();
}
RpcpPurgeEEInfoFromThreadIfNecessary(ThisThread);
NtStatus = NtReplyWaitReceivePortEx(LpcAddressPort,
(PVOID *) &Key,
(PORT_MESSAGE *) LrpcReplyMessage,
(PORT_MESSAGE *) LrpcMessage,
pliTimeout);
AssociationKey = (ULONG) Key; // need this for 64bit
if (NtStatus != STATUS_TIMEOUT
&& NT_SUCCESS(NtStatus))
{
if (pliTimeout != &ShortTimeout)
{
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LPC Thread %X: coming back from long wait\n",
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
#endif
ASSERT((pliTimeout == NULL) || (pliTimeout == &LongTimeout));
ThreadsDoingLongWait.Decrement();
pliTimeout = &ShortTimeout;
}
if (DebugCell)
{
DebugCell->Status = dtsProcessing;
DebugCell->LastUpdateTime = NtGetTickCount();
}
#if 0
if (LrpcMessage->LpcHeader.u2.s2.Type == LPC_CONNECTION_REQUEST)
LogEvent(SU_PACKET, EV_PKT_IN, (void *) LrpcMessage->LpcHeader.u2.ZeroInit,
(void *)LrpcMessage->Connect.BindExchange.ConnectType, AssociationKey);
else
LogEvent(SU_PACKET, EV_PKT_IN, (void *) LrpcMessage->LpcHeader.u2.ZeroInit,
0, AssociationKey);
#endif
if (LrpcMessage->LpcHeader.u2.s2.Type == LPC_DATAGRAM
|| LrpcMessage->LpcHeader.u2.s2.Type == LPC_REQUEST)
{
if (!SERVERKEY(AssociationKey))
{
VALIDATE(LrpcMessage->Bind.MessageType)
{
LRPC_MSG_FAULT,
LRPC_MSG_FAULT2,
LRPC_MSG_RESPONSE,
LRPC_CLIENT_SEND_MORE
} END_VALIDATE;
//
// response or fault on the back connection.
// we are using async rpc or pipes
//
CAssociation = ReferenceClientAssoc(AssociationKey);
if (CAssociation)
{
BeginLongCall();
LrpcReplyMessage = 0;
CAssociation->ProcessResponse(LrpcMessage, &LrpcReplyMessage);
//
// the receive thread needs to allocate a new message
//
LrpcMessage = 0 ;
CAssociation->RemoveReference() ;
EndLongCall();
}
else
{
HandleInvalidAssociationReference(LrpcMessage,
&LrpcReplyMessage,
AssociationKey);
}
continue;
}
Association = ReferenceAssociation(AssociationKey);
if (Association == 0)
{
HandleInvalidAssociationReference(LrpcMessage,
&LrpcReplyMessage,
AssociationKey);
continue;
}
ReplyKey = AssociationKey;
Flags = LrpcMessage->Rpc.RpcHeader.Flags ;
PartialFlag = FALSE ;
if (LrpcMessage->Bind.MessageType == LRPC_MSG_REQUEST)
{
//
// Optimize the common case
//
fStatus = DealWithLRPCRequest (
LrpcMessage,
Reply,
Association,
&LrpcReplyMessage) ;
if (fStatus)
{
// this is the first of two exits from the loop
// (the second is below)
if (DebugCell)
{
DebugCell->Status = dtsAllocated;
DebugCell->LastUpdateTime = NtGetTickCount();
}
return;
}
if (LrpcReplyMessage == 0)
{
LrpcMessage = 0;
}
else
{
DereferenceAssociation(Association);
}
}
else
{
switch (LrpcMessage->Bind.MessageType)
{
case LRPC_PARTIAL_REQUEST:
case LRPC_SERVER_SEND_MORE:
case LRPC_MSG_CANCEL:
LrpcReplyMessage = Association->
DealWithPartialRequest(&LrpcMessage) ;
break;
case LRPC_MSG_COPY:
LrpcReplyMessage = Association->DealWithCopyMessage(
(LRPC_COPY_MESSAGE *)LrpcMessage);
break;
case LRPC_MSG_BIND :
Association->DealWithBindMessage(LrpcMessage);
LrpcReplyMessage = 0 ;
break;
case LRPC_MSG_BIND_BACK:
BeginLongCall();
LrpcReplyMessage = Association->
DealWithBindBackMessage(LrpcMessage);
EndLongCall();
break;
default:
#if DBG
PrintToDebugger("RPC : Bad Message Type (%d) - %d\n",
LrpcMessage->Bind.MessageType,
LrpcMessage->LpcHeader.u2.s2.Type);
#endif // DBG
ASSERT(0) ;
LrpcReplyMessage = 0 ;
Association->Delete();
break;
}
DereferenceAssociation(Association);
}
}
else
{
switch (LrpcMessage->LpcHeader.u2.s2.Type)
{
case LPC_CONNECTION_REQUEST:
if (LrpcMessage->Connect.BindExchange.ConnectType
== LRPC_CONNECT_REQUEST)
{
BeginLongCall();
DealWithNewClient(LrpcMessage) ;
EndLongCall();
}
else if (LrpcMessage->Connect.BindExchange.ConnectType
== LRPC_CONNECT_RESPONSE)
{
DealWithConnectResponse(LrpcMessage) ;
}
else if (LrpcMessage->Connect.BindExchange.ConnectType
== LRPC_CONNECT_TICKLE)
{
HANDLE Ignore;
// always reject - this just has the purpose of tickling
// a thread on a long wait
NtStatus = NtAcceptConnectPort(&Ignore,
NULL,
(PORT_MESSAGE *) LrpcMessage,
FALSE,
NULL,
NULL);
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LPC Thread %X: tickled\n",
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
#endif
}
else
{
ASSERT(0) ;
}
LrpcReplyMessage = 0;
break;
case LPC_CLIENT_DIED:
LrpcReplyMessage = 0;
break;
case LPC_PORT_CLOSED:
if (SERVERKEY(AssociationKey))
{
Association = ReferenceAssociation(AssociationKey);
if (Association == 0)
{
LrpcReplyMessage = 0;
continue;
}
BeginLongCall();
Association->Delete();
DereferenceAssociation(Association);
LrpcReplyMessage = 0;
EndLongCall();
}
else
{
CAssociation = ReferenceClientAssoc(AssociationKey);
if (CAssociation)
{
BeginLongCall();
CAssociation->AbortAssociation(1) ;
CAssociation->RemoveReference() ;
EndLongCall();
}
LrpcReplyMessage = 0;
}
continue;
default:
LrpcReplyMessage = 0 ;
ASSERT(0);
} // switch
} // else
} // if
else
{
switch (NtStatus)
{
case STATUS_NO_MEMORY:
case STATUS_INSUFFICIENT_RESOURCES:
case STATUS_UNSUCCESSFUL:
PauseExecution(500L);
break;
case STATUS_TIMEOUT:
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LPC Thread %X: timed out - gc\n",
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
#endif
PerformGarbageCollection();
if (pliTimeout == &ShortTimeout)
{
// be conservative and presume we will
// be doing long wait. If later we find out
// we won't, we'll reverse that. Also, this must
// be done nefore we check for
// GarbageCollectedRequested - this allows other
// threads to safely count the number of threads
// on short wait without taking a mutex
ThreadsDoingLongWait.Increment();
LrpcReplyMessage = 0;
// if there is garbage collection
// requested, don't switch to long
// wait
if (GarbageCollectionRequested)
{
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LPC Thread %X: gc requested - can't do long wait\n",
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
#endif
ThreadsDoingLongWait.Decrement();
}
else
{
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LPC Thread %X: going to long wait\n",
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
#endif
// there is no garbage collection requested
// switch to longer wait (but not infinite yet)
pliTimeout = &LongTimeout;
}
}
else if (pliTimeout == &LongTimeout)
{
// if this is a long wait, and we're a spare
// thread, we can go
AddressMutex.Request();
if (CallThreadCount - ActiveCallCount > 1)
{
CallThreadCount -= 1;
ASSERT(CallThreadCount > ActiveCallCount);
AddressMutex.Clear();
// decrease the counter of threads doing long
// listen after we decrease the CallThreadCount
// This allows other threads to use the number
// of threads doing short wait without taking
// a mutex
ThreadsDoingLongWait.Decrement();
FreeMessage(LrpcMessage);
// N.B. This is the second exit from the loop (see above)
if (DebugCell)
{
DebugCell->Status = dtsAllocated;
DebugCell->LastUpdateTime = NtGetTickCount();
}
return ;
}
else
{
//
// We are assuming that if the call has timed out, the reply has
// been sent
//
LrpcReplyMessage = 0;
pliTimeout = NULL;
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LPC Thread %X: going to infinite wait\n",
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
#endif
}
AddressMutex.Clear();
}
else
{
ASSERT(!"We cannot get a timeout on wait with infinite timeout");
}
if (DebugCell)
{
RelocateCellIfPossible((void **) &DebugCell, &ThisThread->DebugCellTag);
ThisThread->DebugCell = DebugCell;
}
break;
default:
if (LrpcReplyMessage)
{
LrpcReplyMessage = 0;
if (ReplyKey != -1)
{
Association = ReferenceAssociation(ReplyKey);
if (Association == 0)
{
continue;
}
}
else
continue;
BeginLongCall();
Association->Delete();
DereferenceAssociation(Association);
EndLongCall();
}
break;
} // switch
} // else
} // for
}
#define DEFAULT_PORT_DIR "\\RPC Control\\"
#define DEFAULT_PORT_NAME "ARPC Port1"
#define DEFAULT_REPLY_NAME "ARPC Reply Port"
void
LRPC_ADDRESS::DealWithNewClient (
IN LRPC_MESSAGE * ConnectionRequest
)
/*++
Routine Description:
A new client has connected with our address port. We need to take
care of the new client and send a response.
Arguments:
ConnectionRequest - Supplies information need by LPC to abort the
connect request. Includes the bind request from the client.
This contains the information about which interface the client
wants to bind with. and which we use to send the status code
back in.
--*/
{
LRPC_SASSOCIATION * Association;
NTSTATUS NtStatus;
RPC_STATUS Status = RPC_S_OK;
DWORD Key;
LPC_KEY *LpcKey = (LPC_KEY *) &Key;
Association = new LRPC_SASSOCIATION(this,
&Status);
if (Association == 0)
{
RejectNewClient(ConnectionRequest, RPC_S_OUT_OF_MEMORY);
return;
}
if (Status != RPC_S_OK)
{
delete Association ;
RejectNewClient(ConnectionRequest, RPC_S_OUT_OF_MEMORY);
return ;
}
AddressMutex.Request();
Association->DictionaryKey = (unsigned short)
AssociationDictionary.Insert(Association);
AssociationCount++;
SequenceNumber = (SequenceNumber+1) % (0x7FFF);
Association->SequenceNumber = SequenceNumber;
AddressMutex.Clear();
if (Association->DictionaryKey == -1)
{
AddressMutex.Request();
AssociationCount-- ;
AddressMutex.Clear();
delete Association ;
RejectNewClient(ConnectionRequest, RPC_S_OUT_OF_MEMORY);
return;
}
if (ConnectionRequest->Connect.BindExchange.Flags & BIND_BACK_FLAG)
{
ConnectionRequest->Connect.BindExchange.szPortName[PORT_NAME_LEN-1] = NULL;
Status = Association->BindBack(
(RPC_CHAR *)ConnectionRequest->Connect.BindExchange.szPortName,
ConnectionRequest->Connect.BindExchange.AssocKey) ;
if (Status != RPC_S_OK)
{
RejectNewClient(ConnectionRequest, RPC_S_OUT_OF_MEMORY);
Association->Delete() ;
return;
}
}
ConnectionRequest->Connect.BindExchange.RpcStatus = RPC_S_OK;
ASSERT(sizeof(unsigned long) <= sizeof(PVOID));
ASSERT((Association->SequenceNumber & SERVER_KEY_MASK) == 0);
LpcKey->SeqNumber = Association->SequenceNumber | SERVER_KEY_MASK;
LpcKey->AssocKey = Association->DictionaryKey;
// After the call to NtAcceptConnectPort, the client will become unblocked
// the association will be in the dictionary and will have refcount 1. If the client quits
// or closes port the association will be deleted. Then NtCompleteConnectPort
// may touch invalid memory or operate on a bad handle. To prevent that we
// need to hold an extra count between the two calls.
//
// Since this thread is the only one playing with the association up to now,
// there is no need for a lock.
Association->AssociationReferenceCount++;
NtStatus = NtAcceptConnectPort(&(Association->LpcServerPort),
ULongToPtr(Key),
(PORT_MESSAGE *) ConnectionRequest,
TRUE,
NULL,
NULL);
if (NT_ERROR(NtStatus))
{
Association->Delete();
// We just have to dereference the association to remove the extra
// count added above. This should cause its deletion.
DereferenceAssociation(Association);
#if DBG
PrintToDebugger("RPC : NtAcceptConnectPort : %lx\n", NtStatus);
#endif // DBG
return;
}
NtStatus = NtCompleteConnectPort(Association->LpcServerPort);
if (NT_ERROR(NtStatus))
{
#if DBG
PrintToDebugger("RPC : NtCompleteConnectPort : %lx\n", NtStatus);
#endif // DBG
// If Association->Delete() has already been called on a different
// theread due to a closed client port, this call will be ignored...
Association->Delete();
// and the final reference will be removed here causing a deletion.
DereferenceAssociation(Association);
return;
}
// Remove the extra-reference.
DereferenceAssociation(Association);
}
void
LRPC_ADDRESS::DealWithConnectResponse (
IN LRPC_MESSAGE * ConnectResponse
)
/*++
Routine Description:
Just received a connect response from the remove server,
need to handle that.
Arguments:
ConnectionRequest -
Needed to get the pAssoc
--*/
{
NTSTATUS NtStatus;
HANDLE temp ;
LRPC_CASSOCIATION * Association ;
DWORD Key;
Key = ConnectResponse->Connect.BindExchange.AssocKey;
Association = ReferenceClientAssoc(Key);
if (Association == 0)
{
RejectNewClient(ConnectResponse, RPC_S_PROTOCOL_ERROR);
return;
}
NtStatus = NtAcceptConnectPort(&temp,
ULongToPtr(Key),
(PPORT_MESSAGE) ConnectResponse,
TRUE,
NULL,
NULL);
if (NT_SUCCESS(NtStatus))
{
Association->SetReceivePort(temp) ;
NtStatus = NtCompleteConnectPort(temp);
if (!NT_SUCCESS(NtStatus))
{
#if DBG
PrintToDebugger("LRPC: NtCompleteConnectPort(1) failed: %lx\n",
NtStatus) ;
#endif
Association->Delete();
}
}
else
{
#if DBG
PrintToDebugger("LRPC: NtAcceptConnectionPort(1) failed: %lx\n",
NtStatus) ;
#endif
Association->Delete();
}
//
// Remove the reference we added above
//
Association->RemoveReference() ;
}
void
LRPC_ADDRESS::RejectNewClient (
IN LRPC_MESSAGE * ConnectionRequest,
IN RPC_STATUS Status
)
/*++
Routine Description:
A new client has connected with our address port. We need to reject
the client.
Arguments:
ConnectionRequest - Supplies information need by LPC to abort the
connect request. Includes the bind request from the client,
which we use to send the status code back in.
Status - Supplies the reason the client is being rejected.
--*/
{
NTSTATUS NtStatus;
HANDLE Ignore;
ASSERT(Status != RPC_S_OK);
ConnectionRequest->Connect.BindExchange.RpcStatus = Status;
ConnectionRequest->Connect.BindExchange.Flags |= SERVER_BIND_EXCH_RESP;
NtStatus = NtAcceptConnectPort(&Ignore,
NULL,
(PORT_MESSAGE *) ConnectionRequest,
FALSE,
NULL,
NULL);
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtAcceptConnectPort : %lx\n", NtStatus);
// if the client thread dies for whatever reason, NtAcceptConnectPort
// can return STATUS_REPLY_MESSAGE_MISMATCH
VALIDATE(NtStatus)
{
STATUS_INVALID_CID,
STATUS_REPLY_MESSAGE_MISMATCH
} END_VALIDATE;
}
#endif // DBG
}
void
LRPC_ADDRESS::EnumerateAndCallEachAssociation (
IN AssociationCallbackType asctType,
IN OUT void *Context OPTIONAL
)
/*++
Function Name: EnumerateAndCallEachAssociation
Parameters:
asctType - type of callback to make
Context - opaque memory block specific for the callback
type.
Description:
Common infrastructure for calling into each association
Returns:
--*/
{
LRPC_SASSOCIATION *CurrentAssociation;
BOOL CopyOfDictionaryUsed;
LRPC_SASSOCIATION_DICT AssocDictCopy;
LRPC_SASSOCIATION_DICT *AssocDictToUse;
BOOL Res;
DictionaryCursor cursor;
DestroyContextHandleCallbackContext *CallbackContext;
AddressMutex.Request();
CopyOfDictionaryUsed = AssocDictCopy.ExpandToSize(AssociationDictionary.Size());
if (CopyOfDictionaryUsed)
{
AssociationDictionary.Reset(cursor);
while ( (CurrentAssociation = AssociationDictionary.Next(cursor)) != 0 )
{
Res = AssocDictCopy.Insert(CurrentAssociation);
ASSERT(Res != -1);
// artifically add a count to keep it alive
// while we destroy the contexts
CurrentAssociation->AssociationReferenceCount++;
}
AddressMutex.Clear();
AssocDictToUse = &AssocDictCopy;
}
else
{
AssocDictToUse = &AssociationDictionary;
}
AssocDictToUse->Reset(cursor);
while ( (CurrentAssociation = AssocDictToUse->Next(cursor)) != 0 )
{
switch (asctType)
{
case asctDestroyContextHandle:
CallbackContext = (DestroyContextHandleCallbackContext *)Context;
// call into the association to destroy the context handles
CurrentAssociation->DestroyContextHandlesForInterface(
CallbackContext->RpcInterfaceInformation,
CallbackContext->RundownContextHandles);
break;
case asctCleanupIdleSContext:
CurrentAssociation->CleanupIdleSContexts();
break;
default:
ASSERT(0);
}
}
if (CopyOfDictionaryUsed)
{
while ( (CurrentAssociation = AssocDictCopy.Next(cursor)) != 0 )
{
// remove the extra refcounts
DereferenceAssociation(CurrentAssociation);
}
}
else
{
AddressMutex.Clear();
}
}
void
LRPC_ADDRESS::DestroyContextHandlesForInterface (
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation,
IN BOOL RundownContextHandles
)
/*++
Function Name: DestroyContextHandlesForInterface
Parameters:
RpcInterfaceInformation - the interface for which context handles
are to be unregistered
RundownContextHandles - if non-zero, rundown the context handles. If
FALSE, destroy the runtime portion of the context handle resource,
but don't call the user rundown routine.
Description:
The implementation for context handle destruction for the local RPC
(LRPC). Using the callback infrastructure it will walk the list of
associations, and for each one it will ask the association to
destroy the context handles for that interface.
Returns:
--*/
{
DestroyContextHandleCallbackContext CallbackContext;
CallbackContext.RpcInterfaceInformation = RpcInterfaceInformation;
CallbackContext.RundownContextHandles = RundownContextHandles;
EnumerateAndCallEachAssociation(asctDestroyContextHandle,
&CallbackContext);
}
void
LRPC_ADDRESS::CleanupIdleSContexts (
void
)
/*++
Function Name: CleanupIdleSContexts
Parameters:
Description:
The implementation for idle SContext cleanup for the local RPC
(LRPC). Using the callback infrastructure it will walk the list of
associations, and for each one it will ask the association to
destroy the idle scontexts
Returns:
--*/
{
LogEvent(SU_GC, EV_PRUNE, this, 0, 0, 0, 0);
EnumerateAndCallEachAssociation(asctCleanupIdleSContext,
NULL);
}
BOOL
LRPC_ADDRESS::PrepareForLoopbackTickling (
void
)
{
RPC_CHAR * LpcPortName;
int DirectoryNameLength;
int EndpointLength;
LrpcMutexVerifyOwned();
DirectoryNameLength = RpcpStringLength(LRPC_DIRECTORY_NAME);
EndpointLength = RpcpStringLength(InqEndpoint());
LpcPortName = new RPC_CHAR[
EndpointLength
+ DirectoryNameLength + 1];
if (LpcPortName == 0)
{
return FALSE;
}
TickleMessage = new LRPC_BIND_EXCHANGE;
if (TickleMessage == NULL)
{
delete LpcPortName;
return FALSE;
}
RpcpMemoryCopy(LpcPortName, LRPC_DIRECTORY_NAME,
DirectoryNameLength * sizeof(RPC_CHAR));
RpcpMemoryCopy(LpcPortName + DirectoryNameLength,
InqEndpoint(),
(EndpointLength + 1) * sizeof(RPC_CHAR));
RtlInitUnicodeString(&ThisAddressLoopbackString, LpcPortName);
return TRUE;
}
BOOL
LRPC_ADDRESS::LoopbackTickle (
void
)
{
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
HANDLE LoopbackPort;
ULONG TickleMessageLength = sizeof(LRPC_BIND_EXCHANGE);
NTSTATUS NtStatus;
ASSERT (IsPreparedForLoopbackTickling());
SecurityQualityOfService.EffectiveOnly = FALSE;
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
SecurityQualityOfService.ImpersonationLevel = SecurityAnonymous;
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
TickleMessage->ConnectType = LRPC_CONNECT_TICKLE ;
// TickleMessage->AssocKey = Key;
TickleMessage->Flags = 0;
NtStatus = NtConnectPort(
&LoopbackPort,
&ThisAddressLoopbackString,
&SecurityQualityOfService,
NULL,
NULL,
NULL,
TickleMessage,
&TickleMessageLength);
if (NtStatus == STATUS_PORT_CONNECTION_REFUSED)
return TRUE;
else
{
ASSERT(NtStatus != RPC_S_OK);
return FALSE;
}
}
LRPC_SASSOCIATION::LRPC_SASSOCIATION (
IN LRPC_ADDRESS * Address,
IN RPC_STATUS *Status
) : AssociationMutex(Status)
/*++
--*/
{
ObjectType = LRPC_SASSOCIATION_TYPE;
LpcServerPort = 0;
LpcReplyPort = 0 ;
this->Address = Address;
AssociationReferenceCount = 1;
Aborted = 0 ;
Deleted = -1 ;
if (*Status == RPC_S_OK)
{
CachedSCall = new LRPC_SCALL(Status);
if (CachedSCall == 0)
{
*Status = RPC_S_OUT_OF_MEMORY;
}
}
else
{
CachedSCall = NULL;
}
CachedSCallAvailable = 0;
fFirstCall = 0;
}
LRPC_SASSOCIATION::~LRPC_SASSOCIATION (
)
/*++
Routine Description:
We will call this routine when the client has notified us that this port
has closed, and there are no calls outstanding on it.
--*/
{
PVOID Buffer;
LRPC_SBINDING * Binding;
LRPC_SCALL *SCall ;
unsigned int Length ;
LRPC_SCONTEXT *SContext;
DictionaryCursor cursor;
while (SCall = (LRPC_SCALL *) FreeSCallQueue.TakeOffQueue(&Length))
{
delete SCall;
}
Bindings.Reset(cursor);
while ((Binding = Bindings.Next(cursor)) != 0)
{
delete Binding;
}
if (CachedSCall)
{
delete CachedSCall;
}
SContextDict.Reset(cursor);
while ((SContext = SContextDict.Next(cursor)) != 0)
{
delete SContext;
}
}
RPC_STATUS
LRPC_SASSOCIATION::AllocateSCall (
IN LRPC_MESSAGE * LrpcMessage,
IN LRPC_MESSAGE * LrpcReplyMessage,
IN unsigned int Flags,
IN LRPC_SCALL **SCall
)
/*++
Routine Description:
Allocate an SCall
Arguments:
LrpcMessage - Request message
LrpcReplyMessage - Reply message
Flags - Request flags
Return Value:
Pointer to the SCall
--*/
{
unsigned int Length ;
RPC_STATUS Status ;
LRPC_SCALL *NewSCall;
*SCall = NULL;
if (InterlockedIncrement(&CachedSCallAvailable) == 1)
{
NewSCall = CachedSCall;
}
else
{
AssociationMutex.Request() ;
NewSCall = (LRPC_SCALL *) FreeSCallQueue.TakeOffQueue(&Length) ;
AssociationMutex.Clear() ;
if (NewSCall == 0)
{
NewSCall = new LRPC_SCALL(&Status) ;
if (NewSCall == 0)
{
return RPC_S_OUT_OF_MEMORY;
}
if (Status != RPC_S_OK)
{
delete NewSCall;
return Status;
}
}
}
Status = NewSCall->ActivateCall(this,
LrpcMessage,
LrpcReplyMessage,
Flags) ;
if ((Flags & LRPC_BUFFER_PARTIAL)
|| NewSCall->IsClientAsync())
{
Status = NewSCall->SetupCall() ;
if (Status != RPC_S_OK)
{
if (NewSCall != CachedSCall)
{
delete NewSCall ;
}
return RPC_S_OUT_OF_MEMORY ;
}
}
LogEvent(SU_SCALL, EV_CREATE, NewSCall, 0, Flags, 1);
*SCall = NewSCall;
return RPC_S_OK;
}
void
LRPC_SASSOCIATION::FreeSCall (
IN LRPC_SCALL *SCall
)
/*++
Routine Description:
Free the SCall
Arguments:
SCall - Pointer to the SCall object
--*/
{
ASSERT(SCall->pAsync != (PRPC_ASYNC_STATE) -1);
if (SCall->pAsync)
{
SCall->DoPostDispatchProcessing();
}
if (SCall->SBinding
&& SCall->SBinding->RpcInterface->IsAutoListenInterface())
{
SCall->SBinding->RpcInterface->EndAutoListenCall() ;
}
if (SCall->ReceiveEvent)
{
AssociationMutex.Request() ;
SCallDict.Delete(ULongToPtr(SCall->CallId));
AssociationMutex.Clear() ;
}
LogEvent(SU_SCALL, EV_DELETE, SCall, SCall->pAsync, SCall->Flags, 1);
SCall->pAsync = (PRPC_ASYNC_STATE) -1;
if (SCall->SContext)
{
SCall->SContext->RemoveReference();
}
if (SCall->ClientPrincipalName != NULL)
{
delete SCall->ClientPrincipalName;
SCall->ClientPrincipalName = NULL;
}
SCall->DeactivateCall();
if (SCall == CachedSCall)
{
CachedSCallAvailable = 0;
}
else
{
AssociationMutex.Request() ;
SCall->pAsync = (PRPC_ASYNC_STATE) -1;
if (FreeSCallQueue.PutOnQueue(SCall, 0))
delete SCall ;
AssociationMutex.Clear() ;
}
}
int
LRPC_SASSOCIATION::MaybeQueueSCall (
IN LRPC_SCALL *SCall
)
/*++
Routine Description:
if the thread is currently executing a call, the call
is queued up, otherwise it is signalled to be dispatched.
Arguments:
SCall - the SCall to be dispatched.
Return Value:
0: dispatch the call
1: don't dispatch the call
-1: error
--*/
{
LRPC_SCALL *FirstSCall ;
int Status ;
AssociationMutex.Request() ;
FirstSCall = ClientThreadDict.Find(
MsgClientIdToClientId(SCall->LrpcRequestMessage->Rpc.LpcHeader.ClientId).UniqueThread) ;
if (FirstSCall == 0)
{
Status = ClientThreadDict.Insert(
MsgClientIdToClientId(SCall->LrpcRequestMessage->Rpc.LpcHeader.ClientId).UniqueThread,
SCall) ;
SCall->LastSCall = SCall ;
AssociationMutex.Clear() ;
VALIDATE(Status)
{
0,
-1
} END_VALIDATE;
return Status ;
}
ASSERT(FirstSCall->LastSCall);
FirstSCall->LastSCall->NextSCall = SCall ;
FirstSCall->LastSCall = SCall ;
AssociationMutex.Clear() ;
return 1 ;
}
LRPC_SCALL *
LRPC_SASSOCIATION::GetNextSCall (
IN LRPC_SCALL *SCall
)
/*++
Routine Description:
description
Arguments:
SCall - description
Return Value:
--*/
{
LRPC_SCALL *NextSCall ;
ASSERT(SCall) ;
AssociationMutex.Request() ;
NextSCall = SCall->NextSCall ;
if (NextSCall != 0)
{
ASSERT(SCall->LastSCall);
NextSCall->LastSCall = SCall->LastSCall ;
ClientThreadDict.Update (
MsgClientIdToClientId(SCall->LrpcRequestMessage->Rpc.LpcHeader.ClientId).UniqueThread,
NextSCall) ;
}
else
{
ClientThreadDict.Delete (
MsgClientIdToClientId(SCall->LrpcRequestMessage->Rpc.LpcHeader.ClientId).UniqueThread) ;
}
AssociationMutex.Clear() ;
return NextSCall ;
}
void
LRPC_SASSOCIATION::Delete(
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
LRPC_SCALL *SCall ;
DictionaryCursor cursor;
if (InterlockedIncrement(&Deleted) == 0)
{
AssociationMutex.Request() ;
SCallDict.Reset(cursor) ;
while ((SCall = SCallDict.Next(cursor)) != 0)
{
SCall->Deleted = 1;
if (SCall->ReceiveEvent)
{
SCall->ReceiveEvent->Raise();
}
}
AssociationMutex.Clear() ;
LogEvent(SU_SASSOC, EV_DELETE,
this, 0, AssociationReferenceCount, 1, 1);
Address->DereferenceAssociation(this);
}
}
RPC_STATUS
LRPC_SASSOCIATION::BindBack (
IN RPC_CHAR *Endpoint,
IN DWORD AssocKey
)
/*++
Routine Description:
Create a back connection to the client.
Arguments:
LrpcThread - LrpcThread to connect to.
pAssoc - Pointer to client association.
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
NTSTATUS NtStatus;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
RPC_CHAR * LpcPortName ;
UNICODE_STRING unicodePortName;
LRPC_BIND_EXCHANGE BindExchange;
unsigned long BindExchangeLength = sizeof(LRPC_BIND_EXCHANGE);
LpcPortName = new RPC_CHAR[RpcpStringLength(Endpoint)
+ RpcpStringLength(LRPC_DIRECTORY_NAME) + 1];
if (LpcPortName == 0)
{
#if DBG
PrintToDebugger("LRPC: Out of memory in DealWithNewClient\n") ;
#endif
return RPC_S_OUT_OF_MEMORY ;
}
RpcpMemoryCopy(LpcPortName,
LRPC_DIRECTORY_NAME,
RpcpStringLength(LRPC_DIRECTORY_NAME) * sizeof(RPC_CHAR));
RpcpMemoryCopy(LpcPortName + RpcpStringLength(LRPC_DIRECTORY_NAME),
Endpoint,
(RpcpStringLength(Endpoint) + 1) * sizeof(RPC_CHAR));
RtlInitUnicodeString(&unicodePortName, LpcPortName);
// Hack Hack, where do I get the real QOS values from ??
SecurityQualityOfService.EffectiveOnly = TRUE;
SecurityQualityOfService.ContextTrackingMode = SECURITY_STATIC_TRACKING;
SecurityQualityOfService.ImpersonationLevel = SecurityAnonymous;
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
BindExchange.ConnectType = LRPC_CONNECT_RESPONSE ;
BindExchange.AssocKey = AssocKey ;
NtStatus = NtConnectPort(&LpcReplyPort,
&unicodePortName,
&SecurityQualityOfService,
0,
0,
0,
&BindExchange,
&BindExchangeLength);
delete LpcPortName ;
if (!NT_SUCCESS(NtStatus))
{
#if DBG
PrintToDebugger("LRPC: NtConnectPort : %lx\n", NtStatus);
#endif // DBG
return RPC_S_OUT_OF_MEMORY ;
}
return RPC_S_OK ;
}
LRPC_MESSAGE *
LRPC_SASSOCIATION::DealWithBindBackMessage (
IN LRPC_MESSAGE *BindBackMessage
)
/*++
Routine Description:
Used in conjuction with Async RPC. This function
creates a back connection to the client so that two asynchronous
flow of data can occur.
Arguments:
BindBackMessage - The message receive from the client
Return Value:
reply message.
--*/
{
RPC_STATUS Status ;
BindBackMessage->BindBack.szPortName[PORT_NAME_LEN-1] = NULL;
Status = BindBack((RPC_CHAR *) BindBackMessage->BindBack.szPortName,
BindBackMessage->BindBack.AssocKey) ;
BindBackMessage->Ack.MessageType = LRPC_MSG_ACK ;
BindBackMessage->Ack.RpcStatus = Status ;
BindBackMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_BIND_MESSAGE) - sizeof(PORT_MESSAGE);
BindBackMessage->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_BIND_MESSAGE);
if (Status != RPC_S_OK)
{
Delete() ;
}
return BindBackMessage ;
}
RPC_STATUS
LRPC_SASSOCIATION::AddBinding (
IN OUT LRPC_BIND_EXCHANGE * BindExchange
)
/*++
Routine Description:
We will attempt to add a new binding to this association.
Arguments:
BindExchange - Supplies a description of the interface to which the
client wish to bind.
Return Value:
--*/
{
RPC_STATUS Status;
RPC_SYNTAX_IDENTIFIER TransferSyntax;
RPC_INTERFACE * RpcInterface;
LRPC_SBINDING * Binding;
BOOL fIgnored;
int DictKey;
RPC_SYNTAX_IDENTIFIER ProposedSyntaxes[MaximumNumberOfTransferSyntaxes];
int PresentationContexts[MaximumNumberOfTransferSyntaxes];
int TransferSyntaxFlagSettings[MaximumNumberOfTransferSyntaxes];
int NextProposedSyntax;
int ChosenProposedTransferSyntax;
int ChosenAvailableTransferSyntax;
NextProposedSyntax = 0;
if (BindExchange->TransferSyntaxSet & TS_NDR20_FLAG)
{
RpcpMemoryCopy(&ProposedSyntaxes[NextProposedSyntax],
NDR20TransferSyntax, sizeof(RPC_SYNTAX_IDENTIFIER));
PresentationContexts[NextProposedSyntax] = (int)BindExchange->PresentationContext[0];
TransferSyntaxFlagSettings[NextProposedSyntax] = TS_NDR20_FLAG;
NextProposedSyntax ++;
}
if (BindExchange->TransferSyntaxSet & TS_NDR64_FLAG)
{
RpcpMemoryCopy(&ProposedSyntaxes[NextProposedSyntax],
NDR64TransferSyntax, sizeof(RPC_SYNTAX_IDENTIFIER));
PresentationContexts[NextProposedSyntax] = (int)BindExchange->PresentationContext[1];
TransferSyntaxFlagSettings[NextProposedSyntax] = TS_NDR64_FLAG;
NextProposedSyntax ++;
}
if (BindExchange->TransferSyntaxSet & TS_NDRTEST_FLAG)
{
RpcpMemoryCopy(&ProposedSyntaxes[NextProposedSyntax],
NDRTestTransferSyntax, sizeof(RPC_SYNTAX_IDENTIFIER));
PresentationContexts[NextProposedSyntax] = (int)BindExchange->PresentationContext[2];
TransferSyntaxFlagSettings[NextProposedSyntax] = TS_NDRTEST_FLAG;
NextProposedSyntax ++;
}
if (NextProposedSyntax == 0)
{
// no syntaxes proposed - protocol error
ASSERT(0);
return RPC_S_PROTOCOL_ERROR;
}
ASSERT(NextProposedSyntax <= MaximumNumberOfTransferSyntaxes);
Status = Address->FindInterfaceTransfer(&(BindExchange->InterfaceId),
ProposedSyntaxes,
NextProposedSyntax,
&TransferSyntax,
&RpcInterface,
&fIgnored,
&ChosenProposedTransferSyntax,
&ChosenAvailableTransferSyntax);
if (Status != RPC_S_OK)
{
return(Status);
}
ASSERT (ChosenProposedTransferSyntax < NextProposedSyntax);
Binding = new LRPC_SBINDING(RpcInterface,
ChosenAvailableTransferSyntax);
if (Binding == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
Binding->SetPresentationContext(PresentationContexts[ChosenProposedTransferSyntax]);
DictKey = (unsigned char) Bindings.Insert(Binding);
if (DictKey == -1)
{
delete Binding;
return(RPC_S_OUT_OF_MEMORY);
}
BindExchange->TransferSyntaxSet = TransferSyntaxFlagSettings[ChosenProposedTransferSyntax];
return(RPC_S_OK);
}
RPC_STATUS
LRPC_SASSOCIATION::SaveToken (
IN LRPC_MESSAGE *LrpcMessage,
OUT HANDLE *pTokenHandle,
IN BOOL fRestoreToken
)
/*++
Routine Description:
Impersonate the client and save away the token.
Arguments:
LrpcMessage - request message
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
NTSTATUS NtStatus ;
HANDLE ImpersonationToken = 0;
RPC_STATUS Status;
if (fRestoreToken)
{
//
// Save away the old token
//
if (OpenThreadToken (GetCurrentThread(),
TOKEN_IMPERSONATE | TOKEN_QUERY,
TRUE,
&ImpersonationToken) == FALSE)
{
ImpersonationToken = 0;
#if DBG
if (GetLastError() != ERROR_NO_TOKEN)
{
PrintToDebugger("LRPC: OpenThreadToken failed %d\n", GetLastError());
}
#endif
}
}
NtStatus = NtImpersonateClientOfPort(LpcServerPort,
(PORT_MESSAGE *) LrpcMessage);
if (NT_ERROR(NtStatus))
{
#if DBG
PrintToDebugger("LRPC: NtImpersonateClientOfPort failed: 0x%lX\n",
NtStatus) ;
#endif
return RPC_S_INVALID_AUTH_IDENTITY ;
}
Status = RPC_S_OK;
if (OpenThreadToken (GetCurrentThread(),
TOKEN_IMPERSONATE | TOKEN_QUERY,
TRUE,
pTokenHandle) == FALSE)
{
*pTokenHandle = 0;
if (GetLastError() == ERROR_CANT_OPEN_ANONYMOUS)
{
Status = ERROR_CANT_OPEN_ANONYMOUS;
}
else
{
#if DBG
PrintToDebugger("LRPC: OpenThreadToken failed\n") ;
#endif
}
}
if (fRestoreToken)
{
//
// Restore the token
//
NtStatus = NtSetInformationThread(NtCurrentThread(),
ThreadImpersonationToken,
&ImpersonationToken,
sizeof(HANDLE));
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtSetInformationThread : %lx\n", NtStatus);
}
#endif // DBG
if (ImpersonationToken)
{
CloseHandle(ImpersonationToken);
}
}
return Status;
}
RPC_STATUS
LRPC_SASSOCIATION::GetClientName (
IN LRPC_SCALL *SCall,
IN OUT ULONG *ClientPrincipalNameBufferLength OPTIONAL, // in bytes
OUT RPC_CHAR **ClientPrincipalName
)
/*++
Routine Description:
Gets the client name for the given scall
Arguments:
SCall - the SCall for which to get the client name
ClientPrincipalNameBufferLength - if present, *ClientPrincipalName must
point to a caller supplied buffer, which if big enough,
will be filled with the client principal name. If not present,
*ClientPrincipalName must be NULL.
ClientPrincipalName - see ClientPrincipalNameBufferLength
Return Value:
RPC_S_OK for success, or RPC_S_* / Win32 error code for error.
--*/
{
RPC_STATUS Status = RPC_S_OK;
BOOL Result;
unsigned long Size;
HANDLE TokenHandle = 0;
LRPC_SCONTEXT *SContext = 0;
TOKEN_STATISTICS TokenStatisticsInformation;
DictionaryCursor cursor;
BOOL fAnonymous;
BOOL fMutexHeld = FALSE;
BOOL fAssociationSContextUsed = FALSE;
RPC_CHAR *CurrentUserName;
ULONG CurrentUserNameLength;
if (SCall->SContext == NULL)
{
// take the lock opportunistically
AssociationMutex.Request();
fMutexHeld = TRUE;
if (SCall->SContext == NULL)
{
Status = SaveToken(
SCall->LrpcRequestMessage,
&TokenHandle, 1);
if ((Status != RPC_S_OK) && (Status != ERROR_CANT_OPEN_ANONYMOUS))
{
goto Cleanup;
}
if (Status == RPC_S_OK)
{
Result = GetTokenInformation(
TokenHandle,
TokenStatistics,
&TokenStatisticsInformation,
sizeof(TokenStatisticsInformation),
&Size
);
if (Result != TRUE)
{
Status = RPC_S_INVALID_AUTH_IDENTITY;
goto Cleanup;
}
fAnonymous = FALSE;
}
else
{
ASSERT(Status == ERROR_CANT_OPEN_ANONYMOUS);
fAnonymous = TRUE;
TokenHandle = 0;
}
SContextDict.Reset(cursor);
while ((SContext = SContextDict.Next(cursor)) != 0)
{
// if either input and found are anonymous, or the modified
// ids match, we have found it
if ((fAnonymous && SContext->GetAnonymousFlag())
||
FastCompareLUIDAligned(&SContext->ClientLuid,
&TokenStatisticsInformation.ModifiedId))
{
break;
}
}
if (SContext == 0)
{
SContext = new LRPC_SCONTEXT(NULL,
fAnonymous ? NULL : ((LUID *) &TokenStatisticsInformation.ModifiedId),
this,
FALSE, // fDefaultLogonId
fAnonymous
);
if (SContext == 0)
{
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
if (SContextDict.Insert(SContext) == -1)
{
delete SContext;
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
// mark the context as server side only
SContext->SetServerSideOnlyFlag();
// record that we have used this recently to prevent it from being
// garbage collected
SContext->UpdateLastAccessTime();
EnableIdleLrpcSContextsCleanup();
// tell the garbage collector that we have something to be
// collected
GarbageCollectionNeeded(FALSE, // fOneTimeCleanup
LRPC_SCONTEXT::CONTEXT_IDLE_TIMEOUT);
}
else
{
// record that we have used this recently to prevent it from being
// garbage collected
SContext->UpdateLastAccessTime();
}
// we have taken or created the current SContext in the association
// we need to prevent the garbage collection thread from destroying
// it underneath us. We add one refcount for the purpose and record
// this
SContext->AddReference();
fAssociationSContextUsed = TRUE;
}
else
{
SContext = SCall->SContext;
// record that we have used this recently to prevent it from being
// garbage collected
SContext->UpdateLastAccessTime();
}
AssociationMutex.Clear() ;
fMutexHeld = FALSE;
}
else
{
SContext = SCall->SContext;
// record that we have used this recently to prevent it from being
// garbage collected
SContext->UpdateLastAccessTime();
}
ASSERT(SContext);
// if we go through the path where the token is retrieved from
// the SContext, passing NULL TokenHandle to get user name is Ok
// as it will retrieve the token from the SContext
Status = SContext->GetUserName(ClientPrincipalNameBufferLength, ClientPrincipalName, TokenHandle);
// If ARGUMENT_PRESENT(ClientPrincipalNameBufferLength), Status may be
// ERROR_MORE_DATA, which is a success error code.
if (fAssociationSContextUsed)
{
if ((Status == RPC_S_OK)
&& (!ARGUMENT_PRESENT(ClientPrincipalNameBufferLength)))
{
// we weren't supplied a user buffer. Copy the principal
// name to a call variable to avoid the garbage collector
// collecting this under the feet of our caller. Then
// we can release the refcount
if (SCall->ClientPrincipalName == NULL)
{
CurrentUserNameLength = (RpcpStringLength(*ClientPrincipalName) + 1) * sizeof(RPC_CHAR);
// CurrentUserNameLength is in bytes. Allocate chars for it and cast it back
CurrentUserName = (RPC_CHAR *) new char [CurrentUserNameLength];
if (CurrentUserName != NULL)
{
RpcpMemoryCopy(CurrentUserName,
*ClientPrincipalName,
CurrentUserNameLength);
SCall->ClientPrincipalName = CurrentUserName;
*ClientPrincipalName = CurrentUserName;
}
else
{
Status = RPC_S_OUT_OF_MEMORY;
// fall through in cleanup path
}
}
else
{
*ClientPrincipalName = SCall->ClientPrincipalName;
}
}
// succeeded or not, drop the refcount
SContext->RemoveReference();
}
if (Status != RPC_S_OK)
{
// N.B. failure of this function doesn't mean we have
// to delete a newly created scontext. scontexts without
// names are perfectly valid, and since we know the only
// missing part from this scontext is the name, we can
// leave it alone, return failure, and attempt to get the
// name next time
goto Cleanup;
}
Cleanup:
if (fMutexHeld)
{
AssociationMutex.Clear() ;
}
if (TokenHandle)
{
CloseHandle(TokenHandle);
}
return Status;
}
#if defined(_WIN64)
C_ASSERT((FIELD_OFFSET(TOKEN_STATISTICS, ModifiedId) % 8) == 0);
C_ASSERT((FIELD_OFFSET(LRPC_SCONTEXT, ClientLuid) % 8) == 0);
#endif
void
LRPC_SASSOCIATION::DealWithBindMessage (
IN LRPC_MESSAGE * LrpcMessage
)
/*++
Routine Description:
LRPC_ADDRESS::ReceiveLotsaCalls will call this routine when the client
sends a bind message. We need to process the bind message, and send
a response to the client.
Arguments:
LrpcMessage - Supplies the bind message. We will also use this to send
the response.
Return Value:
The reply message to be sent to the client will be returned.
--*/
{
RPC_STATUS Status = RPC_S_OK;
NTSTATUS NtStatus ;
HANDLE ImpersonationToken = 0;
HANDLE TokenHandle;
unsigned long Size;
BOOL Result;
LRPC_SCONTEXT *SContext;
ULONG SecurityContextId = -1;
DictionaryCursor cursor;
BOOL fBindDefaultLogonId;
BOOL fAnonymous;
if (LrpcMessage->Bind.BindExchange.Flags & NEW_SECURITY_CONTEXT_FLAG)
{
TOKEN_STATISTICS TokenStatisticsInformation;
//
// If SaveToken succeeds, as a side-effect, it will
// fill in the SecurityContextId field of the BindExchange
//
Status = SaveToken(
LrpcMessage,
&TokenHandle) ;
if ((Status != RPC_S_OK) && (Status != ERROR_CANT_OPEN_ANONYMOUS))
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLDealWithBindMessage10);
goto Cleanup;
}
if (TokenHandle || (Status == ERROR_CANT_OPEN_ANONYMOUS))
{
if (TokenHandle)
{
Result = GetTokenInformation(
TokenHandle,
TokenStatistics,
&TokenStatisticsInformation,
sizeof(TokenStatisticsInformation),
&Size
);
if (Result != TRUE)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_INVALID_AUTH_IDENTITY,
EEInfoDLDealWithBindMessage20,
GetLastError());
CloseHandle(TokenHandle);
Status = RPC_S_INVALID_AUTH_IDENTITY;
goto Cleanup;
}
fAnonymous = FALSE;
}
else
{
fAnonymous = TRUE;
Status = RPC_S_OK;
}
AssociationMutex.Request();
int Key = 0;
fBindDefaultLogonId =
(LrpcMessage->Bind.BindExchange.Flags & DEFAULT_LOGONID_FLAG)
? TRUE : FALSE;
SContextDict.Reset(cursor);
while ((SContext = SContextDict.NextWithKey(cursor, &Key)) != 0)
{
if ((fAnonymous && SContext->GetAnonymousFlag())
||
(FastCompareLUIDAligned(&SContext->ClientLuid,
&TokenStatisticsInformation.ModifiedId)
&&
(SContext->GetDefaultLogonIdFlag() == fBindDefaultLogonId)))
{
SecurityContextId = Key;
SContext->ClearServerSideOnlyFlag();
break;
}
}
if (SContext == 0)
{
if (fAnonymous)
{
SContext = new LRPC_SCONTEXT(TokenHandle,
(LUID *) NULL,
this,
0,
fAnonymous);
}
else
{
SContext = new LRPC_SCONTEXT(TokenHandle,
(LUID *) &TokenStatisticsInformation.ModifiedId,
this,
fBindDefaultLogonId,
0 // fAnonymousToken
);
}
if (SContext == 0)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_OUT_OF_MEMORY,
EEInfoDLDealWithBindMessage30,
sizeof(LRPC_SCONTEXT));
CloseHandle(TokenHandle);
Status = RPC_S_OUT_OF_MEMORY;
AssociationMutex.Clear();
goto Cleanup;
}
if ((SecurityContextId = SContextDict.Insert(SContext)) == -1)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_OUT_OF_MEMORY,
EEInfoDLDealWithBindMessage40);
delete SContext;
Status = RPC_S_OUT_OF_MEMORY;
AssociationMutex.Clear();
goto Cleanup;
}
}
else if (SContext->hToken == NULL)
{
// if the context had no token, add one. This can happen
// if previous callers for this modified id just queried
// the user name. In this case, we won't cache the token
SContext->hToken = TokenHandle;
}
else
{
CloseHandle(TokenHandle);
}
AssociationMutex.Clear();
}
Cleanup:
//
// Revert
//
NtStatus = NtSetInformationThread(NtCurrentThread(),
ThreadImpersonationToken,
&ImpersonationToken,
sizeof(HANDLE));
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtSetInformationThread : %lx\n", NtStatus);
}
#endif // DBG
}
if (Status == RPC_S_OK
&& LrpcMessage->Bind.BindExchange.Flags & NEW_PRESENTATION_CONTEXT_FLAG)
{
Status = AddBinding(&(LrpcMessage->Bind.BindExchange));
if (Status != RPC_S_OK)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLDealWithBindMessage50);
}
}
LrpcMessage->Bind.BindExchange.RpcStatus = Status ;
if (LrpcMessage->Bind.OldSecurityContexts.NumContexts > 0)
{
DWORD i;
LRPC_SCONTEXT *SContext;
DWORD NumContexts = LrpcMessage->Bind.OldSecurityContexts.NumContexts;
DWORD CalculatedSize = ((NumContexts-1) * sizeof(DWORD))+sizeof(LRPC_BIND_MESSAGE);
if (NumContexts > MAX_LRPC_CONTEXTS
|| CalculatedSize > (DWORD) LrpcMessage->LpcHeader.u1.s1.TotalLength)
{
//
// Bogus request
//
LrpcMessage->Bind.BindExchange.RpcStatus = RPC_S_PROTOCOL_ERROR;
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_PROTOCOL_ERROR,
EEInfoDLDealWithBindMessage60,
NumContexts,
CalculatedSize,
(DWORD) LrpcMessage->LpcHeader.u1.s1.TotalLength);
goto Reply;
}
AssociationMutex.Request();
for (i = 0; i < NumContexts; i++)
{
SContext = SContextDict.Delete(
LrpcMessage->Bind.OldSecurityContexts.SecurityContextId[i]);
if (SContext)
{
SContext->Destroy();
}
else
{
ASSERT(0);
}
}
AssociationMutex.Clear();
}
Reply:
// if failure, check out of EEInfo
if ((LrpcMessage->Bind.BindExchange.RpcStatus != RPC_S_OK) && (g_fSendEEInfo))
{
SetBindAckFault(LrpcMessage,
LrpcMessage->Bind.BindExchange.RpcStatus);
}
LrpcMessage->Bind.MessageType = LRPC_BIND_ACK ;
LrpcMessage->Bind.BindExchange.SecurityContextId = SecurityContextId;
if (!(LrpcMessage->Bind.BindExchange.Flags & EXTENDED_ERROR_INFO_PRESENT))
{
LrpcMessage->LpcHeader.u1.s1.DataLength = sizeof(LRPC_BIND_MESSAGE)
- sizeof(PORT_MESSAGE);
}
ReplyMessage(LrpcMessage);
}
RPC_STATUS LRPC_SASSOCIATION::CreateThread(void)
{
RPC_STATUS status;
status = Address->BeginLongCall();
if (status != RPC_S_OK)
{
Address->EndLongCall();
}
return status;
}
void LRPC_SASSOCIATION::RundownNotificationCompleted(void)
{
Address->EndLongCall();
}
RPC_STATUS
LRPC_SBINDING::CheckSecurity (
SCALL * Context
)
{
if ( (RpcInterface->SequenceNumber == SequenceNumber)
|| (RpcInterface->IsSecurityCallbackReqd() == 0))
{
return (RPC_S_OK);
}
RPC_STATUS Status = RpcInterface->CheckSecurityIfNecessary(Context);
NukeStaleEEInfoIfNecessary(Status);
Context->RevertToSelf();
if (Status == RPC_S_OK)
{
SequenceNumber = RpcInterface->SequenceNumber ;
return (RPC_S_OK);
}
else
{
SequenceNumber = 0;
RpcpErrorAddRecord(EEInfoGCApplication,
RPC_S_ACCESS_DENIED,
EEInfoDLCheckSecurity10,
Status);
return (RPC_S_ACCESS_DENIED);
}
}
void
LRPC_SCALL::DealWithRequestMessage (
)
/*++
Routine Description:
We will process the original request message in this routine, dispatch
the remote procedure call to the stub, and then send the response
message.
Arguments:
RpcMessage - Contains the request buffer
Return Value:
none
--*/
{
RPC_STATUS Status, ExceptionCode;
int Flags = LrpcRequestMessage->Rpc.RpcHeader.Flags ;
LRPC_SBINDING *LrpcBinding ;
THREAD *ThisThread;
DebugThreadInfo *ThreadDebugCell;
DebugCallInfo *CallDebugCell;
ULONG TickCount;
PRPC_DISPATCH_TABLE DispatchTableToUse;
RuntimeInfo.Length = sizeof(RPC_RUNTIME_INFO) ;
ClientId = MsgClientIdToClientId(LrpcRequestMessage->LpcHeader.ClientId);
MessageId = LrpcRequestMessage->LpcHeader.MessageId;
CallbackId = LrpcRequestMessage->LpcHeader.CallbackId;
LrpcBinding = LookupBinding(
LrpcRequestMessage->Rpc.RpcHeader.PresentContext);
if (LrpcBinding == 0)
{
COPYMSG(LrpcReplyMessage, LrpcRequestMessage) ;
FreeBuffer(&RpcMessage);
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_UNKNOWN_IF,
EEInfoDLDealWithRequestMessage10,
LrpcRequestMessage->Rpc.RpcHeader.PresentContext);
SetFaultPacket(LrpcReplyMessage, RPC_S_UNKNOWN_IF, Flags, NULL);
return;
}
SBinding = LrpcBinding;
if (SBinding->RpcInterface->IsAutoListenInterface())
{
LrpcBinding->RpcInterface->BeginAutoListenCall() ;
}
LrpcBinding->GetSelectedTransferSyntaxAndDispatchTable(&RpcMessage.TransferSyntax,
&DispatchTableToUse);
RpcMessage.ProcNum = LrpcRequestMessage->Rpc.RpcHeader.ProcedureNumber;
RpcMessage.Handle = this;
RpcMessage.ReservedForRuntime = &RuntimeInfo ;
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
RpcMessage.DataRepresentation = 0x00 | 0x10 | 0x0000;
if ((LrpcRequestMessage->Rpc.RpcHeader.Flags & LRPC_OBJECT_UUID))
{
ObjectUuidFlag = 1;
RpcpMemoryCopy(&ObjectUuid,
&(LrpcRequestMessage->Rpc.RpcHeader.ObjectUuid), sizeof(UUID));
}
ThisThread = RpcpGetThreadPointer();
ASSERT(ThisThread);
RpcpSetThreadContextWithThread(ThisThread, this);
ThreadDebugCell = ThisThread->DebugCell;
//
// Check IF Level Security
//
if (LrpcBinding->RpcInterface->IsSecurityCallbackReqd() != 0)
{
Status = LrpcBinding->CheckSecurity(this);
if (Status != RPC_S_OK)
{
COPYMSG(LrpcReplyMessage, LrpcRequestMessage) ;
FreeBuffer(&RpcMessage);
// the error record (if any) was already added
// by CheckSecurity
SetFaultPacket(LrpcReplyMessage,
RPC_S_ACCESS_DENIED,
Flags,
NULL) ;
RpcpSetThreadContextWithThread(ThisThread, 0) ;
return;
}
}
if (ThreadDebugCell)
{
TickCount = NtGetTickCount();
ThreadDebugCell->Status = dtsDispatched;
ThreadDebugCell->LastUpdateTime = TickCount;
CallDebugCell = DebugCell;
CallDebugCell->InterfaceUUIDStart = LrpcBinding->RpcInterface->GetInterfaceFirstDWORD();
CallDebugCell->CallID = CallId;
CallDebugCell->LastUpdateTime = TickCount;
// shoehorn the PID and TID into shorts - most of the time
// it doesn't actually truncate important information
CallDebugCell->PID = (USHORT)ClientId.UniqueProcess;
CallDebugCell->TID = (USHORT)ClientId.UniqueThread;
CallDebugCell->ProcNum = (unsigned short)RpcMessage.ProcNum;
CallDebugCell->Status = csDispatched;
GetDebugCellIDFromDebugCell((DebugCellUnion *)ThreadDebugCell,
&ThisThread->DebugCellTag, &CallDebugCell->ServicingTID);
if (LrpcBinding->RpcInterface->IsPipeInterface())
CallDebugCell->CallFlags |= DBGCELL_PIPE_CALL;
}
if (ObjectUuidFlag != 0)
{
Status = LrpcBinding->RpcInterface->DispatchToStubWithObject(
&RpcMessage,
&ObjectUuid,
0,
DispatchTableToUse,
&ExceptionCode);
}
else
{
Status = LrpcBinding->RpcInterface->DispatchToStub(
&RpcMessage,
0,
DispatchTableToUse,
&ExceptionCode);
}
RpcpSetThreadContextWithThread(ThisThread, 0);
LRPC_SCALL::RevertToSelf();
if (ThreadDebugCell)
{
ThreadDebugCell->Status = dtsProcessing;
ThreadDebugCell->LastUpdateTime = NtGetTickCount();
}
if (Status != RPC_S_OK)
{
if (Status == RPC_P_EXCEPTION_OCCURED)
{
SetFaultPacket(LrpcReplyMessage,
LrpcMapRpcStatus(ExceptionCode),
Flags,
this) ;
}
else
{
VALIDATE(Status)
{
RPC_S_PROCNUM_OUT_OF_RANGE,
RPC_S_UNKNOWN_IF,
RPC_S_NOT_LISTENING,
RPC_S_SERVER_TOO_BUSY,
RPC_S_UNSUPPORTED_TYPE
} END_VALIDATE;
if (Status == RPC_S_NOT_LISTENING)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLDealWithRequestMessage20);
Status = RPC_S_SERVER_TOO_BUSY;
}
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLDealWithRequestMessage30);
SetFaultPacket(LrpcReplyMessage,
LrpcMapRpcStatus(Status),
Flags,
this);
}
if (IsSyncCall())
{
INITMSG(LrpcReplyMessage,
ClientId,
CallbackId,
MessageId) ;
}
else
{
if (Flags & LRPC_NON_PIPE)
{
INITMSG(LrpcReplyMessage,
ClientId,
CallbackId,
MessageId) ;
Association->ReplyMessage(LrpcReplyMessage);
}
else
{
if ((LrpcReplyMessage->Rpc.RpcHeader.MessageType != LRPC_MSG_FAULT2)
|| (!IsClientAsync()))
{
SendDGReply(LrpcReplyMessage);
}
}
RemoveReference();
}
}
else
{
//
// The rest of the response headers are set in ::GetBuffer.
//
LrpcReplyMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE;
}
}
void
LRPC_SCALL::SendReply (
)
{
RPC_STATUS Status;
BOOL Shutup ;
LRPC_SASSOCIATION *LocalAssociation;
if (IsSyncCall())
{
if (IsClientAsync())
{
if (LrpcReplyMessage->Fault.RpcHeader.MessageType == LRPC_MSG_FAULT)
{
SendDGReply(LrpcReplyMessage);
}
else
{
RpcMessage.RpcFlags = 0;
Status = SendRequest(&RpcMessage, &Shutup) ;
if (Status != RPC_S_OK)
{
#if DBG
PrintToDebugger("RPC: SendRequest failed: %d\n", Status);
#endif
Association->Delete();
}
}
}
else
{
INITMSG(LrpcReplyMessage,
ClientId,
CallbackId,
MessageId) ;
Association->ReplyMessage(LrpcReplyMessage);
}
FreeMessage(LrpcRequestMessage) ;
LocalAssociation = Association;
Association->FreeSCall(this) ;
// don't touch the this pointer after FreeSCall - it may be freed
LocalAssociation->Address->DereferenceAssociation(LocalAssociation);
}
else
{
if ((LrpcReplyMessage->Rpc.RpcHeader.MessageType != LRPC_MSG_FAULT2)
|| (!IsClientAsync()))
{
RemoveReference();
}
else
{
BOOL Shutup;
RpcMessage.RpcFlags = 0;
Status = SendRequest(&RpcMessage, &Shutup) ;
if (Status != RPC_S_OK)
{
#if DBG
PrintToDebugger("RPC: SendRequest failed: %d\n", Status);
#endif
Association->Delete();
}
}
}
}
LRPC_MESSAGE *
LRPC_SASSOCIATION::DealWithCopyMessage (
IN LRPC_COPY_MESSAGE * LrpcMessage
)
/*++
Routine Description:
We will process a copy message in this routine; this means that we need
to copy a buffer of data from the server into the client's address
space.
Arguments:
LrpcMessage - Supplies the copy message which was received from
the client.
Return Value:
The reply message to be sent to the client will be returned.
--*/
{
NTSTATUS NtStatus;
SIZE_T NumberOfBytesWritten;
PVOID Buffer;
ASSERT(LrpcMessage->IsPartial == 0);
AssociationMutex.Request() ;
// We need this only to prevent an attack
// Also, the pointer is to a server address. It is ok to just cast it
// to the server's pointer type and it won't hurt anything in the case
// of 32/64 bit LRPC.
Buffer = Buffers.DeleteItemByBruteForce(MsgPtrToPtr(LrpcMessage->Server.Buffer));
AssociationMutex.Clear() ;
if (LrpcMessage->RpcStatus == RPC_S_OK)
{
if (Buffer == 0)
{
LrpcMessage->RpcStatus = RPC_S_PROTOCOL_ERROR;
}
else
{
NtStatus = NtWriteRequestData(LpcServerPort,
(PORT_MESSAGE *) LrpcMessage,
0,
(PVOID) Buffer,
LrpcMessage->Server.Length,
&NumberOfBytesWritten);
if (NT_ERROR(NtStatus))
{
LrpcMessage->RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else
{
ASSERT(LrpcMessage->Server.Length == NumberOfBytesWritten);
LrpcMessage->RpcStatus = RPC_S_OK;
}
}
}
LrpcMessage->LpcHeader.u1.s1.DataLength = sizeof(LRPC_COPY_MESSAGE)
- sizeof(PORT_MESSAGE);
LrpcMessage->LpcHeader.u1.s1.TotalLength = sizeof(LRPC_COPY_MESSAGE);
if (Buffer != 0)
{
RpcpFarFree(Buffer);
}
return((LRPC_MESSAGE *) LrpcMessage);
}
LRPC_MESSAGE *
LRPC_SASSOCIATION::DealWithPartialRequest (
IN LRPC_MESSAGE **LrpcMessage
)
/*++
Routine Description:
Deal with more data on a dispatched call. This
only happens when you have pipes. Pipe data on
async calls is handled differently from sync calls.
Arguments:
LrpcMessage - the LRPC message. For pipe data, we always
take the slow path (ie: NtReadRequestData).
Return Value:
NULL: if the request was processed.
not NULL: if there was a problem. the return value contains the
reply message.
--*/
{
LRPC_SCALL *SCall ;
RPC_STATUS Status ;
AssociationMutex.Request() ;
SCall = SCallDict.Find(ULongToPtr((*LrpcMessage)->Rpc.RpcHeader.CallId));
AssociationMutex.Clear() ;
// we have to wait until the server either calls
// Receive or calls Register. If it Calls Receive,
// we know that it is synchronous. If it calls
// Register, we know that it is async.
if (SCall)
{
Status = SCall->ProcessResponse(LrpcMessage) ;
}
else
{
#if DBG
PrintToDebugger("LRPC: No call corresponding the the pipe request\n");
#endif
Status = RPC_S_OUT_OF_MEMORY ;
}
if (Status != RPC_S_OK)
{
SetFaultPacket(*LrpcMessage,
Status,
LRPC_SYNC_CLIENT,
NULL) ;
return *LrpcMessage ;
}
return NULL ;
}
void
LRPC_SASSOCIATION::CleanupIdleSContexts (
void
)
/*++
Routine Description:
Walks the list of SContexts, finds the ones
that are idle and server side only, and cleans
them up.
Arguments:
Return Value:
--*/
{
LRPC_SCONTEXT *SContext;
DictionaryCursor cursor;
SContextDict.Reset(cursor);
AssociationMutex.Request();
while ((SContext = SContextDict.Next(cursor)) != 0)
{
if (SContext->GetServerSideOnlyFlag())
{
if (SContext->IsIdle())
{
SContext = (LRPC_SCONTEXT *)SContextDict.DeleteItemByBruteForce(SContext);
ASSERT(SContext);
SContext->Destroy();
}
}
}
AssociationMutex.Clear();
}
RPC_STATUS
LRPC_SCALL::SetupCall(
)
/*++
Routine Description:
Helper function that does the setup needed to use the
call in conjuction with Pipes or Async RPC.
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status = RPC_S_OK ;
//
// Stuff from ActivateCall
//
RcvBufferLength = 0;
CallId = LrpcRequestMessage->Rpc.RpcHeader.CallId ;
ReceiveComplete = 0;
AsyncReply = 0;
CachedAPCInfoAvailable = 1;
Choked = 0;
AsyncStatus = RPC_S_OK ;
NeededLength = 0;
NotificationIssued = -1;
if (ReceiveEvent == 0)
{
ReceiveEvent = new EVENT(&Status, 0);
if (ReceiveEvent == 0 || Status)
{
delete ReceiveEvent;
ReceiveEvent = 0;
return RPC_S_OUT_OF_MEMORY ;
}
CallMutex = new MUTEX(&Status) ;
if (CallMutex == 0 || Status)
{
Association->SCallDict.Delete(ULongToPtr(CallId));
goto Cleanup;
}
}
else
{
ReceiveEvent->Lower();
}
Association->AssociationMutex.Request() ;
if (Association->SCallDict.Insert(ULongToPtr(CallId), this) == -1)
{
Association->AssociationMutex.Clear() ;
goto Cleanup;
}
Association->AssociationMutex.Clear() ;
LrpcReplyMessage->Rpc.RpcHeader.CallId = CallId ;
return (RPC_S_OK) ;
Cleanup:
delete CallMutex ;
delete ReceiveEvent;
CallMutex = 0;
ReceiveEvent = 0;
return RPC_S_OUT_OF_MEMORY ;
}
RPC_STATUS
LRPC_SCALL::NegotiateTransferSyntax (
IN OUT PRPC_MESSAGE Message
)
{
// this can happen in the callback case only.
// Just return the already negotiated transfer syntax
PRPC_DISPATCH_TABLE Ignored;
SBinding->GetSelectedTransferSyntaxAndDispatchTable(&Message->TransferSyntax,
&Ignored);
return RPC_S_OK;
}
RPC_STATUS
LRPC_SCALL::GetBuffer (
IN OUT PRPC_MESSAGE Message,
IN UUID *
)
/*++
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.
--*/
{
int BufferKey ;
ASSERT(LrpcReplyMessage != 0) ;
if (PARTIAL(Message))
{
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)
{
ASSERT(((ULONG_PTR) LrpcReplyMessage->Rpc.Buffer) % 8 == 0);
// uncomment this to check for 16 byte alignment on 64 bits
// ASSERT(IsBufferAligned(LrpcReplyMessage->Rpc.Buffer));
Message->Buffer = LrpcReplyMessage->Rpc.Buffer;
LrpcReplyMessage->LpcHeader.u2.ZeroInit = 0;
LrpcReplyMessage->Rpc.RpcHeader.Flags = LRPC_BUFFER_IMMEDIATE;
LrpcReplyMessage->LpcHeader.u1.s1.DataLength = (USHORT)
(Align4(Message->BufferLength) + sizeof(LRPC_RPC_HEADER));
return (RPC_S_OK) ;
}
else
{
Message->Buffer = RpcpFarAllocate(Message->BufferLength);
if (Message->Buffer == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
}
LrpcReplyMessage->Rpc.RpcHeader.Flags = LRPC_BUFFER_SERVER;
LrpcReplyMessage->LpcHeader.u2.ZeroInit = 0;
if (PARTIAL(Message) || IsClientAsync())
{
LrpcReplyMessage->Rpc.Request.CountDataEntries = 1;
LrpcReplyMessage->LpcHeader.MessageId = 0;
LrpcReplyMessage->LpcHeader.CallbackId = 0;
LrpcReplyMessage->LpcHeader.u2.s2.DataInfoOffset =
sizeof(PORT_MESSAGE) + sizeof(LRPC_RPC_HEADER);
LrpcReplyMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_RPC_HEADER) + sizeof(PORT_DATA_INFORMATION);
LrpcReplyMessage->Rpc.Request.DataEntries[0].Base = PtrToMsgPtr(Message->Buffer);
LrpcReplyMessage->Rpc.Request.DataEntries[0].Size = Message->BufferLength;
}
else
{
Association->AssociationMutex.Request() ;
BufferKey = Association->Buffers.Insert((LRPC_CLIENT_BUFFER *) Message->Buffer) ;
Association->AssociationMutex.Clear() ;
if (BufferKey == -1)
{
RpcpFarFree(Message->Buffer) ;
return RPC_S_OUT_OF_MEMORY ;
}
LrpcReplyMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_RPC_HEADER) + sizeof(LRPC_SERVER_BUFFER) ;
ASSERT(Message->BufferLength < 0x80000000);
LrpcReplyMessage->Rpc.Server.Length = Message->BufferLength ;
LrpcReplyMessage->Rpc.Server.Buffer = PtrToMsgPtr(Message->Buffer) ;
}
return(RPC_S_OK);
}
void
LRPC_SCALL::FreeBuffer (
IN PRPC_MESSAGE Message
)
/*++
Routine Description:
We will free the supplied buffer.
Arguments:
Message - Supplies the buffer to be freed.
--*/
{
ASSERT(LrpcReplyMessage != NULL) ;
if (!(Message->Buffer == LrpcRequestMessage->Rpc.Buffer
|| Message->Buffer == LrpcReplyMessage->Rpc.Buffer))
{
if (!PARTIAL(Message) && !IsClientAsync())
{
Association->AssociationMutex.Request() ;
Association->Buffers.DeleteItemByBruteForce((LRPC_CLIENT_BUFFER *) Message->Buffer);
Association->AssociationMutex.Clear() ;
}
RpcpFarFree(Message->Buffer);
}
}
void
LRPC_SCALL::FreePipeBuffer (
IN PRPC_MESSAGE Message
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RpcpFarFree(Message->Buffer) ;
}
RPC_STATUS
LRPC_SCALL::ReallocPipeBuffer (
IN PRPC_MESSAGE Message,
IN unsigned int NewSize
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
int BufferKey;
PVOID Buffer ;
void *NewBuffer ;
BOOL BufferChanged = FALSE ;
if (NewSize > CurrentBufferLength)
{
NewBuffer = RpcpFarAllocate(NewSize) ;
if (NewBuffer == 0)
{
RpcpFarFree(Message->Buffer) ;
return (RPC_S_OUT_OF_MEMORY) ;
}
if (CurrentBufferLength > 0)
{
RpcpMemoryCopy(NewBuffer, Message->Buffer, Message->BufferLength) ;
FreePipeBuffer(Message) ;
}
Message->Buffer = NewBuffer ;
CurrentBufferLength = NewSize ;
BufferChanged = TRUE ;
}
Message->BufferLength = NewSize ;
LrpcReplyMessage->Rpc.RpcHeader.Flags = LRPC_BUFFER_SERVER;
ASSERT(Message->BufferLength < 0x80000000);
LrpcReplyMessage->Rpc.Request.DataEntries[0].Base = PtrToMsgPtr(Message->Buffer);
LrpcReplyMessage->Rpc.Request.DataEntries[0].Size = Message->BufferLength;
return (RPC_S_OK) ;
}
RPC_STATUS
LRPC_SCALL::AbortAsyncCall (
IN PRPC_ASYNC_STATE pAsync,
IN unsigned long ExceptionCode
)
{
NTSTATUS NtStatus;
RPC_STATUS Status = RPC_S_OK;
NukeStaleEEInfoIfNecessary(ExceptionCode);
RpcpErrorAddRecord(EEInfoGCApplication,
ExceptionCode,
EEInfoDLAbortCall,
SBinding->GetInterfaceFirstDWORD(),
(short)RpcMessage.ProcNum,
RpcMessage.RpcFlags);
SetFaultPacket(LrpcReplyMessage, ExceptionCode, Flags, this);
if (IsClientAsync())
{
if (LrpcReplyMessage->Rpc.RpcHeader.MessageType != LRPC_MSG_FAULT2)
{
NtStatus = SendDGReply(LrpcReplyMessage) ;
}
else
{
BOOL Ignored;
RpcMessage.RpcFlags = 0;
RpcMessage.Buffer = NULL;
Status = SendRequest(&RpcMessage,
&Ignored // shutup parameter - it is not relevant for us
);
if (Status != RPC_S_OK)
{
#if DBG
PrintToDebugger("RPC: SendRequest failed: %d\n", Status);
#endif
Association->Delete();
}
}
}
else
{
INITMSG(LrpcReplyMessage,
ClientId,
CallbackId,
MessageId);
NtStatus = Association->ReplyMessage(LrpcReplyMessage);
}
if (NT_ERROR(NtStatus))
{
Status = RPC_S_CALL_FAILED ;
}
RemoveReference();
return Status ;
}
RPC_STATUS
LRPC_SCALL::Receive (
IN PRPC_MESSAGE Message,
IN unsigned int Size
)
/*++
Routine Description:
Receive routine used by pipes
Arguments:
Message - contains to buffer to receive in
pSize - pointer to a size value that contains the minimum amount of
data that needs to be received.
Return Value:
RPC_S_OK - We have successfully converted the message.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to do the
conversion.
--*/
{
int RequestedSize;
unsigned long Extra = IsExtraMessage(Message) ;
ASSERT(ReceiveEvent) ;
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
if (!Extra && Message->Buffer)
{
ASSERT(LrpcRequestMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_REQUEST);
RpcpFarFree(Message->Buffer);
Message->Buffer = 0;
Message->BufferLength = 0;
}
//
// It is ok for us to find out that the buffer is complete
// before SavedBuffer is set,
// we need to take the CallMutex in GetCoalescedBuffer
//
while (!BufferComplete && (!PARTIAL(Message) || RcvBufferLength < Size))
{
if (ReceiveEvent->Wait() == WAIT_FAILED)
{
return RPC_S_CALL_FAILED;
}
if (AsyncStatus != RPC_S_OK)
{
return AsyncStatus;
}
}
return GetCoalescedBuffer(Message, Extra) ;
}
RPC_STATUS
LRPC_SCALL::Send (
IN OUT PRPC_MESSAGE Message
)
{
BOOL Shutup ;
Message->RpcFlags |= RPC_BUFFER_PARTIAL;
return SendRequest(Message, &Shutup) ;
}
RPC_STATUS
LRPC_SCALL::SendRequest (
IN OUT PRPC_MESSAGE Message,
OUT BOOL *Shutup
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status;
NTSTATUS NtStatus ;
int RemainingLength = 0;
LRPC_MESSAGE ReplyMessage ;
*Shutup = 0;
if (PARTIAL(Message))
{
if (Message->BufferLength < MINIMUM_PARTIAL_BUFFLEN)
{
return (RPC_S_SEND_INCOMPLETE) ;
}
if (NOT_MULTIPLE_OF_EIGHT(Message->BufferLength))
{
RemainingLength = Message->BufferLength & LOW_BITS ;
Message->BufferLength &= ~LOW_BITS ;
}
LrpcReplyMessage->Rpc.RpcHeader.Flags |= LRPC_BUFFER_PARTIAL ;
}
if (FirstSend)
{
// this code will get executed only in
// the non async case
FirstSend = 0;
if (ReceiveEvent == 0)
{
Status = SetupCall() ;
if (Status != RPC_S_OK)
{
if (PARTIAL(Message)
&& LrpcReplyMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_SERVER)
{
RpcpFarFree(Message->Buffer);
}
return Status ;
}
}
}
if (LrpcReplyMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_SERVER)
{
ASSERT((Message->Buffer == NULL)
|| (PtrToMsgPtr(Message->Buffer) == LrpcReplyMessage->Rpc.Request.DataEntries[0].Base));
LrpcReplyMessage->LpcHeader.u1.s1.TotalLength =
LrpcReplyMessage->LpcHeader.u1.s1.DataLength
+ sizeof(PORT_MESSAGE);
if (LrpcReplyMessage->Rpc.RpcHeader.Flags & LRPC_EEINFO_PRESENT)
{
LrpcReplyMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_FAULT2;
// for FAULT2, the length has already been set
}
else
{
LrpcReplyMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE;
LrpcReplyMessage->Rpc.Request.DataEntries[0].Size =
Message->BufferLength ;
}
LrpcReplyMessage->Rpc.RpcHeader.CallId = CallId;
NtStatus = NtRequestWaitReplyPort(Association->LpcReplyPort,
(PORT_MESSAGE *) LrpcReplyMessage,
(PORT_MESSAGE *) &ReplyMessage) ;
if (NT_ERROR(NtStatus))
{
if (Message->Buffer)
{
RpcpFarFree(Message->Buffer);
}
return RPC_S_CALL_FAILED ;
}
else
{
ASSERT((ReplyMessage.Rpc.RpcHeader.MessageType == LRPC_MSG_ACK)
||
(ReplyMessage.Rpc.RpcHeader.MessageType == LRPC_MSG_FAULT));
if (!PARTIAL(Message) &&
(LrpcReplyMessage->Rpc.RpcHeader.MessageType != LRPC_MSG_FAULT2) &&
(LrpcReplyMessage->Rpc.RpcHeader.MessageType != LRPC_MSG_FAULT))
{
if (Message->Buffer)
{
RpcpFarFree(Message->Buffer);
}
}
if (ReplyMessage.Rpc.RpcHeader.MessageType == LRPC_MSG_ACK)
{
*Shutup = ReplyMessage.Ack.Shutup;
}
else
{
Status = ReplyMessage.Fault.RpcStatus;
return Status;
}
}
}
else
{
ASSERT(!PARTIAL(Message)) ;
LrpcReplyMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE;
NtStatus = SendDGReply(LrpcReplyMessage) ;
if (NT_ERROR(NtStatus))
{
return RPC_S_CALL_FAILED ;
}
}
if (RemainingLength)
{
ASSERT(PARTIAL(Message)) ;
RpcpMemoryMove(Message->Buffer,
(char *) Message->Buffer + Message->BufferLength,
RemainingLength) ;
Message->BufferLength = RemainingLength ;
return (RPC_S_SEND_INCOMPLETE) ;
}
return RPC_S_OK ;
}
inline RPC_STATUS
LRPC_SCALL::GetBufferDo(
IN OUT PRPC_MESSAGE Message,
IN unsigned long NewSize,
IN BOOL fDataValid
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
void *NewBuffer ;
if (NewSize < CurrentBufferLength)
{
Message->BufferLength = NewSize ;
}
else
{
NewBuffer = RpcpFarAllocate(NewSize) ;
if (NewBuffer == 0)
{
RpcpFarFree(Message->Buffer) ;
Message->BufferLength = 0;
return RPC_S_OUT_OF_MEMORY ;
}
if (fDataValid && Message->BufferLength > 0)
{
RpcpMemoryCopy(NewBuffer,
Message->Buffer,
Message->BufferLength) ;
}
if (EXTRA(Message))
{
ASSERT(Message->ReservedForRuntime) ;
((PRPC_RUNTIME_INFO)Message->ReservedForRuntime)->OldBuffer =
NewBuffer;
}
RpcpFarFree(Message->Buffer) ;
Message->Buffer = NewBuffer ;
Message->BufferLength = NewSize ;
}
return RPC_S_OK ;
}
RPC_STATUS
LRPC_SCALL::SendReceive (
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
Arguments:
Message - Supplies the request and returns the response of a remote
procedure call.
Return Value:
RPC_S_OK - The remote procedure call completed successful.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
remote procedure call.
RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to complete
the remote procedure call.
--*/
{
NTSTATUS NtStatus;
RPC_STATUS ExceptionCode, Status;
LRPC_MESSAGE *LrpcSavedMessage;
SIZE_T NumberOfBytesRead;
RPC_MESSAGE RpcMessage ;
RPC_RUNTIME_INFO RuntimeInfo ;
PRPC_DISPATCH_TABLE DispatchTableToUse;
// The LrpcMessage must be saved, it is in use by the stub. The current
// LrpcReplyMessage can be used for the callback request message and reply.
//
// We must:
// Save the current LrpcRequestMessage
// Make the current LrpcReplyMessage the LrpcRequestMessage
// Allocate a new LrpcReplyMessage.
LrpcSavedMessage = LrpcRequestMessage;
LrpcRequestMessage = LrpcReplyMessage;
LrpcReplyMessage = 0; // Only needed if we receive a recursive request.
Association->Address->Server->OutgoingCallback();
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
LrpcRequestMessage->LpcHeader.u1.s1.TotalLength = sizeof(PORT_MESSAGE)
+ LrpcRequestMessage->LpcHeader.u1.s1.DataLength;
LrpcRequestMessage->LpcHeader.u2.s2.Type = LPC_REQUEST;
INITMSG(LrpcRequestMessage, ClientId, CallbackId, MessageId);
LrpcRequestMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_CALLBACK;
LrpcRequestMessage->Rpc.RpcHeader.ProcedureNumber = (unsigned short) Message->ProcNum;
LrpcRequestMessage->Rpc.RpcHeader.PresentContext =
SBinding->GetOnTheWirePresentationContext();
NtStatus = NtRequestWaitReplyPort(Association->LpcServerPort,
(PORT_MESSAGE *) LrpcRequestMessage,
(PORT_MESSAGE *) LrpcRequestMessage);
if (NT_ERROR(NtStatus))
{
LrpcReplyMessage = LrpcRequestMessage;
LrpcRequestMessage = LrpcSavedMessage;
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);
ASSERT(0) ;
}
#endif // DBG
return(RPC_S_CALL_FAILED);
}
for (;;)
{
if (LrpcRequestMessage->Rpc.RpcHeader.MessageType
== LRPC_MSG_FAULT)
{
Status = LrpcRequestMessage->Fault.RpcStatus;
break;
}
if (LrpcRequestMessage->Rpc.RpcHeader.MessageType
== LRPC_MSG_RESPONSE)
{
if (LrpcRequestMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_REQUEST)
{
LrpcRequestMessage->LpcHeader.ClientId = LrpcSavedMessage->Rpc.LpcHeader.ClientId;
LrpcRequestMessage->LpcHeader.CallbackId = LrpcRequestMessage->Rpc.LpcHeader.CallbackId + 1;
LrpcSavedMessage->LpcHeader.MessageId = LrpcSavedMessage->Rpc.LpcHeader.MessageId;
}
Status = LrpcMessageToRpcMessage(LrpcRequestMessage, Message);
break;
}
if (LrpcRequestMessage->Rpc.RpcHeader.MessageType
== LRPC_MSG_PUSH)
{
ASSERT(PushedResponse == 0);
PushedResponse = RpcpFarAllocate(
(unsigned int)
LrpcRequestMessage->Push.Response.DataEntries[0].Size);
if (PushedResponse == 0)
{
LrpcRequestMessage->Push.RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else
{
NtStatus = NtReadRequestData(
Association->LpcServerPort,
(PORT_MESSAGE *) LrpcRequestMessage,
0,
PushedResponse,
LrpcRequestMessage->Push.Response.DataEntries[0].Size,
&NumberOfBytesRead);
if (NT_ERROR(NtStatus))
{
RpcpFarFree(PushedResponse);
PushedResponse = 0;
LrpcRequestMessage->Push.RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else
{
ASSERT(LrpcRequestMessage->Push.Response.DataEntries[0].Size
== NumberOfBytesRead);
LrpcRequestMessage->Push.RpcStatus = RPC_S_OK;
}
}
INITMSG(LrpcRequestMessage,
ClientId,
CallbackId,
MessageId) ;
NtStatus = NtReplyWaitReplyPort(Association->LpcServerPort,
(PORT_MESSAGE *) LrpcRequestMessage);
if (PushedResponse)
{
RpcpFarFree(PushedResponse);
PushedResponse = 0;
}
}
else
{
VALIDATE(LrpcRequestMessage->Rpc.RpcHeader.MessageType)
{
LRPC_MSG_REQUEST
} END_VALIDATE;
Status = LrpcMessageToRpcMessage(LrpcRequestMessage,
Message);
if (Status != RPC_S_OK)
{
LrpcRequestMessage->Fault.RpcHeader.MessageType =
LRPC_MSG_FAULT;
LrpcRequestMessage->Fault.RpcStatus = LrpcMapRpcStatus(Status);
LrpcRequestMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_FAULT_MESSAGE) - sizeof(PORT_MESSAGE);
LrpcRequestMessage->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_FAULT_MESSAGE);
INITMSG(LrpcRequestMessage,
ClientId,
CallbackId,
MessageId) ;
NtStatus = NtReplyWaitReplyPort(Association->LpcServerPort,
(PORT_MESSAGE *) LrpcRequestMessage);
}
else
{
LrpcReplyMessage = new LRPC_MESSAGE;
if (LrpcReplyMessage != 0)
{
SBinding->GetSelectedTransferSyntaxAndDispatchTable(&Message->TransferSyntax,
&DispatchTableToUse);
Message->ProcNum =
LrpcRequestMessage->Rpc.RpcHeader.ProcedureNumber;
RuntimeInfo.Length = sizeof(RPC_RUNTIME_INFO) ;
RpcMessage = *Message ;
RpcMessage.ReservedForRuntime = &RuntimeInfo ;
if (ObjectUuidFlag != 0)
{
Status = SBinding->RpcInterface->
DispatchToStubWithObject(
&RpcMessage,
&ObjectUuid,
1,
DispatchTableToUse,
&ExceptionCode);
}
else
{
Status = SBinding->RpcInterface->
DispatchToStub(
&RpcMessage,
1,
DispatchTableToUse,
&ExceptionCode);
}
*Message = RpcMessage ;
// Because we must send the reply and recieve the
// reply into the same message, we just copy the
// response into the LrpcRequestMessage
RpcpMemoryCopy(LrpcRequestMessage,
LrpcReplyMessage,
sizeof(LRPC_MESSAGE));
delete LrpcReplyMessage;
LrpcReplyMessage = 0;
}
else
Status = RPC_S_OUT_OF_MEMORY;
if (Status != RPC_S_OK)
{
VALIDATE(Status)
{
RPC_S_OUT_OF_MEMORY,
RPC_P_EXCEPTION_OCCURED,
RPC_S_PROCNUM_OUT_OF_RANGE
} END_VALIDATE;
if (Status == RPC_P_EXCEPTION_OCCURED)
{
Status = LrpcMapRpcStatus(ExceptionCode);
}
LrpcRequestMessage->Fault.RpcStatus = Status;
LrpcRequestMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_FAULT_MESSAGE) - sizeof(PORT_MESSAGE);
LrpcRequestMessage->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_FAULT_MESSAGE);
LrpcRequestMessage->Fault.RpcHeader.MessageType =
LRPC_MSG_FAULT;
}
else
{
LrpcRequestMessage->LpcHeader.u1.s1.TotalLength =
sizeof(PORT_MESSAGE)
+ LrpcRequestMessage->LpcHeader.u1.s1.DataLength;
LrpcRequestMessage->Rpc.RpcHeader.MessageType =
LRPC_MSG_RESPONSE;
}
INITMSG(LrpcRequestMessage,
ClientId,
CallbackId,
MessageId) ;
NtStatus = NtReplyWaitReplyPort(Association->LpcServerPort,
(PORT_MESSAGE *) LrpcRequestMessage);
}
}
if (NT_ERROR(NtStatus))
{
if (NtStatus == STATUS_NO_MEMORY)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else if (NtStatus == STATUS_INSUFFICIENT_RESOURCES)
{
Status = RPC_S_OUT_OF_RESOURCES;
}
else
{
VALIDATE(NtStatus)
{
STATUS_INVALID_PORT_HANDLE,
STATUS_INVALID_HANDLE,
STATUS_INVALID_CID,
STATUS_PORT_DISCONNECTED,
STATUS_LPC_REPLY_LOST
} END_VALIDATE;
Status = RPC_S_CALL_FAILED;
}
break;
}
}
if (Status == RPC_S_OK)
{
Message->Handle = (RPC_BINDING_HANDLE) this;
}
ASSERT(LrpcReplyMessage == 0);
LrpcReplyMessage = LrpcRequestMessage;
LrpcRequestMessage = LrpcSavedMessage;
return(Status);
}
void
LRPC_SCALL::FreeObject (
)
{
LRPC_SASSOCIATION *MyAssociation;
ASSERT(pAsync) ;
ASSERT(DispatchBuffer) ;
if (DispatchBuffer != LrpcRequestMessage->Rpc.Buffer)
{
RpcpFarFree(DispatchBuffer);
}
FreeMessage(LrpcRequestMessage) ;
SBinding->RpcInterface->EndCall(0, 1) ;
MyAssociation = Association;
MyAssociation->FreeSCall(this) ;
MyAssociation->Address->DereferenceAssociation(MyAssociation);
// Warning: The SCALL could have been nuked at this point.
// DO NOT touch the SCALL after this
}
RPC_STATUS
LRPC_SCALL::AsyncSend (
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
Send an async reply. This request can either be partial or complete.
If it is a complete request, we cleanup the SCall.
Arguments:
Message - contains the request
Return Value:
RPC_S_OK - Function succeeded
RPC_S_SEND_INCOMPLETE - some data still needs to be sent.
Message->Buffer pointes to the remaining data, and
Message->BufferLength is the length of the remaining data.
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status = RPC_S_OK;
NTSTATUS NtStatus ;
BOOL fRetVal ;
BOOL Shutup ;
ASSERT(ReceiveEvent) ;
if (AsyncStatus != RPC_S_OK)
{
if (PARTIAL(Message))
{
Status = AsyncStatus;
}
goto Cleanup;
}
FirstSend = 0;
if (Flags & LRPC_NON_PIPE)
{
LrpcReplyMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE;
ASSERT(!IsClientAsync()) ;
NtStatus = Association->ReplyMessage(LrpcReplyMessage);
if (!NT_SUCCESS(NtStatus))
{
Status = RPC_S_OUT_OF_MEMORY ;
}
}
else
{
Status = SendRequest(Message, &Shutup) ;
}
if (PARTIAL(Message))
{
if (Status == RPC_S_OK
|| Status == RPC_S_SEND_INCOMPLETE)
{
if ((pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE) && !Shutup)
{
CallMutex->Request() ;
if (!IssueNotification(RpcSendComplete))
{
Status = RPC_S_OUT_OF_MEMORY ;
}
CallMutex->Clear() ;
}
return Status;
}
}
else
{
//
// Non partial async sends will always succeed
// if they fail, we will hide the error
//
Status = RPC_S_OK;
}
Cleanup:
//
// on the server, the stub never calls FreeBuffer
//
RemoveReference();
return Status;
}
RPC_STATUS
LRPC_SCALL::AsyncReceive (
IN OUT PRPC_MESSAGE Message,
IN unsigned int Size
)
/*++
Routine Description:
On the server, this routine is only called when the stub needs
more data to unmarshall the non pipe parameters, or when it needs
pipe data.
Arguments:
Message - contains information about the request
Size - needed size
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status ;
int Extra = IsExtraMessage(Message);
ASSERT(ReceiveEvent) ;
if (PARTIAL(Message) == 0)
{
return Receive(Message, Size);
}
if (Extra)
{
Status = Receive(Message, Size);
//
// don't need to check the status. If Receive failed, we are
// never going to access dispatch buffer anyway
//
DispatchBuffer = Message->Buffer ;
return Status;
}
CallMutex->Request();
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
if (BufferComplete == 0
&& RcvBufferLength < Size)
{
if (NOTIFY(Message))
{
NeededLength = Size ;
}
CallMutex->Clear() ;
return RPC_S_ASYNC_CALL_PENDING;
}
else
{
Status = GetCoalescedBuffer(Message, 0);
}
CallMutex->Clear();
return Status ;
}
RPC_STATUS
LRPC_SCALL::SetAsyncHandle (
IN PRPC_ASYNC_STATE pAsync
)
/*++
Routine Description:
Set the async handle corresponding this SCALL. This call is made
by the stubs.
Arguments:
pAsync - The async handle to association with this SCall
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status ;
THREAD *Thread = RpcpGetThreadPointer();
ASSERT(Thread);
ASSERT(pAsync);
Thread->fAsync = TRUE;
if (DebugCell)
{
ASSERT(IsServerSideDebugInfoEnabled());
DebugCell->CallFlags |= DBGCELL_ASYNC_CALL;
}
if (ReceiveEvent == 0)
{
Status = SetupCall();
if (Status != RPC_S_OK)
{
return Status;
}
}
if (LrpcAsyncReplyMessage == 0)
{
LrpcAsyncReplyMessage = AllocateMessage() ;
if (LrpcAsyncReplyMessage == 0)
{
return RPC_S_OUT_OF_MEMORY ;
}
}
LrpcReplyMessage = LrpcAsyncReplyMessage;
LrpcReplyMessage->Rpc.RpcHeader.CallId = CallId ;
INITMSG(LrpcReplyMessage,
ClientId,
CallbackId,
MessageId) ;
this->pAsync = pAsync ;
return RPC_S_OK ;
}
RPC_STATUS
LRPC_SCALL::ProcessResponse (
IN LRPC_MESSAGE **LrpcMessage
)
/*++
Routine Description:
A buffer has just arrived, process it. If some other buffer is already
processing buffers, simply queue it and go away. Otherwise, does
the processing ourselves.
Arguments:
Message - Details on the arrived message
--*/
{
RPC_MESSAGE Message ;
RPC_STATUS Status ;
switch ((*LrpcMessage)->Rpc.RpcHeader.MessageType)
{
case LRPC_SERVER_SEND_MORE:
if (pAsync && (pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE))
{
if (!IssueNotification(RpcSendComplete))
{
AsyncStatus = RPC_S_OUT_OF_MEMORY ;
#if DBG
PrintToDebugger("RPC: IssueNotification failed\n") ;
#endif
//
// We are pretty much hosed here, but we'll try to
// queue notification anyway.
//
IssueNotification() ;
return RPC_S_OUT_OF_MEMORY ;
}
}
return RPC_S_OK ;
case LRPC_MSG_CANCEL:
InterlockedExchange(&CancelPending, 1);
return RPC_S_OK;
default:
break;
}
CallMutex->Request() ;
ASSERT(BufferComplete == 0);
Message.RpcFlags = 0;
Status = LrpcMessageToRpcMessage(
*LrpcMessage,
&Message) ;
if (Status != RPC_S_OK)
{
#if DBG
PrintToDebugger("RPC: LrpcMessageToRpcMessage failed: %x\n", Status) ;
#endif
AsyncStatus = Status ;
IssueNotification() ;
return Status ;
}
if (COMPLETE(&Message))
{
ASSERT(BufferComplete == 0);
BufferComplete = 1;
}
if (Message.BufferLength)
{
RcvBufferLength += Message.BufferLength ;
if (BufferQueue.PutOnQueue(Message.Buffer,
Message.BufferLength))
{
AsyncStatus = Status = RPC_S_OUT_OF_MEMORY ;
#if DBG
PrintToDebugger("RPC: PutOnQueue failed\n") ;
#endif
}
}
if (IsSyncCall())
{
CallMutex->Clear() ;
ReceiveEvent->Raise();
}
else
{
if (Status == RPC_S_OK
&& NeededLength > 0
&& RcvBufferLength >= NeededLength)
{
IssueNotification(RpcReceiveComplete);
}
CallMutex->Clear() ;
}
return Status ;
}
RPC_STATUS
LRPC_SCALL::GetCoalescedBuffer (
IN PRPC_MESSAGE Message,
IN BOOL BufferValid
)
/*++
Routine Description:
Remove buffers from the queue and coalesce them into a single buffer.
Arguments:
Message - on return this will contain the coalesced buffer, Message->BufferLength
gives us the length of the coalesced buffer.
BufferValid - Tells us if Message->Buffer is valid on entry.
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
void *NewBuffer, *Buffer ;
char *Current ;
unsigned int bufferlength ;
unsigned int TotalLength ;
LRPC_MESSAGE SendMore ;
NTSTATUS NtStatus ;
CallMutex->Request() ;
ASSERT(RcvBufferLength);
if (BufferValid)
{
TotalLength = RcvBufferLength + Message->BufferLength ;
}
else
{
TotalLength = RcvBufferLength ;
}
NewBuffer = RpcpFarAllocate(TotalLength) ;
if (NewBuffer == 0)
{
CallMutex->Clear() ;
return RPC_S_OUT_OF_MEMORY;
}
if (BufferValid)
{
RpcpMemoryCopy(NewBuffer, Message->Buffer, Message->BufferLength) ;
Current = (char *) NewBuffer + Message->BufferLength ;
}
else
{
Current = (char *) NewBuffer;
}
while ((Buffer = BufferQueue.TakeOffQueue(&bufferlength)) != 0)
{
RpcpMemoryCopy(Current, Buffer, bufferlength) ;
Current += bufferlength ;
RpcpFarFree(Buffer);
}
if (BufferValid && Message->Buffer)
{
RpcpFarFree(Message->Buffer);
//
// Update the dispatch buffer
//
ASSERT(Message->ReservedForRuntime) ;
((PRPC_RUNTIME_INFO)Message->ReservedForRuntime)->OldBuffer = NewBuffer;
if (Message->Buffer == DispatchBuffer)
DispatchBuffer = NewBuffer;
}
Message->Buffer = NewBuffer ;
Message->BufferLength = TotalLength ;
RcvBufferLength = 0;
if (BufferComplete)
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
}
else
{
if (Choked)
{
CallMutex->Clear() ;
//
// send a message to the client
// to start sending data again
//
SendMore.Rpc.RpcHeader.MessageType = LRPC_CLIENT_SEND_MORE;
SendMore.LpcHeader.u1.s1.DataLength =
sizeof(LRPC_MESSAGE) - sizeof(PORT_MESSAGE);
SendMore.LpcHeader.u2.ZeroInit = 0;
SendMore.Rpc.RpcHeader.CallId = CallId ;
NtStatus = SendDGReply(&SendMore) ;
if (!NT_SUCCESS(NtStatus))
{
return RPC_S_CALL_FAILED ;
}
return RPC_S_OK;
}
}
CallMutex->Clear() ;
return RPC_S_OK ;
}
RPC_STATUS
LRPC_SCALL::ImpersonateClient (
)
/*++
Routine Description:
We will impersonate the client which made the remote procedure call.
--*/
{
NTSTATUS NtStatus;
RPC_STATUS Status;
HANDLE hToken;
DWORD LastError;
Status = SetThreadSecurityContext((SECURITY_CONTEXT *) MAXUINT_PTR);
if (RPC_S_OK != Status)
{
return Status;
}
if (SContext)
{
if (SContext->GetAnonymousFlag())
{
NtStatus = NtImpersonateAnonymousToken(NtCurrentThread());
if (!NT_SUCCESS(NtStatus))
{
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_ACCESS_DENIED,
EEInfoDLLRPC_SCALL__ImpersonateClient10,
(ULONG)NtStatus,
(ULONG)GetCurrentThreadId());
ClearThreadSecurityContext();
return RPC_S_ACCESS_DENIED;
}
}
else if (SetThreadToken(NULL, SContext->hToken) == FALSE)
{
LastError = GetLastError();
ClearThreadSecurityContext();
if (LastError == ERROR_OUTOFMEMORY)
{
return (RPC_S_OUT_OF_MEMORY) ;
}
return RPC_S_ACCESS_DENIED;
}
}
else
{
NtStatus = NtImpersonateClientOfPort(Association->LpcServerPort,
(PORT_MESSAGE *) LrpcRequestMessage);
if ((NtStatus == STATUS_INVALID_CID)
|| (NtStatus == STATUS_PORT_DISCONNECTED)
|| (NtStatus == STATUS_REPLY_MESSAGE_MISMATCH))
{
ClearThreadSecurityContext();
return RPC_S_NO_CONTEXT_AVAILABLE;
}
if (!NT_SUCCESS(NtStatus))
{
#if DBG
PrintToDebugger("RPC : NtImpersonateClientOfPort : %lx\n",NtStatus);
#endif // DBG
return RPC_S_ACCESS_DENIED;
}
}
return(RPC_S_OK);
}
RPC_STATUS
LRPC_SCALL::RevertToSelf (
)
/*++
Routine Description:
This reverts a server thread back to itself after impersonating a client.
We just check to see if the server thread is impersonating; this optimizes
the common case.
--*/
{
HANDLE ImpersonationToken = 0;
NTSTATUS NtStatus;
if (ClearThreadSecurityContext())
{
NtStatus = NtSetInformationThread(
NtCurrentThread(),
ThreadImpersonationToken,
&ImpersonationToken,
sizeof(HANDLE));
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtSetInformationThread : %lx\n", NtStatus);
}
#endif // DBG
if (!NT_SUCCESS(NtStatus))
{
if (NtStatus == STATUS_NO_MEMORY)
{
return RPC_S_OUT_OF_MEMORY;
}
return RPC_S_ACCESS_DENIED;
}
}
return(RPC_S_OK);
}
RPC_STATUS
LRPC_SCALL::GetAuthorizationContext (
IN BOOL ImpersonateOnReturn,
IN AUTHZ_RESOURCE_MANAGER_HANDLE AuthzResourceManager,
IN PLARGE_INTEGER pExpirationTime OPTIONAL,
IN LUID Identifier,
IN DWORD Flags,
IN PVOID DynamicGroupArgs OPTIONAL,
OUT PAUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContext
)
/*++
Routine Description:
Gets an authorization context for the client that can be used
with Authz functions. The resulting context is owned by the caller
and must be freed by it.
Arguments:
ImpersonateOnReturn - if TRUE, when we return, we should be impersonating.
AuthzResourceManager - the resource manager to use (passed to Authz)
pExpirationTime - the expiration time to use (passed to Authz)
Identifier - the LUID (passed to Authz)
Flags - Flags (passed to Authz)
DynamicGroupArgs - parameter required by Authz (passed to Authz)
pAuthzClientContext - the authorization context, returned on success.
Undefined on failure.
--*/
{
RPC_STATUS Status = RPC_S_OK;
RPC_STATUS RevertStatus;
BOOL fNeedToRevert = FALSE;
HANDLE ImpersonationToken;
BOOL Result;
BOOL fImpersonating = FALSE;
PAUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContextPlaceholder;
ASSERT (AuthzResourceManager != NULL);
if (ImpersonateOnReturn
|| (SContext == NULL)
|| (SContext->AuthzClientContext == NULL))
{
Status = LRPC_SCALL::ImpersonateClient();
if (Status != RPC_S_OK)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLRPC_SCALL__GetAuthorizationContext10,
(ULONG)ImpersonateOnReturn,
(ULONGLONG)SContext);
return Status;
}
fImpersonating = TRUE;
if (!ImpersonateOnReturn)
{
fNeedToRevert = TRUE;
}
}
if (SContext && SContext->AuthzClientContext)
{
Status = DuplicateAuthzContext(SContext->AuthzClientContext,
pExpirationTime,
Identifier,
Flags,
DynamicGroupArgs,
pAuthzClientContext);
}
else
{
// either we don't have an scontext, or its
// AuthzClientContext is not set yet.
// Get the token from the thread
Result = OpenThreadToken(GetCurrentThread(),
TOKEN_QUERY,
TRUE,
&ImpersonationToken);
if (Result)
{
if (SContext)
pAuthzClientContextPlaceholder = &SContext->AuthzClientContext;
else
pAuthzClientContextPlaceholder = NULL;
Status = CreateAndSaveAuthzContextFromToken(pAuthzClientContextPlaceholder,
ImpersonationToken,
AuthzResourceManager,
pExpirationTime,
Identifier,
Flags,
DynamicGroupArgs,
pAuthzClientContext);
CloseHandle(ImpersonationToken);
}
else
{
Status = GetLastError();
if (Status == ERROR_CANT_OPEN_ANONYMOUS)
{
Result = AuthzInitializeContextFromSidFn(
AUTHZ_SKIP_TOKEN_GROUPS,
(PSID)&AnonymousSid,
AuthzResourceManager,
pExpirationTime,
Identifier,
DynamicGroupArgs,
pAuthzClientContext);
if (Result)
{
if (SContext)
{
if (InterlockedCompareExchangePointer((PVOID *)&SContext->AuthzClientContext,
pAuthzClientContext,
NULL) != NULL)
{
// somebody beat us to the punch - free the context we obtained
AuthzFreeContextFn(*pAuthzClientContext);
*pAuthzClientContext = SContext->AuthzClientContext;
}
}
// else
// the authz context is already loaded in pAuthzClientContext
Status = RPC_S_OK;
}
else
{
Status = GetLastError();
RpcpErrorAddRecord(EEInfoGCAuthz,
Status,
EEInfoDLLRPC_SCALL__GetAuthorizationContext30,
GetCurrentThreadId(),
(ULONGLONG)AuthzResourceManager);
}
}
else
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLRPC_SCALL__GetAuthorizationContext20,
GetCurrentThreadId());
}
}
}
// if caller didn't ask us to impersonate and we are,
// or we if he did ask us, but we failed somewhere,
// revert to self
if (fNeedToRevert || (Status && fImpersonating))
{
RevertStatus = LRPC_SCALL::RevertToSelf();
ASSERT(RevertStatus == RPC_S_OK);
}
return Status;
}
RPC_STATUS
LRPC_SCALL::IsClientLocal (
OUT unsigned int * ClientLocalFlag
)
/*++
Routine Description:
A client using LRPC will always be local.
Arguments:
ClientLocalFlag - Returns a flag which will always be set to a non-zero
value indicating that the client is local.
--*/
{
UNUSED(this);
*ClientLocalFlag = 1;
return(RPC_S_OK);
}
RPC_STATUS
LRPC_SCALL::ConvertToServerBinding (
OUT RPC_BINDING_HANDLE __RPC_FAR * ServerBinding
)
/*++
Routine Description:
If possible, convert this call into a server binding, meaning a
binding handle pointing back to the client.
Arguments:
ServerBinding - Returns the server binding.
Return Value:
RPC_S_OK - The server binding has successfully been created.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate
a new binding handle.
--*/
{
BOOL Boolean;
RPC_STATUS Status;
RPC_CHAR UuidString[37];
RPC_CHAR * StringBinding;
RPC_CHAR * NetworkAddress;
DWORD NetworkAddressLength = MAX_COMPUTERNAME_LENGTH + 1;
if (ObjectUuidFlag != 0)
{
ObjectUuid.ConvertToString(UuidString);
UuidString[36] = '\0';
}
NetworkAddress = new RPC_CHAR[NetworkAddressLength];
if (NetworkAddress == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
Boolean = GetComputerNameW(
NetworkAddress,
&NetworkAddressLength);
if (Boolean != TRUE)
{
Status = GetLastError();
#if DBG
PrintToDebugger("RPC : GetComputerNameW : %d\n", Status);
#endif // DBG
if (Status == ERROR_NOT_ENOUGH_MEMORY)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else if ((Status == ERROR_NOT_ENOUGH_QUOTA)
|| (Status == ERROR_NO_SYSTEM_RESOURCES))
{
Status = RPC_S_OUT_OF_RESOURCES;
}
else
{
ASSERT(0);
Status = RPC_S_OUT_OF_MEMORY;
}
delete NetworkAddress;
return Status;
}
Status = RpcStringBindingComposeW(
(ObjectUuidFlag != 0 ? UuidString : 0),
RPC_STRING_LITERAL("ncalrpc"),
NetworkAddress,
0,
0,
&StringBinding);
delete NetworkAddress;
if (Status != RPC_S_OK)
{
return(Status);
}
Status = RpcBindingFromStringBindingW(
StringBinding,
ServerBinding);
RpcStringFreeW(&StringBinding);
return(Status);
}
void
LRPC_SCALL::InquireObjectUuid (
OUT RPC_UUID * ObjectUuid
)
/*++
Routine Description:
This routine copies the object uuid from the call into the supplied
ObjectUuid argument.
Arguments:
ObjectUuid - Returns a copy of the object uuid passed by the client
in the remote procedure call.
--*/
{
if (ObjectUuidFlag == 0)
{
ObjectUuid->SetToNullUuid();
}
else
{
ObjectUuid->CopyUuid(&(this->ObjectUuid));
}
}
RPC_STATUS
LRPC_SCALL::ToStringBinding (
OUT RPC_CHAR ** StringBinding
)
/*++
Routine Description:
We need to convert this call into a string binding. We will ask the
address for a binding handle which we can then convert into a string
binding.
Arguments:
StringBinding - Returns the string binding for this call.
Return Value:
--*/
{
RPC_STATUS Status;
BINDING_HANDLE * BindingHandle
= Association->Address->InquireBinding();
if (BindingHandle == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
Status = BindingHandle->ToStringBinding(StringBinding);
BindingHandle->BindingFree();
return(Status);
}
RPC_STATUS
LRPC_SCALL::GetAssociationContextCollection (
OUT ContextCollection **CtxCollection
)
{
return Association->GetAssociationContextCollection(CtxCollection);
}
inline RPC_STATUS
LRPC_SCALL::LrpcMessageToRpcMessage (
IN LRPC_MESSAGE * LrpcMessage,
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
We will convert from an LRPC_MESSAGE representation of a buffer (and
its length) to an RPC_MESSAGE representation.
Arguments:
RpcMessage - Returns the RPC_MESSAGE representation.
Return Value:
RPC_S_OK - We have successfully converted the message.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to do the
conversion.
--*/
{
NTSTATUS NtStatus;
SIZE_T NumberOfBytesRead;
unsigned char MessageType = LrpcMessage->Rpc.RpcHeader.MessageType;
RPC_STATUS Status = RPC_S_OK ;
LRPC_MESSAGE ReplyMessage ;
if(LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_IMMEDIATE)
{
Message->Buffer = LrpcMessage->Rpc.Buffer;
ASSERT(LrpcMessage->LpcHeader.u1.s1.DataLength
>= sizeof(LRPC_RPC_HEADER));
Message->BufferLength =
(unsigned int) LrpcMessage->LpcHeader.u1.s1.DataLength
- sizeof(LRPC_RPC_HEADER);
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
}
else if (LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_REQUEST)
{
Message->BufferLength = LrpcMessage->Rpc.Request.DataEntries[0].Size;
if (LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_PARTIAL)
{
CallMutex->Request() ;
//
// If the user ever specifies a Size > LRPC_THRESHOLD_SIZE
// our performance will be bad.
//
if (RcvBufferLength >= LRPC_THRESHOLD_SIZE)
{
Choked = 1;
}
CallMutex->Clear() ;
}
else
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
}
Message->Buffer = RpcpFarAllocate(Message->BufferLength) ;
if (Message->Buffer == 0)
{
Status = RPC_S_OUT_OF_MEMORY ;
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLrpcMessageToRpcMessage10,
Message->BufferLength);
}
else
{
NtStatus = NtReadRequestData(Association->LpcServerPort,
(PORT_MESSAGE *) LrpcMessage,
0,
Message->Buffer,
Message->BufferLength,
&NumberOfBytesRead) ;
if (NT_ERROR(NtStatus))
{
RpcpFarFree(Message->Buffer) ;
Message->Buffer = 0;
Status = RPC_S_OUT_OF_MEMORY ;
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLrpcMessageToRpcMessage20,
NtStatus);
}
else
{
ASSERT(Message->BufferLength == NumberOfBytesRead) ;
}
}
if (IsClientAsync())
{
COPYMSG((&ReplyMessage), LrpcMessage) ;
ReplyMessage.Ack.MessageType = LRPC_MSG_ACK ;
ReplyMessage.Ack.RpcStatus = Status;
ReplyMessage.Ack.Shutup = (short) Choked ;
ReplyMessage.LpcHeader.u1.s1.DataLength =
sizeof(LRPC_ACK_MESSAGE) - sizeof(PORT_MESSAGE) ;
NtStatus = Association->ReplyMessage(&ReplyMessage);
if (NT_ERROR(NtStatus))
{
RpcpFarFree(Message->Buffer);
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_OUT_OF_MEMORY,
EEInfoDLLrpcMessageToRpcMessage30,
NtStatus);
return(RPC_S_OUT_OF_MEMORY);
}
}
}
else
{
ASSERT((LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_IMMEDIATE)
|| (LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_REQUEST));
}
return(Status);
}
RPC_STATUS
LRPC_SCALL::InquireAuthClient (
OUT RPC_AUTHZ_HANDLE * Privileges,
OUT RPC_CHAR * * ServerPrincipalName, OPTIONAL
OUT unsigned long * AuthenticationLevel,
OUT unsigned long * AuthenticationService,
OUT unsigned long * AuthorizationService,
IN unsigned long Flags
)
/*++
Routine Description:
Each protocol module must define this routine: it is used to obtain
the authentication and authorization information about a client making
the remote procedure call represented by this.
Arguments:
Privileges - Returns a the privileges of the client.
ServerPrincipalName - Returns the server principal name which the client
specified.
AuthenticationLevel - Returns the authentication level requested by
the client.
AuthenticationService - Returns the authentication service requested by
the client.
AuthorizationService - Returns the authorization service requested by
the client.
Return Value:
RPC_S_OK or RPC_S_* / Win32 error
--*/
{
RPC_STATUS Status;
if(ARGUMENT_PRESENT(Privileges))
{
*(RPC_CHAR **)Privileges = NULL;
Status = Association->GetClientName(this,
NULL, // ClientPrincipalNameBufferLength
(RPC_CHAR **) Privileges);
if (Status != RPC_S_OK)
{
return Status;
}
}
if (ARGUMENT_PRESENT(ServerPrincipalName))
{
*ServerPrincipalName = NULL;
}
if(ARGUMENT_PRESENT(AuthenticationLevel))
{
*AuthenticationLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY ;
}
if(ARGUMENT_PRESENT(AuthenticationService))
{
*AuthenticationService = RPC_C_AUTHN_WINNT ;
}
if(ARGUMENT_PRESENT(AuthorizationService))
{
*AuthorizationService = RPC_C_AUTHZ_NONE ;
}
return(RPC_S_OK);
}
RPC_STATUS
LRPC_SCALL::InquireCallAttributes (
IN OUT void *RpcCallAttributes
)
/*++
Routine Description:
Inquire the security context attributes for the LRPC client
Arguments:
RpcCallAttributes - a pointer to
RPC_CALL_ATTRIBUTES_V1_W structure. The Version
member must be initialized.
Return Value:
RPC_S_OK or RPC_S_* / Win32 error. EEInfo will be returned.
--*/
{
RPC_CALL_ATTRIBUTES_V1 *CallAttributes;
RPC_STATUS Status = RPC_S_OK;
CallAttributes =
(RPC_CALL_ATTRIBUTES_V1 *)RpcCallAttributes;
CallAttributes->AuthenticationLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
CallAttributes->AuthenticationService = RPC_C_AUTHN_WINNT;
CallAttributes->NullSession = FALSE;
if (CallAttributes->Flags & RPC_QUERY_CLIENT_PRINCIPAL_NAME)
{
Status = Association->GetClientName(this,
&CallAttributes->ClientPrincipalNameBufferLength,
&CallAttributes->ClientPrincipalName);
if ((Status != RPC_S_OK) && (Status != ERROR_MORE_DATA))
{
return Status;
}
}
if (CallAttributes->Flags & RPC_QUERY_SERVER_PRINCIPAL_NAME)
{
CallAttributes->ServerPrincipalNameBufferLength = 0;
}
return Status;
}
LRPC_SBINDING *
LRPC_SCALL::LookupBinding (
IN unsigned short PresentContextId
)
/*++
Function Name:LookupBinding
Parameters:
Description:
Returns:
--*/
{
LRPC_SBINDING *CurBinding;
DictionaryCursor cursor;
Association->Bindings.Reset(cursor);
while ((CurBinding = Association->Bindings.Next(cursor)))
{
if (CurBinding->GetPresentationContext() == PresentContextId)
{
return CurBinding;
}
}
return NULL;
}
LRPC_SCONTEXT::LRPC_SCONTEXT (
IN HANDLE MyToken,
IN LUID *UserLuid,
IN LRPC_SASSOCIATION *MyAssociation,
IN BOOL fDefaultLogonId,
IN BOOL fAnonymousToken
)
{
hToken = MyToken;
ClientName = NULL;
RefCount = 1;
ClearDeletedFlag();
Association = MyAssociation;
AuthzClientContext = NULL;
if (fAnonymousToken)
SetAnonymousFlag();
else
ClearAnonymousFlag();
if (fAnonymousToken)
{
ASSERT(fDefaultLogonId == FALSE);
ASSERT(UserLuid == NULL);
}
else
{
ASSERT(fAnonymousToken == FALSE);
if (fDefaultLogonId)
SetDefaultLogonIdFlag();
else
ClearDefaultLogonIdFlag();
FastCopyLUID(&ClientLuid, UserLuid);
}
}
LRPC_SCONTEXT::~LRPC_SCONTEXT (
void
)
{
if (hToken)
{
CloseHandle(hToken);
}
RpcpFarFree(ClientName);
if (AuthzClientContext)
{
AuthzFreeContextFn(AuthzClientContext);
AuthzClientContext = NULL;
}
if (GetServerSideOnlyFlag())
{
// if this is server side only context, remove us
// from the garbage collection count
InterlockedDecrement(&PeriodicGarbageCollectItems);
}
}
RPC_STATUS
LRPC_SCONTEXT::GetUserName (
IN OUT ULONG *ClientPrincipalNameBufferLength OPTIONAL,
OUT RPC_CHAR **UserName,
IN HANDLE hUserToken OPTIONAL
)
/*++
Routine Description:
Gets the user name for the given context.
Arguments:
ClientPrincipalNameBufferLength - if present, *UserName must
point to a caller supplied buffer, which if big enough,
will be filled with the client principal name. If not present,
*UserName must be NULL.
UserName - see ClientPrincipalNameBufferLength
hUserToken - if present, the user name for the given token will
be retrieved instead of the user name for the token inside
the LRPC_SCONTEXT
Return Value:
RPC_S_OK for success, or RPC_S_* / Win32 error code for error.
--*/
{
TOKEN_USER *pUser;
RPC_STATUS Status;
RPC_CHAR *ClientPrincipalName;
ULONG ClientPrincipalNameLength; // in bytes, including NULL terminator
if (ClientName == 0)
{
if (GetAnonymousFlag() == 0)
{
if (hUserToken == NULL)
{
ASSERT(hToken != NULL);
hUserToken = hToken;
}
pUser = GetSID(hUserToken);
if (pUser == 0)
{
return RPC_S_OUT_OF_MEMORY;
}
Status = LookupUser((SID *)pUser->User.Sid, &ClientPrincipalName);
delete pUser;
}
else
{
Status = LookupUser((SID *)&AnonymousSid, &ClientPrincipalName);
}
if (Status != RPC_S_OK)
{
return Status;
}
if (InterlockedCompareExchangePointer((PVOID *)&ClientName, ClientPrincipalName, NULL) != NULL)
{
// somebody beat us to the punch. Free the allocated string
delete ClientPrincipalName;
}
}
// at this stage, ClientName must contain the client principal name
ASSERT(ClientName);
// See where our caller wants us to put it
if (ARGUMENT_PRESENT(ClientPrincipalNameBufferLength))
{
// in the future, we may think of caching the length to avoid
// computing it every time
ClientPrincipalNameLength = (RpcpStringLength(ClientName) + 1) * sizeof(RPC_CHAR);
// if there is enough space in the data, copy it to user buffer
if (ClientPrincipalNameLength <= *ClientPrincipalNameBufferLength)
{
RpcpMemoryCopy(*UserName,
ClientName,
ClientPrincipalNameLength);
Status = RPC_S_OK;
}
else
{
Status = ERROR_MORE_DATA;
}
*ClientPrincipalNameBufferLength = ClientPrincipalNameLength;
return Status;
}
else
{
ASSERT(*UserName == NULL);
*UserName = ClientName;
}
return RPC_S_OK;
}
TOKEN_USER *
LRPC_SCONTEXT::GetSID (
IN HANDLE hToken
)
{
char *Buf = NULL;
ULONG Bufflen = 64 ;
ULONG Length;
Buf = new char[Bufflen];
if (Buf == 0)
{
return NULL;
}
while (1)
{
if (GetTokenInformation(hToken,
TokenUser, Buf, Bufflen,
&Length) == FALSE)
{
if (Length > Bufflen)
{
Bufflen = Length ;
delete Buf;
Buf = new char[Bufflen];
if (Buf == 0)
{
return NULL;
}
continue;
}
else
{
#if DBG
PrintToDebugger("LRPC: GetTokenInformation failed\n") ;
#endif
return NULL;
}
}
break;
}
return (TOKEN_USER *) Buf;
}
RPC_STATUS
LRPC_SCONTEXT::LookupUser (
IN SID *pSid,
OUT RPC_CHAR **UserName
)
{
unsigned long UserLength = USER_NAME_LEN ;
unsigned long OldDomainLen, OldUserLen;
unsigned long DomainLen = DOMAIN_NAME_LEN ;
RPC_CHAR *DomainName = NULL, *MyUserName = NULL;
SID_NAME_USE Name ;
RPC_STATUS Status = RPC_S_OK ;
MyUserName = new RPC_CHAR[UserLength];
if (MyUserName == 0)
{
Status = RPC_S_OUT_OF_MEMORY ;
goto Cleanup ;
}
DomainLen += UserLength ;
DomainName = new RPC_CHAR [DomainLen];
if (DomainName == 0)
{
Status = RPC_S_OUT_OF_MEMORY ;
goto Cleanup ;
}
OldDomainLen = DomainLen ;
OldUserLen = UserLength ;
while (1)
{
if (LookupAccountSidW(NULL, pSid,
MyUserName, &UserLength,
DomainName, &DomainLen, &Name) == FALSE)
{
if ((UserLength > OldUserLen) || (DomainLen > OldDomainLen))
{
if (UserLength > OldUserLen)
{
OldUserLen = UserLength ;
delete MyUserName;
MyUserName = new RPC_CHAR[UserLength];
if (MyUserName == 0)
{
Status = RPC_S_OUT_OF_MEMORY ;
goto Cleanup ;
}
}
if (DomainLen > OldDomainLen)
{
DomainLen += UserLength;
OldDomainLen = DomainLen;
delete DomainName;
DomainName = new RPC_CHAR[DomainLen];
if (DomainName == 0)
{
Status = RPC_S_OUT_OF_MEMORY ;
goto Cleanup ;
}
}
continue;
}
else
{
#if DBG
PrintToDebugger("LRPC: LookupAccountSid failed\n");
#endif
Status = RPC_S_UNKNOWN_PRINCIPAL;
goto Cleanup ;
}
}
break;
}
RpcpStringConcatenate(DomainName, RPC_CONST_STRING("\\")) ;
RpcpStringConcatenate(DomainName, MyUserName) ;
delete MyUserName;
*UserName = DomainName ;
ASSERT(Status == RPC_S_OK);
Cleanup:
if (Status)
{
if (MyUserName)
delete MyUserName;
if (DomainName)
delete DomainName;
return Status ;
}
return RPC_S_OK;
}
LRPC_ADDRESS *LrpcAddressList = NULL;
RPC_ADDRESS *
LrpcCreateRpcAddress (
)
/*++
Routine Description:
We just to create a new LRPC_ADDRESS. This routine is a proxy for the
new constructor to isolate the other modules.
--*/
{
RPC_STATUS Status = RPC_S_OK;
RPC_ADDRESS * RpcAddress;
RpcAddress = new LRPC_ADDRESS(&Status);
if (Status != RPC_S_OK)
{
return(0);
}
return(RpcAddress);
}
/*
This private API was requested by KumarP from the LSA group on 04/05/2000.
Here's his justification:
I am adding a new auditing feature to LSA that will allow any local process
to make an rpc call to LSA and generate an arbitrary audit. To be able to
make this call, the clients will first issue one call to get an audit-context
handle from LSA. LSA will maintain a list of handles till the client
explicitly closes the audit-context.
The reason I would like to have this API is to track which processes have
opened audit-contexts. This will help in situations where there is a
rogue/mal-functioning process that opens up a large number of audit-contexts.
In this case, I should be able to break LSA into debugger and dump the context
list and know which process has opened which handles. This may optionally
allow me to prevent certain processes from calling this API (though currently
there is no such requirement).
*/
RPC_STATUS
RPC_ENTRY
I_RpcBindingInqLocalClientPID (
IN RPC_BINDING_HANDLE Binding,
OUT unsigned long *Pid
)
{
LRPC_SCALL * Call;
HANDLE LocalPid;
InitializeIfNecessary();
if (Binding == NULL)
{
Call = (LRPC_SCALL *) RpcpGetThreadContext();
if (Call == NULL)
return RPC_S_NO_CALL_ACTIVE;
}
else
{
Call = (LRPC_SCALL *) Binding;
}
if (Call->InvalidHandle(LRPC_SCALL_TYPE))
return(RPC_S_INVALID_BINDING);
LocalPid = Call->InqLocalClientPID();
*Pid = HandleToUlong(LocalPid);
return RPC_S_OK;
}
const SID AnonymousSid = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_ANONYMOUS_LOGON_RID};