2073 lines
58 KiB
C
2073 lines
58 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
lpcsend.c
|
||
|
||
Abstract:
|
||
|
||
Local Inter-Process Communication (LPC) request system services.
|
||
|
||
Author:
|
||
|
||
Steve Wood (stevewo) 15-May-1989
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "lpcp.h"
|
||
|
||
NTSTATUS
|
||
LpcpRequestWaitReplyPort (
|
||
IN PVOID PortAddress,
|
||
IN PPORT_MESSAGE RequestMessage,
|
||
OUT PPORT_MESSAGE ReplyMessage,
|
||
IN KPROCESSOR_MODE AccessMode
|
||
);
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,NtRequestPort)
|
||
#pragma alloc_text(PAGE,NtRequestWaitReplyPort)
|
||
#pragma alloc_text(PAGE,LpcRequestPort)
|
||
#pragma alloc_text(PAGE,LpcRequestWaitReplyPort)
|
||
#pragma alloc_text(PAGE,LpcpRequestWaitReplyPort)
|
||
#pragma alloc_text(PAGE,LpcRequestWaitReplyPortEx)
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
NtRequestPort (
|
||
IN HANDLE PortHandle,
|
||
IN PPORT_MESSAGE RequestMessage
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
A client and server process send datagram messages using this procedure.
|
||
|
||
The message pointed to by the RequestMessage parameter is placed in the
|
||
message queue of the port connected to the communication port specified
|
||
by the PortHandle parameter. This service returns an error if PortHandle
|
||
is invalid or if the MessageId field of the PortMessage structure is
|
||
non-zero.
|
||
|
||
Arguments:
|
||
|
||
PortHandle - Specifies the handle of the communication port to send
|
||
the request message to.
|
||
|
||
RequestMessage - Specifies a pointer to the request message. The Type
|
||
field of the message is set to LPC_DATAGRAM by the service.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - A status code that indicates whether or not the operation was
|
||
successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PETHREAD CurrentThread;
|
||
PLPCP_PORT_OBJECT PortObject;
|
||
PLPCP_PORT_OBJECT QueuePort;
|
||
PORT_MESSAGE CapturedRequestMessage;
|
||
ULONG MsgType;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
PLPCP_MESSAGE Msg;
|
||
PLPCP_PORT_OBJECT ConnectionPort = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get previous processor mode and validate parameters
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
try {
|
||
|
||
ProbeForReadSmallStructure( RequestMessage,
|
||
sizeof( *RequestMessage ),
|
||
PROBE_ALIGNMENT (PORT_MESSAGE));
|
||
|
||
CapturedRequestMessage = *RequestMessage;
|
||
CapturedRequestMessage.u2.s2.Type &= ~LPC_KERNELMODE_MESSAGE;
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
if (CapturedRequestMessage.u2.s2.Type != 0) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is a kernel mode caller
|
||
//
|
||
|
||
CapturedRequestMessage = *RequestMessage;
|
||
|
||
if ((CapturedRequestMessage.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != 0) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Make sure that the caller has given us some data to send
|
||
//
|
||
|
||
if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Make sure DataLength is valid with respect to header size and total length
|
||
//
|
||
|
||
if ((((CLONG)CapturedRequestMessage.u1.s1.DataLength) + sizeof( PORT_MESSAGE )) >
|
||
((CLONG)CapturedRequestMessage.u1.s1.TotalLength)) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Reference the communication port object by handle. Return status if
|
||
// unsuccessful.
|
||
//
|
||
|
||
Status = LpcpReferencePortObject( PortHandle,
|
||
0,
|
||
PreviousMode,
|
||
&PortObject );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Validate the message length
|
||
//
|
||
|
||
if (((ULONG)CapturedRequestMessage.u1.s1.TotalLength > PortObject->MaxMessageLength) ||
|
||
((ULONG)CapturedRequestMessage.u1.s1.TotalLength <= (ULONG)CapturedRequestMessage.u1.s1.DataLength)) {
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_PORT_MESSAGE_TOO_LONG;
|
||
}
|
||
|
||
//
|
||
// Determine which port to queue the message to and get client
|
||
// port context if client sending to server. Also validate
|
||
// length of message being sent.
|
||
//
|
||
|
||
//
|
||
// Allocate and initialize the LPC message to send off
|
||
//
|
||
|
||
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedRequestMessage.u1.s1.TotalLength );
|
||
|
||
if (Msg == NULL) {
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
Msg->RepliedToThread = NULL;
|
||
Msg->PortContext = NULL;
|
||
MsgType = CapturedRequestMessage.u2.s2.Type | LPC_DATAGRAM;
|
||
|
||
CurrentThread = PsGetCurrentThread();
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
try {
|
||
|
||
LpcpMoveMessage( &Msg->Request,
|
||
&CapturedRequestMessage,
|
||
(RequestMessage + 1),
|
||
MsgType,
|
||
&CurrentThread->Cid );
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
Status = GetExceptionCode();
|
||
|
||
LpcpFreeToPortZone( Msg, 0 );
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return Status;
|
||
}
|
||
|
||
}
|
||
else {
|
||
|
||
LpcpMoveMessage( &Msg->Request,
|
||
&CapturedRequestMessage,
|
||
(RequestMessage + 1),
|
||
MsgType,
|
||
&CurrentThread->Cid );
|
||
}
|
||
|
||
//
|
||
// Acquire the global Lpc mutex that guards the LpcReplyMessage
|
||
// field of the thread and the request message queue. Stamp the
|
||
// request message with a serial number, insert the message at
|
||
// the tail of the request message queue and remember the address
|
||
// of the message in the LpcReplyMessage field for the current thread.
|
||
//
|
||
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
//
|
||
// Based on what type of port the caller gave us we'll need to get
|
||
// the port to actually queue the message off to.
|
||
//
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
|
||
|
||
//
|
||
// The caller didn't give us a connection port so find the
|
||
// connection port for this port. If it is null then we'll
|
||
// fall through without sending a message
|
||
//
|
||
|
||
QueuePort = PortObject->ConnectedPort;
|
||
|
||
//
|
||
// Check if the queue port is in process of going away
|
||
//
|
||
|
||
if ( QueuePort != NULL) {
|
||
|
||
//
|
||
// If the port is a client communication port then give the
|
||
// message the proper port context
|
||
//
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
|
||
|
||
Msg->PortContext = QueuePort->PortContext;
|
||
ConnectionPort = QueuePort = PortObject->ConnectionPort;
|
||
|
||
if (ConnectionPort == NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
|
||
//
|
||
// In the case we don't have a CLIENT_COMMUNICATION_PORT nor
|
||
// SERVER_COMMUNICATION_PORT we'll use the connection port
|
||
// to queue messages.
|
||
//
|
||
|
||
} else if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
|
||
|
||
ConnectionPort = QueuePort = PortObject->ConnectionPort;
|
||
|
||
if (ConnectionPort == NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
}
|
||
|
||
if (ConnectionPort) {
|
||
|
||
ObReferenceObject( ConnectionPort );
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// The caller supplied a server connection port so that is the port
|
||
// we queue off to
|
||
//
|
||
|
||
QueuePort = PortObject;
|
||
}
|
||
|
||
//
|
||
// At this point we have an LPC message ready to send and if queue port is
|
||
// not null then we have a port to actually send the message off to
|
||
//
|
||
|
||
if (QueuePort != NULL) {
|
||
|
||
//
|
||
// Reference the QueuePort to prevent this port evaporating under us
|
||
// Test if the QueuePort isn't in the process of going away
|
||
// (i.e. we need to have at least 2 references for this object when
|
||
// ObReferenceObject returns). Note the LPC lock is still held.
|
||
//
|
||
|
||
if ( ObReferenceObjectSafe( QueuePort ) ) {
|
||
|
||
//
|
||
// Finish filling in the message and then insert it in the queue
|
||
//
|
||
|
||
Msg->Request.MessageId = LpcpGenerateMessageId();
|
||
Msg->Request.CallbackId = 0;
|
||
Msg->SenderPort = PortObject;
|
||
|
||
CurrentThread->LpcReplyMessageId = 0;
|
||
|
||
InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
|
||
|
||
LpcpTrace(( "%s Send DataGram (%s) Msg %lx [%08x %08x %08x %08x] to Port %lx (%s)\n",
|
||
PsGetCurrentProcess()->ImageFileName,
|
||
LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
|
||
Msg,
|
||
*((PULONG)(Msg+1)+0),
|
||
*((PULONG)(Msg+1)+1),
|
||
*((PULONG)(Msg+1)+2),
|
||
*((PULONG)(Msg+1)+3),
|
||
QueuePort,
|
||
LpcpGetCreatorName( QueuePort )));
|
||
|
||
//
|
||
// Release the mutex, increment the request message queue
|
||
// semaphore by one for the newly inserted request message
|
||
// then exit the critical region.
|
||
//
|
||
// Disable APCs to prevent this thread from being suspended
|
||
// before being able to release the semaphore.
|
||
//
|
||
|
||
KeEnterCriticalRegionThread(&CurrentThread->Tcb);
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
KeReleaseSemaphore( QueuePort->MsgQueue.Semaphore,
|
||
LPC_RELEASE_WAIT_INCREMENT,
|
||
1L,
|
||
FALSE );
|
||
|
||
//
|
||
// If this is a waitable port then we'll need to set the event for
|
||
// anyone that was waiting on the port
|
||
//
|
||
|
||
if ( QueuePort->Flags & PORT_WAITABLE ) {
|
||
|
||
KeSetEvent( &QueuePort->WaitEvent,
|
||
LPC_RELEASE_WAIT_INCREMENT,
|
||
FALSE );
|
||
}
|
||
|
||
//
|
||
// Exit the critical region and release the port object
|
||
//
|
||
|
||
KeLeaveCriticalRegionThread(&CurrentThread->Tcb);
|
||
|
||
if (ConnectionPort) {
|
||
|
||
ObDereferenceObject( ConnectionPort );
|
||
}
|
||
|
||
ObDereferenceObject( QueuePort );
|
||
ObDereferenceObject( PortObject );
|
||
|
||
//
|
||
// And return to our caller. This is the only successful way out
|
||
// of this routine
|
||
//
|
||
|
||
return Status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// At this point we have a message but not a valid port to queue it off
|
||
// to so we'll free up the port object and release the unused message.
|
||
//
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
if (ConnectionPort) {
|
||
|
||
ObDereferenceObject( ConnectionPort );
|
||
}
|
||
|
||
//
|
||
// And return the error status to our caller
|
||
//
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtRequestWaitReplyPort (
|
||
IN HANDLE PortHandle,
|
||
IN PPORT_MESSAGE RequestMessage,
|
||
OUT PPORT_MESSAGE ReplyMessage
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
A client and server process can send a request and wait for a reply using
|
||
the NtRequestWaitReplyPort service.
|
||
|
||
If the Type field of the RequestMessage structure is euqal to LPC_REQUEST,
|
||
then this is identified as a callback request. The ClientId and MessageId
|
||
fields are used to identify the thread that is waiting for a reply. This
|
||
thread is unblocked and the current thread that called this service then
|
||
blocks waiting for a reply.
|
||
|
||
The Type field of the message is set to LPC_REQUEST by the service.
|
||
Otherwise the Type field of the message must be zero and it will be set to
|
||
LPC_REQUEST by the service. The message pointed to by the RequestMessage
|
||
parameter is placed in the message queue of the port connected to the
|
||
communication port specified by the PortHandle parameter. This service
|
||
returns an error if PortHandle is invalid. The calling thread then blocks
|
||
waiting for a reply.
|
||
|
||
The reply message is stored in the location pointed to by the ReplyMessage
|
||
parameter. The ClientId, MessageId and message type fields will be filled
|
||
in by the service.
|
||
|
||
Arguments:
|
||
|
||
PortHandle - Specifies the handle of the communication port to send the
|
||
request message to.
|
||
|
||
RequestMessage - Specifies a pointer to a request message to send.
|
||
|
||
ReplyMessage - Specifies the address of a variable that will receive the
|
||
reply message. This parameter may point to the same buffer as the
|
||
RequestMessage parameter.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - A status code that indicates whether or not the operation was
|
||
successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLPCP_PORT_OBJECT PortObject;
|
||
PLPCP_PORT_OBJECT QueuePort;
|
||
PLPCP_PORT_OBJECT RundownPort;
|
||
PORT_MESSAGE CapturedRequestMessage;
|
||
ULONG MsgType;
|
||
PKSEMAPHORE ReleaseSemaphore;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
PLPCP_MESSAGE Msg;
|
||
PETHREAD CurrentThread;
|
||
PETHREAD WakeupThread;
|
||
BOOLEAN CallbackRequest;
|
||
PORT_DATA_INFORMATION CapturedDataInfo;
|
||
PLPCP_PORT_OBJECT ConnectionPort = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// We cannot wait for a reply if the current thread is exiting
|
||
//
|
||
|
||
CurrentThread = PsGetCurrentThread();
|
||
|
||
if (CurrentThread->LpcExitThreadCalled) {
|
||
|
||
return STATUS_THREAD_IS_TERMINATING;
|
||
}
|
||
|
||
//
|
||
// Get previous processor mode and probe output arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
try {
|
||
|
||
ProbeForReadSmallStructure( RequestMessage,
|
||
sizeof( *RequestMessage ),
|
||
PROBE_ALIGNMENT (PORT_MESSAGE));
|
||
|
||
CapturedRequestMessage = *RequestMessage;
|
||
CapturedRequestMessage.u2.s2.Type &= ~LPC_KERNELMODE_MESSAGE;
|
||
|
||
ProbeForWriteSmallStructure( ReplyMessage,
|
||
sizeof( *ReplyMessage ),
|
||
PROBE_ALIGNMENT (PORT_MESSAGE));
|
||
|
||
//
|
||
// Make sure that if this message has a data info offset that
|
||
// the port data information actually fits in the message.
|
||
//
|
||
// We first check that the DataInfoOffset doesn't put us beyond
|
||
// the end of the message.
|
||
//
|
||
// Then we capture the data info record and compute a pointer to
|
||
// the first unused data entry based on the supplied count. If
|
||
// the start of the message plus its total length doesn't come
|
||
// up to the first unused data entry then the last valid data
|
||
// entry doesn't fit in the message buffer. Also if the data
|
||
// entry pointer that we compute is less than the data info
|
||
// pointer then we must have wrapped.
|
||
//
|
||
|
||
if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
|
||
|
||
PPORT_DATA_INFORMATION DataInfo;
|
||
PPORT_DATA_ENTRY DataEntry;
|
||
|
||
if (((ULONG)CapturedRequestMessage.u2.s2.DataInfoOffset) > (CapturedRequestMessage.u1.s1.TotalLength - sizeof(PORT_DATA_INFORMATION))) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if ((ULONG)CapturedRequestMessage.u2.s2.DataInfoOffset < sizeof(PORT_MESSAGE)) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
DataInfo = (PPORT_DATA_INFORMATION)(((PUCHAR)RequestMessage) + CapturedRequestMessage.u2.s2.DataInfoOffset);
|
||
|
||
ProbeForReadSmallStructure( DataInfo,
|
||
sizeof( *DataInfo ),
|
||
PROBE_ALIGNMENT (PORT_DATA_INFORMATION));
|
||
|
||
CapturedDataInfo = *DataInfo;
|
||
|
||
DataEntry = &(DataInfo->DataEntries[CapturedDataInfo.CountDataEntries]);
|
||
|
||
|
||
//
|
||
// The computation of above address, could overflow. So we have to return STATUS_INVALID_PARAMETER if :
|
||
// CountDataEntries * sizeof(DataEntries) < CountDataEntries
|
||
//
|
||
// But CountDataEntries * sizeof(DataEntries) = DataEntry - DataInfo - sizeof(CountDataEntries)
|
||
// and the final condition will be:
|
||
//
|
||
// DataEntry - DataInfo - sizeof(CountDataEntries) < CountDataEntries
|
||
//
|
||
// That is equivalent with:
|
||
// DataEntry < DataInfo + sizeof(CountDataEntries) + CountDataEntries
|
||
//
|
||
// ( The condition (DataEntry < DataInfo) used in the previous versions
|
||
// is always verified by this one )
|
||
//
|
||
|
||
|
||
if (((PUCHAR)DataEntry < (PUCHAR)DataInfo + sizeof(CapturedDataInfo.CountDataEntries) + CapturedDataInfo.CountDataEntries) ||
|
||
((((PUCHAR)RequestMessage) + CapturedRequestMessage.u1.s1.TotalLength) < (PUCHAR)DataEntry)) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
} else {
|
||
|
||
CapturedRequestMessage = *RequestMessage;
|
||
|
||
if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
|
||
|
||
PPORT_DATA_INFORMATION DataInfo;
|
||
|
||
DataInfo = (PPORT_DATA_INFORMATION)(((PUCHAR)RequestMessage) + CapturedRequestMessage.u2.s2.DataInfoOffset);
|
||
|
||
CapturedDataInfo = *DataInfo;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the message type is an lpc request then say we need a callback.
|
||
// Otherwise if it not an lpc request and it is not a kernel mode message
|
||
// then it is an illegal parameter. A third case is if the type is
|
||
// a kernel mode message in which case we make it look like an lpc request
|
||
// but without the callback.
|
||
//
|
||
|
||
if ((CapturedRequestMessage.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) == LPC_REQUEST) {
|
||
|
||
CallbackRequest = TRUE;
|
||
|
||
} else if ((CapturedRequestMessage.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != 0) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
|
||
} else {
|
||
|
||
CapturedRequestMessage.u2.s2.Type |= LPC_REQUEST;
|
||
CallbackRequest = FALSE;
|
||
}
|
||
|
||
//
|
||
// Make sure DataLength is valid with respect to header size and total length
|
||
//
|
||
|
||
if ((((CLONG)CapturedRequestMessage.u1.s1.DataLength) + sizeof( PORT_MESSAGE )) >
|
||
((CLONG)CapturedRequestMessage.u1.s1.TotalLength)) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Reference the communication port object by handle. Return status if
|
||
// unsuccessful.
|
||
//
|
||
|
||
Status = LpcpReferencePortObject( PortHandle,
|
||
0,
|
||
PreviousMode,
|
||
&PortObject );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Validate the message length
|
||
//
|
||
|
||
if (((ULONG)CapturedRequestMessage.u1.s1.TotalLength > PortObject->MaxMessageLength) ||
|
||
((ULONG)CapturedRequestMessage.u1.s1.TotalLength <= (ULONG)CapturedRequestMessage.u1.s1.DataLength)) {
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_PORT_MESSAGE_TOO_LONG;
|
||
}
|
||
|
||
//
|
||
// Determine which port to queue the message to and get client
|
||
// port context if client sending to server. Also validate
|
||
// length of message being sent.
|
||
//
|
||
|
||
//
|
||
// Allocate and initialize the LPC message to send off
|
||
//
|
||
|
||
|
||
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedRequestMessage.u1.s1.TotalLength );
|
||
|
||
if (Msg == NULL) {
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
MsgType = CapturedRequestMessage.u2.s2.Type;
|
||
|
||
//
|
||
// Check if we need to do a callback
|
||
//
|
||
|
||
if (CallbackRequest) {
|
||
|
||
//
|
||
// Check for a valid request message id
|
||
//
|
||
|
||
if (CapturedRequestMessage.MessageId == 0) {
|
||
|
||
LpcpFreeToPortZone( Msg, 0 );
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Translate the ClientId from the request into a
|
||
// thread pointer. This is a referenced pointer to keep the thread
|
||
// from evaporating out from under us.
|
||
//
|
||
|
||
Status = PsLookupProcessThreadByCid( &CapturedRequestMessage.ClientId,
|
||
NULL,
|
||
&WakeupThread );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
LpcpFreeToPortZone( Msg, 0 );
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Acquire the mutex that guards the LpcReplyMessage field of
|
||
// the thread and get the pointer to the message that the thread
|
||
// is waiting for a reply to.
|
||
//
|
||
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
//
|
||
// See if the thread is waiting for a reply to the message
|
||
// specified on this call. If not then a bogus message has been
|
||
// specified, so release the mutex, dereference the thread
|
||
// and return failure.
|
||
//
|
||
|
||
if ((WakeupThread->LpcReplyMessageId != CapturedRequestMessage.MessageId)
|
||
|
||
||
|
||
|
||
((LpcpGetThreadMessage(WakeupThread) != NULL) &&
|
||
(LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
|
||
|
||
||
|
||
|
||
(!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY)) ) {
|
||
|
||
LpcpPrint(( "%s Attempted CallBack Request to Thread %lx (%s)\n",
|
||
PsGetCurrentProcess()->ImageFileName,
|
||
WakeupThread,
|
||
THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
|
||
|
||
LpcpPrint(( "failed. MessageId == %u Client Id: %x.%x\n",
|
||
CapturedRequestMessage.MessageId,
|
||
CapturedRequestMessage.ClientId.UniqueProcess,
|
||
CapturedRequestMessage.ClientId.UniqueThread ));
|
||
|
||
LpcpPrint(( " Thread MessageId == %u Client Id: %x.%x\n",
|
||
WakeupThread->LpcReplyMessageId,
|
||
WakeupThread->Cid.UniqueProcess,
|
||
WakeupThread->Cid.UniqueThread ));
|
||
|
||
#if DBG
|
||
if (LpcpStopOnReplyMismatch) {
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
#endif
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
ObDereferenceObject( WakeupThread );
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_REPLY_MESSAGE_MISMATCH;
|
||
}
|
||
|
||
//
|
||
// Copy over the text of the message
|
||
//
|
||
|
||
try {
|
||
|
||
LpcpMoveMessage( &Msg->Request,
|
||
&CapturedRequestMessage,
|
||
(RequestMessage + 1),
|
||
MsgType,
|
||
&CurrentThread->Cid );
|
||
|
||
if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
|
||
|
||
PPORT_DATA_INFORMATION DataInfo;
|
||
|
||
DataInfo = (PPORT_DATA_INFORMATION)(((PUCHAR)RequestMessage) + CapturedRequestMessage.u2.s2.DataInfoOffset);
|
||
|
||
if ( DataInfo->CountDataEntries != CapturedDataInfo.CountDataEntries ) {
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
ObDereferenceObject( WakeupThread );
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Under the protect of the global lock we'll get everything
|
||
// ready for the callback
|
||
//
|
||
|
||
QueuePort = NULL;
|
||
Msg->PortContext = NULL;
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) == SERVER_CONNECTION_PORT) {
|
||
|
||
RundownPort = PortObject;
|
||
|
||
} else {
|
||
|
||
RundownPort = PortObject->ConnectedPort;
|
||
|
||
if (RundownPort == NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
ObDereferenceObject( WakeupThread );
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
|
||
|
||
Msg->PortContext = RundownPort->PortContext;
|
||
}
|
||
}
|
||
|
||
Msg->Request.CallbackId = LpcpGenerateCallbackId();
|
||
|
||
LpcpTrace(( "%s CallBack Request (%s) Msg %lx (%u.%u) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
|
||
PsGetCurrentProcess()->ImageFileName,
|
||
LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
|
||
Msg,
|
||
Msg->Request.MessageId,
|
||
Msg->Request.CallbackId,
|
||
*((PULONG)(Msg+1)+0),
|
||
*((PULONG)(Msg+1)+1),
|
||
*((PULONG)(Msg+1)+2),
|
||
*((PULONG)(Msg+1)+3),
|
||
WakeupThread,
|
||
THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
|
||
|
||
//
|
||
// Add an extra reference so LpcExitThread does not evaporate
|
||
// the pointer before we get to the wait below
|
||
//
|
||
|
||
ObReferenceObject( WakeupThread );
|
||
|
||
Msg->RepliedToThread = WakeupThread;
|
||
|
||
WakeupThread->LpcReplyMessageId = 0;
|
||
WakeupThread->LpcReplyMessage = (PVOID)Msg;
|
||
|
||
//
|
||
// Remove the thread from the reply rundown list as we are sending a callback
|
||
//
|
||
|
||
if (!IsListEmpty( &WakeupThread->LpcReplyChain )) {
|
||
|
||
RemoveEntryList( &WakeupThread->LpcReplyChain );
|
||
|
||
InitializeListHead( &WakeupThread->LpcReplyChain );
|
||
}
|
||
|
||
CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
|
||
CurrentThread->LpcReplyMessage = NULL;
|
||
|
||
InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
|
||
|
||
LpcpSetPortToThread( CurrentThread, PortObject );
|
||
|
||
KeEnterCriticalRegionThread (&CurrentThread->Tcb);
|
||
LpcpReleaseLpcpLock();
|
||
|
||
//
|
||
// Wake up the thread that is waiting for an answer to its request
|
||
// inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort
|
||
//
|
||
|
||
ReleaseSemaphore = &WakeupThread->LpcReplySemaphore;
|
||
|
||
} else {
|
||
|
||
//
|
||
// A callback is not required, so continue setting up the
|
||
// lpc message
|
||
//
|
||
|
||
try {
|
||
|
||
LpcpMoveMessage( &Msg->Request,
|
||
&CapturedRequestMessage,
|
||
(RequestMessage + 1),
|
||
MsgType,
|
||
&CurrentThread->Cid );
|
||
|
||
if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
|
||
|
||
PPORT_DATA_INFORMATION DataInfo;
|
||
|
||
DataInfo = (PPORT_DATA_INFORMATION)(((PUCHAR)RequestMessage) + CapturedRequestMessage.u2.s2.DataInfoOffset);
|
||
|
||
if ( DataInfo->CountDataEntries != CapturedDataInfo.CountDataEntries ) {
|
||
|
||
LpcpFreeToPortZone( Msg, 0 );
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
LpcpFreeToPortZone( Msg, 0 );
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Acquire the global Lpc mutex that guards the LpcReplyMessage
|
||
// field of the thread and the request message queue. Stamp the
|
||
// request message with a serial number, insert the message at
|
||
// the tail of the request message queue and remember the address
|
||
// of the message in the LpcReplyMessage field for the current thread.
|
||
//
|
||
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
Msg->PortContext = NULL;
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
|
||
|
||
QueuePort = PortObject->ConnectedPort;
|
||
|
||
if (QueuePort == NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
|
||
RundownPort = QueuePort;
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
|
||
|
||
Msg->PortContext = QueuePort->PortContext;
|
||
ConnectionPort = QueuePort = PortObject->ConnectionPort;
|
||
|
||
if (ConnectionPort == NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
|
||
} else if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
|
||
|
||
ConnectionPort = QueuePort = PortObject->ConnectionPort;
|
||
|
||
if (ConnectionPort == NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
}
|
||
|
||
if (ConnectionPort) {
|
||
|
||
ObReferenceObject( ConnectionPort );
|
||
}
|
||
|
||
} else {
|
||
|
||
QueuePort = PortObject;
|
||
RundownPort = PortObject;
|
||
}
|
||
|
||
//
|
||
// Stamp the request message with a serial number, insert the message
|
||
// at the tail of the request message queue
|
||
//
|
||
|
||
Msg->RepliedToThread = NULL;
|
||
Msg->Request.MessageId = LpcpGenerateMessageId();
|
||
Msg->Request.CallbackId = 0;
|
||
Msg->SenderPort = PortObject;
|
||
|
||
CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
|
||
CurrentThread->LpcReplyMessage = NULL;
|
||
|
||
InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
|
||
InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
|
||
|
||
LpcpSetPortToThread(CurrentThread, PortObject);
|
||
|
||
|
||
LpcpTrace(( "%s Send Request (%s) Msg %lx (%u) [%08x %08x %08x %08x] to Port %lx (%s)\n",
|
||
PsGetCurrentProcess()->ImageFileName,
|
||
LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
|
||
Msg,
|
||
Msg->Request.MessageId,
|
||
*((PULONG)(Msg+1)+0),
|
||
*((PULONG)(Msg+1)+1),
|
||
*((PULONG)(Msg+1)+2),
|
||
*((PULONG)(Msg+1)+3),
|
||
QueuePort,
|
||
LpcpGetCreatorName( QueuePort )));
|
||
|
||
KeEnterCriticalRegionThread (&CurrentThread->Tcb);
|
||
LpcpReleaseLpcpLock();
|
||
|
||
//
|
||
// Increment the request message queue semaphore by one for
|
||
// the newly inserted request message.
|
||
//
|
||
|
||
ReleaseSemaphore = QueuePort->MsgQueue.Semaphore;
|
||
|
||
//
|
||
// If port is waitable then set the event that someone could
|
||
// be waiting on
|
||
//
|
||
|
||
if ( QueuePort->Flags & PORT_WAITABLE ) {
|
||
|
||
KeSetEvent( &QueuePort->WaitEvent,
|
||
LPC_RELEASE_WAIT_INCREMENT,
|
||
FALSE );
|
||
}
|
||
}
|
||
|
||
//
|
||
// At this point we've enqueued our request and if necessary
|
||
// set ourselves up for the callback or reply.
|
||
//
|
||
// So now wake up the other end
|
||
//
|
||
|
||
Status = KeReleaseSemaphore( ReleaseSemaphore,
|
||
1,
|
||
1,
|
||
FALSE );
|
||
|
||
KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
|
||
if (CallbackRequest) {
|
||
|
||
ObDereferenceObject( WakeupThread );
|
||
}
|
||
|
||
//
|
||
// And wait for a reply
|
||
//
|
||
|
||
Status = KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
|
||
WrLpcReply,
|
||
PreviousMode,
|
||
FALSE,
|
||
NULL );
|
||
|
||
if (Status == STATUS_USER_APC) {
|
||
|
||
//
|
||
// if the semaphore is signaled, then clear it
|
||
//
|
||
|
||
if (KeReadStateSemaphore( &CurrentThread->LpcReplySemaphore )) {
|
||
|
||
KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
|
||
WrExecutive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL );
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Acquire the LPC mutex. Remove the reply message from the current thread
|
||
//
|
||
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
Msg = LpcpGetThreadMessage(CurrentThread);
|
||
|
||
CurrentThread->LpcReplyMessage = NULL;
|
||
CurrentThread->LpcReplyMessageId = 0;
|
||
|
||
//
|
||
// Remove the thread from the reply rundown list in case we did not wakeup due to
|
||
// a reply
|
||
//
|
||
|
||
if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
|
||
|
||
RemoveEntryList( &CurrentThread->LpcReplyChain );
|
||
|
||
InitializeListHead( &CurrentThread->LpcReplyChain );
|
||
}
|
||
|
||
#if DBG
|
||
if (Status == STATUS_SUCCESS && Msg != NULL) {
|
||
|
||
LpcpTrace(( "%s Got Reply Msg %lx (%u) [%08x %08x %08x %08x] for Thread %lx (%s)\n",
|
||
PsGetCurrentProcess()->ImageFileName,
|
||
Msg,
|
||
Msg->Request.MessageId,
|
||
*((PULONG)(Msg+1)+0),
|
||
*((PULONG)(Msg+1)+1),
|
||
*((PULONG)(Msg+1)+2),
|
||
*((PULONG)(Msg+1)+3),
|
||
CurrentThread,
|
||
THREAD_TO_PROCESS( CurrentThread )->ImageFileName ));
|
||
|
||
if (!IsListEmpty( &Msg->Entry )) {
|
||
|
||
LpcpTrace(( "Reply Msg %lx has non-empty list entry\n", Msg ));
|
||
}
|
||
}
|
||
#endif
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
//
|
||
// If the wait succeeded, copy the reply to the reply buffer.
|
||
//
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
|
||
if (Msg != NULL) {
|
||
|
||
try {
|
||
|
||
LpcpMoveMessage( ReplyMessage,
|
||
&Msg->Request,
|
||
(&Msg->Request) + 1,
|
||
0,
|
||
NULL );
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Acquire the LPC mutex and decrement the reference count for the
|
||
// message. If the reference count goes to zero the message will be
|
||
// deleted.
|
||
//
|
||
|
||
if (((Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) == LPC_REQUEST) &&
|
||
(Msg->Request.u2.s2.DataInfoOffset != 0)) {
|
||
|
||
LpcpSaveDataInfoMessage( PortObject, Msg, 0 );
|
||
|
||
} else {
|
||
|
||
LpcpFreeToPortZone( Msg, 0 );
|
||
}
|
||
|
||
} else {
|
||
|
||
Status = STATUS_LPC_REPLY_LOST;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Wait failed, free the message if any.
|
||
//
|
||
|
||
LpcpTrace(( "%s NtRequestWaitReply wait failed - Status == %lx\n",
|
||
PsGetCurrentProcess()->ImageFileName,
|
||
Status ));
|
||
|
||
if (Msg != NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, 0);
|
||
}
|
||
}
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
if (ConnectionPort) {
|
||
|
||
ObDereferenceObject( ConnectionPort );
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
LpcRequestPort (
|
||
IN PVOID PortAddress,
|
||
IN PPORT_MESSAGE RequestMessage
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure is similar to NtRequestPort but without the Handle based
|
||
interface.
|
||
|
||
Arguments:
|
||
|
||
PortAddress - Supplies a pointer to the communication port to send
|
||
the request message to.
|
||
|
||
RequestMessage - Specifies a pointer to the request message. The Type
|
||
field of the message is set to LPC_DATAGRAM by the service.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - A status code that indicates whether or not the operation was
|
||
successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PETHREAD CurrentThread;
|
||
PLPCP_PORT_OBJECT PortObject = (PLPCP_PORT_OBJECT)PortAddress;
|
||
PLPCP_PORT_OBJECT QueuePort;
|
||
ULONG MsgType;
|
||
PLPCP_MESSAGE Msg;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PLPCP_PORT_OBJECT ConnectionPort = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get previous processor mode and validate parameters
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
if (RequestMessage->u2.s2.Type != 0) {
|
||
|
||
MsgType = RequestMessage->u2.s2.Type & ~LPC_KERNELMODE_MESSAGE;
|
||
|
||
if ((MsgType < LPC_DATAGRAM) ||
|
||
(MsgType > LPC_CLIENT_DIED)) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// If previous mode is kernel, allow the LPC_KERNELMODE_MESSAGE
|
||
// bit to be passed in message type field.
|
||
//
|
||
|
||
if ((PreviousMode == KernelMode) &&
|
||
(RequestMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE)) {
|
||
|
||
MsgType |= LPC_KERNELMODE_MESSAGE;
|
||
}
|
||
|
||
} else {
|
||
|
||
MsgType = LPC_DATAGRAM;
|
||
}
|
||
|
||
if (RequestMessage->u2.s2.DataInfoOffset != 0) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Validate the message length
|
||
//
|
||
|
||
if (((ULONG)RequestMessage->u1.s1.TotalLength > PortObject->MaxMessageLength) ||
|
||
((ULONG)RequestMessage->u1.s1.TotalLength <= (ULONG)RequestMessage->u1.s1.DataLength)) {
|
||
|
||
return STATUS_PORT_MESSAGE_TOO_LONG;
|
||
}
|
||
|
||
//
|
||
// Allocate a message block
|
||
//
|
||
|
||
|
||
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( RequestMessage->u1.s1.TotalLength );
|
||
|
||
if (Msg == NULL) {
|
||
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
//
|
||
// Fill in the message block.
|
||
//
|
||
|
||
Msg->RepliedToThread = NULL;
|
||
Msg->PortContext = NULL;
|
||
|
||
CurrentThread = PsGetCurrentThread();
|
||
|
||
LpcpMoveMessage( &Msg->Request,
|
||
RequestMessage,
|
||
(RequestMessage + 1),
|
||
MsgType,
|
||
&CurrentThread->Cid );
|
||
|
||
//
|
||
// Acquire the global Lpc mutex that guards the LpcReplyMessage
|
||
// field of the thread and the request message queue. Stamp the
|
||
// request message with a serial number, insert the message at
|
||
// the tail of the request message queue
|
||
//
|
||
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
|
||
|
||
QueuePort = PortObject->ConnectedPort;
|
||
|
||
if (QueuePort != NULL) {
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
|
||
|
||
Msg->PortContext = QueuePort->PortContext;
|
||
ConnectionPort = QueuePort = PortObject->ConnectionPort;
|
||
|
||
if (ConnectionPort == NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
|
||
//
|
||
// In the case we don't have a CLIENT_COMMUNICATION_PORT nor
|
||
// SERVER_COMMUNICATION_PORT we'll use the connection port
|
||
// to queue messages.
|
||
//
|
||
|
||
} else if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
|
||
|
||
QueuePort = PortObject->ConnectionPort;
|
||
|
||
if (ConnectionPort == NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
}
|
||
|
||
if (ConnectionPort) {
|
||
|
||
ObReferenceObject( ConnectionPort );
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
QueuePort = PortObject;
|
||
}
|
||
|
||
//
|
||
// At this point we have an LPC message ready to send and if queue port is
|
||
// not null then we have a port to actually send the message off to
|
||
//
|
||
|
||
if (QueuePort != NULL) {
|
||
|
||
Msg->Request.MessageId = LpcpGenerateMessageId();
|
||
Msg->Request.CallbackId = 0;
|
||
Msg->SenderPort = PortObject;
|
||
|
||
CurrentThread->LpcReplyMessageId = 0;
|
||
|
||
InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
|
||
|
||
LpcpTrace(( "%s Send DataGram (%s) Msg %lx [%08x %08x %08x %08x] to Port %lx (%s)\n",
|
||
PsGetCurrentProcess()->ImageFileName,
|
||
LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
|
||
Msg,
|
||
*((PULONG)(Msg+1)+0),
|
||
*((PULONG)(Msg+1)+1),
|
||
*((PULONG)(Msg+1)+2),
|
||
*((PULONG)(Msg+1)+3),
|
||
QueuePort,
|
||
LpcpGetCreatorName( QueuePort )));
|
||
|
||
//
|
||
// Release the mutex, increment the request message queue
|
||
// semaphore by one for the newly inserted request message,
|
||
// then exit the critical region.
|
||
//
|
||
// Disable APCs to prevent this thread from being suspended
|
||
// before being able to release the semaphore.
|
||
//
|
||
|
||
KeEnterCriticalRegionThread(&CurrentThread->Tcb);
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
KeReleaseSemaphore( QueuePort->MsgQueue.Semaphore,
|
||
LPC_RELEASE_WAIT_INCREMENT,
|
||
1L,
|
||
FALSE );
|
||
|
||
|
||
if ( QueuePort->Flags & PORT_WAITABLE ) {
|
||
|
||
KeSetEvent( &QueuePort->WaitEvent,
|
||
LPC_RELEASE_WAIT_INCREMENT,
|
||
FALSE );
|
||
}
|
||
|
||
KeLeaveCriticalRegionThread(&CurrentThread->Tcb);
|
||
|
||
if (ConnectionPort) {
|
||
|
||
ObDereferenceObject( ConnectionPort );
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// At this point we have a message but not a valid port to queue it off
|
||
// to so we'll release the unused message and release our mutex.
|
||
//
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
if (ConnectionPort) {
|
||
|
||
ObDereferenceObject( ConnectionPort );
|
||
}
|
||
|
||
//
|
||
// And return the error status to our caller
|
||
//
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
LpcpRequestWaitReplyPort (
|
||
IN PVOID PortAddress,
|
||
IN PPORT_MESSAGE RequestMessage,
|
||
OUT PPORT_MESSAGE ReplyMessage,
|
||
IN KPROCESSOR_MODE AccessMode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure is similar to NtRequestWaitReplyPort but without the
|
||
handle based interface
|
||
|
||
Arguments:
|
||
|
||
PortAddress - Supplies the communication port object to send the
|
||
request message to.
|
||
|
||
RequestMessage - Specifies a pointer to a request message to send.
|
||
|
||
ReplyMessage - Specifies the address of a variable that will receive the
|
||
reply message. This parameter may point to the same buffer as the
|
||
RequestMessage parameter.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - A status code that indicates whether or not the operation was
|
||
successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLPCP_PORT_OBJECT PortObject = (PLPCP_PORT_OBJECT)PortAddress;
|
||
PLPCP_PORT_OBJECT QueuePort;
|
||
PLPCP_PORT_OBJECT RundownPort;
|
||
PKSEMAPHORE ReleaseSemaphore;
|
||
NTSTATUS Status;
|
||
ULONG MsgType;
|
||
PLPCP_MESSAGE Msg;
|
||
PETHREAD CurrentThread;
|
||
PETHREAD WakeupThread;
|
||
BOOLEAN CallbackRequest;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PLPCP_PORT_OBJECT ConnectionPort = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
CurrentThread = PsGetCurrentThread();
|
||
|
||
if (CurrentThread->LpcExitThreadCalled) {
|
||
|
||
return STATUS_THREAD_IS_TERMINATING;
|
||
}
|
||
|
||
//
|
||
// Get previous processor mode and validate parameters
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
MsgType = RequestMessage->u2.s2.Type & ~LPC_KERNELMODE_MESSAGE;
|
||
CallbackRequest = FALSE;
|
||
|
||
switch (MsgType) {
|
||
|
||
case 0:
|
||
|
||
MsgType = LPC_REQUEST;
|
||
break;
|
||
|
||
case LPC_REQUEST:
|
||
|
||
CallbackRequest = TRUE;
|
||
break;
|
||
|
||
case LPC_CLIENT_DIED:
|
||
case LPC_PORT_CLOSED:
|
||
case LPC_EXCEPTION:
|
||
case LPC_DEBUG_EVENT:
|
||
case LPC_ERROR_EVENT:
|
||
|
||
break;
|
||
|
||
default :
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Allow the LPC_KERNELMODE_MESSAGE
|
||
// bit to be passed in message type field. Don't check the previous mode !!!
|
||
//
|
||
|
||
if ( RequestMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE) {
|
||
|
||
MsgType |= LPC_KERNELMODE_MESSAGE;
|
||
}
|
||
|
||
RequestMessage->u2.s2.Type = (CSHORT)MsgType;
|
||
|
||
//
|
||
// Validate the message length
|
||
//
|
||
|
||
if (((ULONG)RequestMessage->u1.s1.TotalLength > PortObject->MaxMessageLength) ||
|
||
((ULONG)RequestMessage->u1.s1.TotalLength <= (ULONG)RequestMessage->u1.s1.DataLength)) {
|
||
|
||
return STATUS_PORT_MESSAGE_TOO_LONG;
|
||
}
|
||
|
||
//
|
||
// Determine which port to queue the message to and get client
|
||
// port context if client sending to server. Also validate
|
||
// length of message being sent.
|
||
//
|
||
|
||
|
||
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( RequestMessage->u1.s1.TotalLength );
|
||
|
||
|
||
if (Msg == NULL) {
|
||
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
if (CallbackRequest) {
|
||
|
||
//
|
||
// Check for a valid request message id
|
||
//
|
||
|
||
if (RequestMessage->MessageId == 0) {
|
||
|
||
LpcpFreeToPortZone( Msg, 0 );
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Translate the ClientId from the request into a
|
||
// thread pointer. This is a referenced pointer to keep the thread
|
||
// from evaporating out from under us.
|
||
//
|
||
|
||
Status = PsLookupProcessThreadByCid( &RequestMessage->ClientId,
|
||
NULL,
|
||
&WakeupThread );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
LpcpFreeToPortZone( Msg, 0 );
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Acquire the mutex that guards the LpcReplyMessage field of
|
||
// the thread and get the pointer to the message that the thread
|
||
// is waiting for a reply to.
|
||
//
|
||
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
//
|
||
// See if the thread is waiting for a reply to the message
|
||
// specified on this call. If not then a bogus message
|
||
// has been specified, so release the mutex, dereference the thread
|
||
// and return failure.
|
||
//
|
||
|
||
if ((WakeupThread->LpcReplyMessageId != RequestMessage->MessageId)
|
||
|
||
||
|
||
|
||
((LpcpGetThreadMessage(WakeupThread) != NULL) &&
|
||
(LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
|
||
|
||
||
|
||
|
||
(!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY)) ) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
ObDereferenceObject( WakeupThread );
|
||
|
||
return STATUS_REPLY_MESSAGE_MISMATCH;
|
||
}
|
||
|
||
QueuePort = NULL;
|
||
Msg->PortContext = NULL;
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) == SERVER_CONNECTION_PORT) {
|
||
|
||
RundownPort = PortObject;
|
||
|
||
} else {
|
||
|
||
RundownPort = PortObject->ConnectedPort;
|
||
|
||
if (RundownPort == NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
ObDereferenceObject( WakeupThread );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
|
||
|
||
Msg->PortContext = RundownPort->PortContext;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Allocate and initialize a request message
|
||
//
|
||
|
||
LpcpMoveMessage( &Msg->Request,
|
||
RequestMessage,
|
||
(RequestMessage + 1),
|
||
0,
|
||
&CurrentThread->Cid );
|
||
|
||
Msg->Request.CallbackId = LpcpGenerateCallbackId();
|
||
|
||
LpcpTrace(( "%s CallBack Request (%s) Msg %lx (%u.%u) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
|
||
PsGetCurrentProcess()->ImageFileName,
|
||
LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
|
||
Msg,
|
||
Msg->Request.MessageId,
|
||
Msg->Request.CallbackId,
|
||
*((PULONG)(Msg+1)+0),
|
||
*((PULONG)(Msg+1)+1),
|
||
*((PULONG)(Msg+1)+2),
|
||
*((PULONG)(Msg+1)+3),
|
||
WakeupThread,
|
||
THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
|
||
|
||
//
|
||
// Add an extra reference so LpcExitThread does not evaporate
|
||
// the pointer before we get to the wait below
|
||
//
|
||
|
||
ObReferenceObject( WakeupThread );
|
||
|
||
Msg->RepliedToThread = WakeupThread;
|
||
|
||
WakeupThread->LpcReplyMessageId = 0;
|
||
WakeupThread->LpcReplyMessage = (PVOID)Msg;
|
||
|
||
//
|
||
// Remove the thread from the reply rundown list as we are sending a callback
|
||
//
|
||
|
||
if (!IsListEmpty( &WakeupThread->LpcReplyChain )) {
|
||
|
||
RemoveEntryList( &WakeupThread->LpcReplyChain );
|
||
|
||
InitializeListHead( &WakeupThread->LpcReplyChain );
|
||
}
|
||
|
||
CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
|
||
CurrentThread->LpcReplyMessage = NULL;
|
||
|
||
InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
|
||
|
||
LpcpSetPortToThread(CurrentThread, PortObject);
|
||
|
||
KeEnterCriticalRegionThread (&CurrentThread->Tcb);
|
||
LpcpReleaseLpcpLock();
|
||
|
||
//
|
||
// Wake up the thread that is waiting for an answer to its request
|
||
// inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort
|
||
//
|
||
|
||
ReleaseSemaphore = &WakeupThread->LpcReplySemaphore;
|
||
|
||
} else {
|
||
|
||
//
|
||
// There is no callback requested
|
||
//
|
||
|
||
LpcpMoveMessage( &Msg->Request,
|
||
RequestMessage,
|
||
(RequestMessage + 1),
|
||
0,
|
||
&CurrentThread->Cid );
|
||
|
||
//
|
||
// Acquire the global Lpc mutex that guards the LpcReplyMessage
|
||
// field of the thread and the request message queue. Stamp the
|
||
// request message with a serial number, insert the message at
|
||
// the tail of the request message queue and remember the address
|
||
// of the message in the LpcReplyMessage field for the current thread.
|
||
//
|
||
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
Msg->PortContext = NULL;
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
|
||
|
||
QueuePort = PortObject->ConnectedPort;
|
||
|
||
if (QueuePort == NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
|
||
RundownPort = QueuePort;
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
|
||
|
||
Msg->PortContext = QueuePort->PortContext;
|
||
ConnectionPort = QueuePort = PortObject->ConnectionPort;
|
||
|
||
if (ConnectionPort == NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
|
||
} else if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
|
||
|
||
ConnectionPort = QueuePort = PortObject->ConnectionPort;
|
||
|
||
if (ConnectionPort == NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
}
|
||
|
||
if (ConnectionPort) {
|
||
|
||
ObReferenceObject( ConnectionPort );
|
||
}
|
||
|
||
} else {
|
||
|
||
if ((PortObject->Flags & PORT_NAME_DELETED) != 0) {
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
return STATUS_PORT_DISCONNECTED;
|
||
}
|
||
|
||
QueuePort = PortObject;
|
||
RundownPort = PortObject;
|
||
}
|
||
|
||
Msg->RepliedToThread = NULL;
|
||
Msg->Request.MessageId = LpcpGenerateMessageId();
|
||
Msg->Request.CallbackId = 0;
|
||
Msg->SenderPort = PortObject;
|
||
|
||
CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
|
||
CurrentThread->LpcReplyMessage = NULL;
|
||
|
||
InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
|
||
InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
|
||
|
||
LpcpSetPortToThread(CurrentThread, PortObject);
|
||
|
||
LpcpTrace(( "%s Send Request (%s) Msg %lx (%u) [%08x %08x %08x %08x] to Port %lx (%s)\n",
|
||
PsGetCurrentProcess()->ImageFileName,
|
||
LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
|
||
Msg,
|
||
Msg->Request.MessageId,
|
||
*((PULONG)(Msg+1)+0),
|
||
*((PULONG)(Msg+1)+1),
|
||
*((PULONG)(Msg+1)+2),
|
||
*((PULONG)(Msg+1)+3),
|
||
QueuePort,
|
||
LpcpGetCreatorName( QueuePort )));
|
||
|
||
KeEnterCriticalRegionThread (&CurrentThread->Tcb);
|
||
LpcpReleaseLpcpLock();
|
||
|
||
//
|
||
// Increment the request message queue semaphore by one for
|
||
// the newly inserted request message. Release the spin
|
||
// lock, while remaining at the dispatcher IRQL. Then wait for the
|
||
// reply to this request by waiting on the LpcReplySemaphore
|
||
// for the current thread.
|
||
//
|
||
|
||
ReleaseSemaphore = QueuePort->MsgQueue.Semaphore;
|
||
|
||
if ( QueuePort->Flags & PORT_WAITABLE ) {
|
||
|
||
KeSetEvent( &QueuePort->WaitEvent,
|
||
LPC_RELEASE_WAIT_INCREMENT,
|
||
FALSE );
|
||
}
|
||
}
|
||
|
||
//
|
||
// At this point we've enqueued our request and if necessary
|
||
// set ourselves up for the callback or reply.
|
||
//
|
||
// So now wake up the other end
|
||
//
|
||
|
||
Status = KeReleaseSemaphore( ReleaseSemaphore,
|
||
1,
|
||
1,
|
||
FALSE );
|
||
KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
|
||
|
||
if (CallbackRequest) {
|
||
|
||
ObDereferenceObject( WakeupThread );
|
||
}
|
||
|
||
//
|
||
// And wait for a reply
|
||
//
|
||
|
||
Status = KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
|
||
WrLpcReply,
|
||
AccessMode,
|
||
FALSE,
|
||
NULL );
|
||
|
||
if (Status == STATUS_USER_APC) {
|
||
|
||
//
|
||
// if the semaphore is signaled, then clear it
|
||
//
|
||
|
||
if (KeReadStateSemaphore( &CurrentThread->LpcReplySemaphore )) {
|
||
|
||
KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
|
||
WrExecutive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL );
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Acquire the LPC mutex. Remove the reply message from the current thread
|
||
//
|
||
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
Msg = LpcpGetThreadMessage(CurrentThread);
|
||
|
||
CurrentThread->LpcReplyMessage = NULL;
|
||
CurrentThread->LpcReplyMessageId = 0;
|
||
|
||
//
|
||
// Remove the thread from the reply rundown list in case we did not wakeup due to
|
||
// a reply
|
||
//
|
||
|
||
if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
|
||
|
||
RemoveEntryList( &CurrentThread->LpcReplyChain );
|
||
|
||
InitializeListHead( &CurrentThread->LpcReplyChain );
|
||
}
|
||
|
||
#if DBG
|
||
if (Msg != NULL) {
|
||
|
||
LpcpTrace(( "%s Got Reply Msg %lx (%u) [%08x %08x %08x %08x] for Thread %lx (%s)\n",
|
||
PsGetCurrentProcess()->ImageFileName,
|
||
Msg,
|
||
Msg->Request.MessageId,
|
||
*((PULONG)(Msg+1)+0),
|
||
*((PULONG)(Msg+1)+1),
|
||
*((PULONG)(Msg+1)+2),
|
||
*((PULONG)(Msg+1)+3),
|
||
CurrentThread,
|
||
THREAD_TO_PROCESS( CurrentThread )->ImageFileName ));
|
||
}
|
||
#endif
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
//
|
||
// If the wait succeeded, copy the reply to the reply buffer.
|
||
//
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
|
||
if (Msg != NULL) {
|
||
|
||
LpcpMoveMessage( ReplyMessage,
|
||
&Msg->Request,
|
||
(&Msg->Request) + 1,
|
||
0,
|
||
NULL );
|
||
|
||
//
|
||
// Acquire the LPC mutex and decrement the reference count for the
|
||
// message. If the reference count goes to zero the message will be
|
||
// deleted.
|
||
//
|
||
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
if (Msg->RepliedToThread != NULL) {
|
||
|
||
ObDereferenceObject( Msg->RepliedToThread );
|
||
|
||
Msg->RepliedToThread = NULL;
|
||
}
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
} else {
|
||
|
||
Status = STATUS_LPC_REPLY_LOST;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Wait failed, free the message if any.
|
||
//
|
||
|
||
if (Msg != NULL) {
|
||
|
||
LpcpFreeToPortZone( Msg, 0 );
|
||
}
|
||
}
|
||
|
||
if (ConnectionPort) {
|
||
|
||
ObDereferenceObject( ConnectionPort );
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
LpcRequestWaitReplyPort (
|
||
IN PVOID PortAddress,
|
||
IN PPORT_MESSAGE RequestMessage,
|
||
OUT PPORT_MESSAGE ReplyMessage
|
||
)
|
||
{
|
||
return LpcpRequestWaitReplyPort( PortAddress,
|
||
RequestMessage,
|
||
ReplyMessage,
|
||
KernelMode
|
||
);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
LpcRequestWaitReplyPortEx (
|
||
IN PVOID PortAddress,
|
||
IN PPORT_MESSAGE RequestMessage,
|
||
OUT PPORT_MESSAGE ReplyMessage
|
||
)
|
||
{
|
||
return LpcpRequestWaitReplyPort( PortAddress,
|
||
RequestMessage,
|
||
ReplyMessage,
|
||
KeGetPreviousMode()
|
||
);
|
||
}
|
||
|