1082 lines
33 KiB
C
1082 lines
33 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
lpccompl.c
|
||
|
||
Abstract:
|
||
|
||
Local Inter-Process Communication (LPC) connection system services.
|
||
|
||
Author:
|
||
|
||
Steve Wood (stevewo) 15-May-1989
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "lpcp.h"
|
||
|
||
//
|
||
// Local procedure prototypes
|
||
//
|
||
|
||
VOID
|
||
LpcpPrepareToWakeClient (
|
||
IN PETHREAD ClientThread
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,NtAcceptConnectPort)
|
||
#pragma alloc_text(PAGE,NtCompleteConnectPort)
|
||
#pragma alloc_text(PAGE,LpcpPrepareToWakeClient)
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
NtAcceptConnectPort (
|
||
OUT PHANDLE PortHandle,
|
||
IN PVOID PortContext OPTIONAL,
|
||
IN PPORT_MESSAGE ConnectionRequest,
|
||
IN BOOLEAN AcceptConnection,
|
||
IN OUT PPORT_VIEW ServerView OPTIONAL,
|
||
OUT PREMOTE_PORT_VIEW ClientView OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
A server process can accept or reject a client connection request
|
||
using the NtAcceptConnectPort service.
|
||
|
||
The ConnectionRequest parameter must specify a connection request
|
||
returned by a previous call to the NtListenPort service. This
|
||
service will either complete the connection if the AcceptConnection
|
||
parameter is TRUE, or reject the connection request if the
|
||
AcceptConnection parameter is FALSE.
|
||
|
||
In either case, the contents of the data portion of the connection
|
||
request is the data to return to the caller of NtConnectPort.
|
||
|
||
If the connection request is accepted, then two communication port
|
||
objects will be created and connected together. One will be
|
||
inserted in the client process' handle table and returned to the
|
||
client via the PortHandle parameter it specified on the
|
||
NtConnectPort service. The other will be inserted in the server
|
||
process' handle table and returned via the PortHandle parameter
|
||
specified on the NtCompleteConnectPort service. In addition the
|
||
two communication ports (client and server) will be linked together.
|
||
|
||
If the connection request is accepted, and the ServerView parameter
|
||
was specified, then the section handle is examined. If it is valid,
|
||
then the portion of the section described by the SectionOffset and
|
||
ViewSize fields will be mapped into both the client and server
|
||
process address spaces. The address in server's address space will
|
||
be returned in the ViewBase field. The address in the client's
|
||
address space will be returned in the ViewRemoteBase field. The
|
||
actual offset and size used to map the section will be returned in
|
||
the SectionOffset and ViewSize fields.
|
||
|
||
Communication port objects are temporary objects that have no names
|
||
and cannot be inherited. When either the client or server process
|
||
calls the !f NtClose service for a communication port, the port will
|
||
be deleted since there can never be more than one outstanding handle
|
||
for each communication port. The port object type specific delete
|
||
procedure will then be invoked. This delete procedure will examine
|
||
the communication port, and if it is connected to another
|
||
communication port, it will queue an LPC_PORT_CLOSED datagram to
|
||
that port's message queue. This will allow both the client and
|
||
server processes to notice when a port becomes disconnected, either
|
||
because of an explicit call to NtClose or an implicit call due to
|
||
process termination. In addition, the delete procedure will scan
|
||
the message queue of the port being closed and for each message
|
||
still in the queue, it will return an ERROR_PORT_CLOSED status to
|
||
any thread that is waiting for a reply to the message.
|
||
|
||
Arguments:
|
||
|
||
PortHandle - A pointer to a variable that will receive the server
|
||
communication port object handle value.
|
||
|
||
PortContext - An uninterpreted pointer that is stored in the
|
||
server communication port. This pointer is returned whenever
|
||
a message is received for this port.
|
||
|
||
ConnectionRequest - A pointer to a structure that describes the
|
||
connection request being accepted or rejected:
|
||
|
||
The ConnectionRequest structure
|
||
|
||
ULONG Length - Specifies the size of this data structure in
|
||
bytes.
|
||
|
||
CLIENT_ID ClientId - Specifies a structure that contains the
|
||
client identifier (CLIENT_ID) of the thread that sent the
|
||
request.
|
||
|
||
The ClientId Structure
|
||
|
||
ULONG UniqueProcessId - A unique value for each process
|
||
in the system.
|
||
|
||
ULONG UniqueThreadId - A unique value for each thread in the
|
||
system.
|
||
|
||
ULONG MessageId - A unique value that identifies the connection
|
||
request being completed.
|
||
|
||
ULONG PortAttributes - This field has no meaning for this service.
|
||
|
||
ULONG ClientViewSize - This field has no meaning for this service.
|
||
|
||
AcceptConnection - Specifies a boolean value which indicates where
|
||
the connection request is being accepted or rejected. A value
|
||
of TRUE means that the connection request is accepted and a
|
||
server communication port handle will be created and connected
|
||
to the client's communication port handle. A value of FALSE
|
||
means that the connection request is not accepted.
|
||
|
||
ServerView - A pointer to a structure that specifies the section that
|
||
the server process will use to send messages back to the client
|
||
process connected to this port.
|
||
|
||
The ServerView Structure
|
||
|
||
ULONG Length - Specifies the size of this data structure in
|
||
bytes.
|
||
|
||
HANDLE SectionHandle - Specifies an open handle to a section
|
||
object.
|
||
|
||
ULONG SectionOffset - Specifies a field that will receive the
|
||
actual offset, in bytes, from the start of the section. The
|
||
initial value of this parameter specifies the byte offset
|
||
within the section that the client's view is based. The
|
||
value is rounded down to the next host page size boundary.
|
||
|
||
ULONG ViewSize - Specifies the size of the view, in bytes.
|
||
|
||
PVOID ViewBase - Specifies a field that will receive the base
|
||
address of the port memory in the server's address space.
|
||
|
||
PVOID ViewRemoteBase - Specifies a field that will receive
|
||
the base address of the server port's memory in the client's
|
||
address space. Used to generate pointers that are
|
||
meaningful to the client.
|
||
|
||
ClientView - An optional pointer to a structure that will receive
|
||
information about the client process' view in the server's
|
||
address space. The server process can use this information
|
||
to validate pointers it receives from the client process.
|
||
|
||
The ClientView Structure
|
||
|
||
ULONG Length - Specifies the size of this data structure in
|
||
bytes.
|
||
|
||
PVOID ViewBase - Specifies a field that will receive the base
|
||
address of the client port's memory in the server's address
|
||
space.
|
||
|
||
ULONG ViewSize - Specifies a field that will receive the
|
||
size, in bytes, of the client's view in the server's address
|
||
space. If this field is zero, then client has no view in
|
||
the server's address space.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - An appropriate status value.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLPCP_PORT_OBJECT ConnectionPort;
|
||
PLPCP_PORT_OBJECT ServerPort;
|
||
PLPCP_PORT_OBJECT ClientPort;
|
||
PVOID ClientSectionToMap;
|
||
HANDLE Handle;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
ULONG ConnectionInfoLength;
|
||
PLPCP_MESSAGE Msg;
|
||
PLPCP_CONNECTION_MESSAGE ConnectMsg;
|
||
PORT_MESSAGE CapturedReplyMessage;
|
||
PVOID SectionToMap;
|
||
LARGE_INTEGER SectionOffset;
|
||
SIZE_T ViewSize;
|
||
PEPROCESS ClientProcess;
|
||
PETHREAD ClientThread;
|
||
PORT_VIEW CapturedServerView;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get previous processor mode and probe output arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
try {
|
||
|
||
ProbeForWriteHandle( PortHandle );
|
||
|
||
ProbeForReadSmallStructure( ConnectionRequest,
|
||
sizeof( *ConnectionRequest ),
|
||
sizeof( ULONG ));
|
||
|
||
CapturedReplyMessage = *ConnectionRequest;
|
||
|
||
if (ARGUMENT_PRESENT( ServerView )) {
|
||
|
||
CapturedServerView = ProbeAndReadStructure( ServerView, PORT_VIEW );
|
||
|
||
if (CapturedServerView.Length != sizeof( *ServerView )) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
ProbeForWriteSmallStructure( ServerView,
|
||
sizeof( *ServerView ),
|
||
sizeof( ULONG ));
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT( ClientView )) {
|
||
|
||
if (ProbeAndReadUlong( &ClientView->Length ) != sizeof( *ClientView )) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
ProbeForWriteSmallStructure( ClientView,
|
||
sizeof( *ClientView ),
|
||
sizeof( ULONG ));
|
||
}
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Otherwise the previous mode is kernel mode
|
||
//
|
||
|
||
CapturedReplyMessage = *ConnectionRequest;
|
||
|
||
if (ARGUMENT_PRESENT( ServerView )) {
|
||
|
||
if (ServerView->Length != sizeof( *ServerView )) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
CapturedServerView = *ServerView;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT( ClientView )) {
|
||
|
||
if (ClientView->Length != sizeof( *ClientView )) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Translate the ClientId from the connection request into a
|
||
// thread pointer. This is a referenced pointer to keep the thread
|
||
// from evaporating out from under us.
|
||
//
|
||
|
||
Status = PsLookupProcessThreadByCid( &CapturedReplyMessage.ClientId,
|
||
&ClientProcess,
|
||
&ClientThread );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
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.
|
||
//
|
||
|
||
LpcpAcquireLpcpLock();
|
||
|
||
//
|
||
// See if the thread is waiting for a reply to the connection request
|
||
// specified on this call. If not then a bogus connection request
|
||
// has been specified, so release the mutex, dereference the thread
|
||
// and return failure.
|
||
//
|
||
// The check is that the client is waiting for a reply to a connection
|
||
// request and that the message id is both valid and lines up correctly
|
||
//
|
||
|
||
if (( LpcpGetThreadMessage( ClientThread ) == NULL ) ||
|
||
(CapturedReplyMessage.MessageId == 0) ||
|
||
(ClientThread->LpcReplyMessageId != CapturedReplyMessage.MessageId) ||
|
||
((LpcpGetThreadMessage(ClientThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_CONNECTION_REQUEST)) {
|
||
|
||
Msg = NULL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Remember the LPCP message from the thread
|
||
//
|
||
|
||
Msg = LpcpGetThreadMessage(ClientThread);
|
||
|
||
//
|
||
// Get connection message immediately following the LPCP message
|
||
//
|
||
|
||
ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(Msg + 1);
|
||
|
||
//
|
||
// Remember the client port from the connection message
|
||
//
|
||
|
||
ClientPort = ConnectMsg->ClientPort;
|
||
|
||
//
|
||
// Get a pointer to the connection port from the client port.
|
||
//
|
||
|
||
ConnectionPort = ClientPort->ConnectionPort;
|
||
|
||
//
|
||
// Check if the server process accept the connection
|
||
//
|
||
|
||
if ( ConnectionPort->ServerProcess != PsGetCurrentProcess() ) {
|
||
|
||
//
|
||
// Release the LPC mutex
|
||
//
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
ObDereferenceObject( ClientProcess );
|
||
ObDereferenceObject( ClientThread );
|
||
|
||
return (STATUS_REPLY_MESSAGE_MISMATCH);
|
||
}
|
||
|
||
//
|
||
// Remove the LPC message from the thread
|
||
//
|
||
|
||
ClientThread->LpcReplyMessage = NULL;
|
||
|
||
//
|
||
// Remove the client port from the connection message
|
||
//
|
||
|
||
ConnectMsg->ClientPort = NULL;
|
||
|
||
//
|
||
// Clean up the rest of the client thread. This cleanup use to be
|
||
// done unconditionally right before releasing the mutex however
|
||
// this causes trouble if our caller supplied a bad reply message
|
||
// and we clobber an arbitrary threads state
|
||
//
|
||
|
||
ClientThread->LpcReplyMessageId = 0;
|
||
}
|
||
|
||
//
|
||
// Release the mutex that guards the field.
|
||
//
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
//
|
||
// Now if we did not get an LPCP message from a client thread then this
|
||
// isn't a good call and we'll dereference what we thought was the
|
||
// client thread/process and tell our caller their mistake
|
||
//
|
||
|
||
if ( !Msg ) {
|
||
|
||
LpcpPrint(( "%s Attempted AcceptConnectPort to Thread %lx (%s)\n",
|
||
PsGetCurrentProcess()->ImageFileName,
|
||
ClientThread,
|
||
THREAD_TO_PROCESS( ClientThread )->ImageFileName ));
|
||
LpcpPrint(( "failed. MessageId == %u\n", CapturedReplyMessage.MessageId ));
|
||
LpcpPrint(( " Thread MessageId == %u\n", ClientThread->LpcReplyMessageId ));
|
||
LpcpPrint(( " Thread Msg == %x\n", ClientThread->LpcReplyMessage ));
|
||
|
||
ObDereferenceObject( ClientProcess );
|
||
ObDereferenceObject( ClientThread );
|
||
|
||
return (STATUS_REPLY_MESSAGE_MISMATCH);
|
||
}
|
||
|
||
//
|
||
// At this point we have a good matching client for this accept connect
|
||
// call.
|
||
//
|
||
|
||
LpcpTrace(("Replying to Connect Msg %lx to Port %lx\n",
|
||
Msg, ClientPort->ConnectionPort ));
|
||
|
||
//
|
||
// Regardless of whether we are accepting or rejecting the connection,
|
||
// return the connection information to the waiting thread.
|
||
//
|
||
|
||
ConnectionInfoLength = CapturedReplyMessage.u1.s1.DataLength;
|
||
|
||
if (ConnectionInfoLength > ConnectionPort->MaxConnectionInfoLength) {
|
||
|
||
ConnectionInfoLength = ConnectionPort->MaxConnectionInfoLength;
|
||
}
|
||
|
||
Msg->Request.u1.s1.DataLength = (CSHORT)(sizeof( *ConnectMsg ) +
|
||
ConnectionInfoLength);
|
||
|
||
Msg->Request.u1.s1.TotalLength = (CSHORT)(sizeof( *Msg ) +
|
||
Msg->Request.u1.s1.DataLength);
|
||
|
||
Msg->Request.u2.s2.Type = LPC_REPLY;
|
||
Msg->Request.u2.s2.DataInfoOffset = 0;
|
||
Msg->Request.ClientId = CapturedReplyMessage.ClientId;
|
||
Msg->Request.MessageId = CapturedReplyMessage.MessageId;
|
||
Msg->Request.ClientViewSize = 0;
|
||
|
||
try {
|
||
|
||
RtlCopyMemory( ConnectMsg + 1,
|
||
(PCHAR)(ConnectionRequest + 1),
|
||
ConnectionInfoLength );
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Now it is time to process a positive accept request
|
||
//
|
||
|
||
ClientSectionToMap = NULL;
|
||
|
||
if (AcceptConnection) {
|
||
|
||
//
|
||
// Allocate and initialize a server communication port object.
|
||
// Communication ports have no names, can not be inherited and
|
||
// are process private handles.
|
||
//
|
||
|
||
Status = ObCreateObject( PreviousMode,
|
||
LpcPortObjectType,
|
||
NULL,
|
||
PreviousMode,
|
||
NULL,
|
||
FIELD_OFFSET( LPCP_PORT_OBJECT, WaitEvent ),
|
||
0,
|
||
0,
|
||
(PVOID *)&ServerPort );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
goto bailout;
|
||
}
|
||
|
||
RtlZeroMemory( ServerPort, FIELD_OFFSET( LPCP_PORT_OBJECT, WaitEvent ));
|
||
|
||
ServerPort->PortContext = PortContext;
|
||
ServerPort->Flags = SERVER_COMMUNICATION_PORT;
|
||
|
||
InitializeListHead( &ServerPort->LpcReplyChainHead );
|
||
InitializeListHead( &ServerPort->LpcDataInfoChainHead );
|
||
|
||
//
|
||
// Connect the newly created server communication port to the
|
||
// connection port with a referenced pointer. Prevents the
|
||
// connection port from going away until all of the communication
|
||
// ports have been closed.
|
||
//
|
||
|
||
ObReferenceObject( ConnectionPort );
|
||
|
||
ServerPort->ConnectionPort = ConnectionPort;
|
||
ServerPort->MaxMessageLength = ConnectionPort->MaxMessageLength;
|
||
|
||
//
|
||
// Connect the client and server communication ports together
|
||
// with unreferenced pointers. They are unreferenced so that
|
||
// the PortObjectType delete procedure will get called when a
|
||
// communication port is closed. If this were not the case then
|
||
// we would need a special NtClosePort system service in order
|
||
// to tear down a pair of connected communication ports.
|
||
//
|
||
|
||
ServerPort->ConnectedPort = ClientPort;
|
||
ClientPort->ConnectedPort = ServerPort;
|
||
|
||
ServerPort->Creator = PsGetCurrentThread()->Cid;
|
||
ClientPort->Creator = Msg->Request.ClientId;
|
||
|
||
//
|
||
// If the client has allocated a port memory section that is mapped
|
||
// into the client's address space, then map a view of the same
|
||
// section for the server process to see.
|
||
//
|
||
|
||
LpcpAcquireLpcpLock();
|
||
|
||
ClientSectionToMap = ConnectMsg->SectionToMap;
|
||
ConnectMsg->SectionToMap = NULL;
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
if (ClientSectionToMap) {
|
||
|
||
LARGE_INTEGER LargeSectionOffset;
|
||
|
||
LargeSectionOffset.LowPart = ConnectMsg->ClientView.SectionOffset;
|
||
LargeSectionOffset.HighPart = 0;
|
||
|
||
Status = MmMapViewOfSection( ClientSectionToMap,
|
||
PsGetCurrentProcess(),
|
||
&ServerPort->ClientSectionBase,
|
||
0,
|
||
0,
|
||
&LargeSectionOffset,
|
||
&ConnectMsg->ClientView.ViewSize,
|
||
ViewUnmap,
|
||
0,
|
||
PAGE_READWRITE );
|
||
|
||
ConnectMsg->ClientView.SectionOffset = LargeSectionOffset.LowPart;
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
ConnectMsg->ClientView.ViewRemoteBase = ServerPort->ClientSectionBase;
|
||
|
||
//
|
||
// The client section was mapped. We'll add an extra reference to
|
||
// server process. This reference will be removed on port cleanup.
|
||
//
|
||
|
||
ServerPort->MappingProcess = PsGetCurrentProcess();
|
||
|
||
ObReferenceObject( ServerPort->MappingProcess );
|
||
|
||
} else {
|
||
|
||
//
|
||
// At this point we're really going to drop all the way
|
||
// out to the label bailout: because everything else is
|
||
// protected with a test against Status. But first we have
|
||
// to release the server port that we've just created
|
||
//
|
||
|
||
ObDereferenceObject( ServerPort );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the server process has allocated a port memory section for
|
||
// send data to the client on call back requests, map two views
|
||
// of that section, the first for the server process and the
|
||
// second view for the client process. Return the location of the
|
||
// server's view to the caller of this function. Return the
|
||
// client's view to the client process via the reply to the
|
||
// connection request.
|
||
//
|
||
|
||
if (NT_SUCCESS( Status ) && ARGUMENT_PRESENT( ServerView )) {
|
||
|
||
LARGE_INTEGER LargeSectionOffset;
|
||
|
||
LargeSectionOffset.LowPart = CapturedServerView.SectionOffset;
|
||
LargeSectionOffset.HighPart = 0;
|
||
|
||
//
|
||
// Map in the section into the servers address space
|
||
//
|
||
|
||
//
|
||
// Does this call need to verify that the section handle
|
||
// is still valid.
|
||
//
|
||
|
||
Status = ObReferenceObjectByHandle( CapturedServerView.SectionHandle,
|
||
SECTION_MAP_READ |
|
||
SECTION_MAP_WRITE,
|
||
MmSectionObjectType,
|
||
PreviousMode,
|
||
(PVOID *)&SectionToMap,
|
||
NULL );
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
Status = MmMapViewOfSection( SectionToMap,
|
||
PsGetCurrentProcess(),
|
||
&ServerPort->ServerSectionBase,
|
||
0,
|
||
0,
|
||
&LargeSectionOffset,
|
||
&CapturedServerView.ViewSize,
|
||
ViewUnmap,
|
||
0,
|
||
PAGE_READWRITE );
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// The section was mapped into the server process. We'll add a
|
||
// reference to the server process only if we didn't before.
|
||
//
|
||
|
||
if ( ServerPort->MappingProcess == NULL ) {
|
||
|
||
ServerPort->MappingProcess = PsGetCurrentProcess();
|
||
ObReferenceObject( ServerPort->MappingProcess );
|
||
}
|
||
|
||
CapturedServerView.SectionOffset = LargeSectionOffset.LowPart;
|
||
|
||
CapturedServerView.ViewBase = ServerPort->ServerSectionBase;
|
||
|
||
|
||
SectionOffset.LowPart = CapturedServerView.SectionOffset;
|
||
SectionOffset.HighPart = 0;
|
||
|
||
ViewSize = CapturedServerView.ViewSize;
|
||
|
||
Status = MmMapViewOfSection( SectionToMap,
|
||
ClientProcess,
|
||
&ClientPort->ServerSectionBase,
|
||
0,
|
||
0,
|
||
&SectionOffset,
|
||
&ViewSize,
|
||
ViewUnmap,
|
||
0,
|
||
PAGE_READWRITE );
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// The section was mapped into the client process. We'll add a
|
||
// reference to the client process only we didn't before.
|
||
// (we don't have also a client section)
|
||
//
|
||
|
||
if ( ClientPort->MappingProcess == NULL ) {
|
||
|
||
ClientPort->MappingProcess = ClientProcess;
|
||
ObReferenceObject( ClientProcess );
|
||
}
|
||
|
||
//
|
||
// Let the server know where the client's view of the
|
||
// section got mapped
|
||
//
|
||
|
||
CapturedServerView.ViewRemoteBase = ClientPort->ServerSectionBase;
|
||
|
||
//
|
||
// Let the client know where the server's view of the
|
||
// section got mapped
|
||
//
|
||
|
||
ConnectMsg->ServerView.ViewBase = ClientPort->ServerSectionBase;
|
||
ConnectMsg->ServerView.ViewSize = ViewSize;
|
||
|
||
} else {
|
||
|
||
ObDereferenceObject( ServerPort );
|
||
}
|
||
|
||
} else {
|
||
|
||
ObDereferenceObject( ServerPort );
|
||
}
|
||
|
||
ObDereferenceObject( SectionToMap );
|
||
|
||
} else {
|
||
|
||
ObDereferenceObject( ServerPort );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Insert the server communication port object in specified object
|
||
// table. Set port handle value if successful. If not
|
||
// successful, then the port will have been dereferenced, which
|
||
// will cause it to be freed, after our delete procedure is
|
||
// called. The delete procedure will undo the work done to
|
||
// initialize the port.
|
||
//
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// Add an extra reference to the object otherwise right when we
|
||
// create the handle a rouge caller might close and destroy the
|
||
// port.
|
||
//
|
||
|
||
ObReferenceObject( ServerPort );
|
||
|
||
//
|
||
// Now add the handle
|
||
//
|
||
|
||
Status = ObInsertObject( ServerPort,
|
||
NULL,
|
||
PORT_ALL_ACCESS,
|
||
0,
|
||
(PVOID *)NULL,
|
||
&Handle );
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
try {
|
||
|
||
if (ARGUMENT_PRESENT( ServerView )) {
|
||
|
||
*ServerView = CapturedServerView;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT( ClientView )) {
|
||
|
||
ClientView->ViewBase = ConnectMsg->ClientView.ViewRemoteBase;
|
||
ClientView->ViewSize = ConnectMsg->ClientView.ViewSize;
|
||
}
|
||
|
||
*PortHandle = Handle;
|
||
|
||
if (!ARGUMENT_PRESENT( PortContext )) {
|
||
|
||
ServerPort->PortContext = Handle;
|
||
}
|
||
|
||
ServerPort->ClientThread = ClientThread;
|
||
|
||
LpcpAcquireLpcpLock();
|
||
ClientThread->LpcReplyMessage = Msg;
|
||
LpcpReleaseLpcpLock();
|
||
|
||
ClientThread = NULL;
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
NtClose( Handle );
|
||
Status = GetExceptionCode();
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now we can remove the extra object reference
|
||
//
|
||
|
||
ObDereferenceObject( ServerPort );
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Otherwise the server has not accepted the connection request
|
||
//
|
||
|
||
LpcpPrint(( "Refusing connection from %x.%x\n",
|
||
Msg->Request.ClientId.UniqueProcess,
|
||
Msg->Request.ClientId.UniqueThread ));
|
||
}
|
||
|
||
bailout:
|
||
|
||
if ( ClientSectionToMap ) {
|
||
|
||
ObDereferenceObject( ClientSectionToMap );
|
||
}
|
||
|
||
//
|
||
// If the client is not null then this is an error condition and we need
|
||
// to cleanup and wake the client thread. In success cases the client
|
||
// thread is woken up with a call to Complete Connect Request
|
||
//
|
||
|
||
if (ClientThread != NULL) {
|
||
|
||
LpcpAcquireLpcpLock();
|
||
|
||
ClientThread->LpcReplyMessage = Msg;
|
||
|
||
if (AcceptConnection) {
|
||
|
||
LpcpPrint(( "LPC: Failing AcceptConnection with Status == %x\n", Status ));
|
||
}
|
||
|
||
LpcpPrepareToWakeClient( ClientThread );
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
//
|
||
// Wake up the thread that is waiting for an answer to its connection
|
||
// request inside of NtConnectPort.
|
||
//
|
||
|
||
KeReleaseSemaphore( &ClientThread->LpcReplySemaphore,
|
||
0,
|
||
1L,
|
||
FALSE );
|
||
|
||
//
|
||
// Dereference client thread and return the system service status.
|
||
//
|
||
|
||
ObDereferenceObject( ClientThread );
|
||
}
|
||
|
||
if (ClientPort) {
|
||
|
||
ObDereferenceObject( ClientPort );
|
||
}
|
||
|
||
ObDereferenceObject( ClientProcess );
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtCompleteConnectPort (
|
||
IN HANDLE PortHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the server after it calls NtAcceptConnectPort to
|
||
wake up the client thread. Between calling NtAcceptConnectPort and
|
||
NtCompleteConnectPort the server can do whatever work is necessary before
|
||
waking up the client
|
||
|
||
Arguments:
|
||
|
||
PortHandle - Supplies a handle to the server communication port
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - An appropriate status value.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLPCP_PORT_OBJECT PortObject;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
PETHREAD ClientThread;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get previous processor mode
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
//
|
||
// Reference the port object by handle
|
||
//
|
||
|
||
Status = LpcpReferencePortObject( PortHandle,
|
||
0,
|
||
PreviousMode,
|
||
&PortObject );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Error if a port type is invalid.
|
||
//
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_INVALID_PORT_HANDLE;
|
||
}
|
||
|
||
//
|
||
// Under the LPC lock we need to check for a client thread and if there
|
||
// is one we'll remember and remove the client thread, and then prepare
|
||
// to wake the client
|
||
//
|
||
|
||
LpcpAcquireLpcpLock();
|
||
|
||
if (PortObject->ClientThread == NULL) {
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
ClientThread = PortObject->ClientThread;
|
||
|
||
//
|
||
// Double check that the thread is still waiting for a reply message
|
||
//
|
||
|
||
if (LpcpGetThreadMessage(ClientThread) == NULL) {
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
ObDereferenceObject( PortObject );
|
||
//
|
||
// At this point the client has already been woken. We will get a client died message
|
||
//
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// The check needs to ensure that the client thread is really on the
|
||
// reply chain for the sever's connection port. This is a quick and
|
||
// dirty fix for NT 5.0. We zoom down the connection port lpc reply
|
||
// chain looking for an entry that contains the client threads. If
|
||
// we find a match it's okay if we don't it's bad.
|
||
//
|
||
|
||
if (PortObject->ConnectionPort) {
|
||
|
||
PLIST_ENTRY Entry;
|
||
|
||
for (Entry = PortObject->ConnectionPort->LpcReplyChainHead.Flink;
|
||
Entry != (PLIST_ENTRY)(&PortObject->ConnectionPort->LpcReplyChainHead.Flink);
|
||
Entry = Entry->Flink) {
|
||
|
||
if (Entry == ((PLIST_ENTRY)(&ClientThread->LpcReplyChain.Flink))) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (Entry != ((PLIST_ENTRY)(&ClientThread->LpcReplyChain.Flink))) {
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
//
|
||
// At this point the client has already been woken. We will get a client died message
|
||
//
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now do the wakeup
|
||
//
|
||
|
||
PortObject->ClientThread = NULL;
|
||
|
||
LpcpPrepareToWakeClient( ClientThread );
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
//
|
||
// Wake up the thread that is waiting for an answer to its connection
|
||
// request inside of NtConnectPort.
|
||
//
|
||
|
||
KeReleaseSemaphore( &ClientThread->LpcReplySemaphore,
|
||
0,
|
||
1L,
|
||
FALSE );
|
||
|
||
//
|
||
// Dereference client thread
|
||
//
|
||
|
||
ObDereferenceObject( ClientThread );
|
||
ObDereferenceObject( PortObject );
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
VOID
|
||
LpcpPrepareToWakeClient (
|
||
IN PETHREAD ClientThread
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to prepare the client thread to receive a reply to
|
||
its connection request
|
||
|
||
Arguments:
|
||
|
||
ClientThread - Specifies the thread we are preparing to wake up
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Remove the thread from the rundown list the connection port as we are
|
||
// sending a reply. The operation only needs to take place if the
|
||
// thread isn't exiting and it's in the lpc reply chain for a connection
|
||
// port
|
||
//
|
||
|
||
if ((!ClientThread->LpcExitThreadCalled) &&
|
||
(!IsListEmpty( &ClientThread->LpcReplyChain ))) {
|
||
|
||
RemoveEntryList( &ClientThread->LpcReplyChain );
|
||
InitializeListHead( &ClientThread->LpcReplyChain );
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return;
|
||
}
|