457 lines
12 KiB
C
457 lines
12 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
lpccreat.c
|
||
|
||
Abstract:
|
||
|
||
Local Inter-Process Communication (LPC) connection system services.
|
||
|
||
Author:
|
||
|
||
Steve Wood (stevewo) 15-May-1989
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "lpcp.h"
|
||
|
||
//
|
||
// Local procedure prototype
|
||
//
|
||
|
||
NTSTATUS
|
||
LpcpCreatePort (
|
||
OUT PHANDLE PortHandle,
|
||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||
IN ULONG MaxConnectionInfoLength,
|
||
IN ULONG MaxMessageLength,
|
||
IN ULONG MaxPoolUsage,
|
||
IN BOOLEAN Waitable
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,NtCreatePort)
|
||
#pragma alloc_text(PAGE,NtCreateWaitablePort)
|
||
#pragma alloc_text(PAGE,LpcpCreatePort)
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
NtCreatePort (
|
||
OUT PHANDLE PortHandle,
|
||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||
IN ULONG MaxConnectionInfoLength,
|
||
IN ULONG MaxMessageLength,
|
||
IN ULONG MaxPoolUsage
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
See LpcpCreatePort.
|
||
|
||
Arguments:
|
||
|
||
See LpcpCreatePort.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - An appropriate status value
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
Status = LpcpCreatePort( PortHandle,
|
||
ObjectAttributes,
|
||
MaxConnectionInfoLength,
|
||
MaxMessageLength,
|
||
MaxPoolUsage,
|
||
FALSE );
|
||
|
||
return Status ;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtCreateWaitablePort (
|
||
OUT PHANDLE PortHandle,
|
||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||
IN ULONG MaxConnectionInfoLength,
|
||
IN ULONG MaxMessageLength,
|
||
IN ULONG MaxPoolUsage
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Same as NtCreatePort.
|
||
|
||
The only difference between this call and NtCreatePort is that the
|
||
working KEVENT that can be used to wait for LPC messages to arrive
|
||
asynchronously.
|
||
|
||
Arguments:
|
||
|
||
See LpcpCreatePort.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - An appropriate status value
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status ;
|
||
|
||
PAGED_CODE();
|
||
|
||
Status = LpcpCreatePort( PortHandle,
|
||
ObjectAttributes,
|
||
MaxConnectionInfoLength,
|
||
MaxMessageLength,
|
||
MaxPoolUsage,
|
||
TRUE );
|
||
|
||
return Status ;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
LpcpCreatePort (
|
||
OUT PHANDLE PortHandle,
|
||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||
IN ULONG MaxConnectionInfoLength,
|
||
IN ULONG MaxMessageLength,
|
||
IN ULONG MaxPoolUsage,
|
||
IN BOOLEAN Waitable
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
A server process can create a named connection port with the NtCreatePort
|
||
service.
|
||
|
||
A connection port is created with the name and SECURITY_DESCRIPTOR
|
||
specified in the ObjectAttributes structure. A handle to the connection
|
||
port object is returned in the location pointed to by the PortHandle
|
||
parameter. The returned handle can then be used to listen for connection
|
||
requests to that port name, using the NtListenPort service.
|
||
|
||
The standard object architecture defined desired access parameter is not
|
||
necessary since this service can only create a new port, not access an
|
||
existing port.
|
||
|
||
Connection ports cannot be used to send and receive messages. They are
|
||
only valid as a parameter to the NtListenPort service.
|
||
|
||
Arguments:
|
||
|
||
PortHandle - A pointer to a variable that will receive the connection port
|
||
object handle value.
|
||
|
||
ObjectAttributes - A pointer to a structure that specifies the name of the
|
||
object, an access control list (SECURITY_DESCRIPTOR) to be applied to
|
||
the object, and a set of object attribute flags.
|
||
|
||
PUNICODE_STRING ObjectName - An optional pointer to a null terminated
|
||
port name string. The form of the name is
|
||
[\name...\name]\port_name. If no name is specified then an
|
||
unconnected communication port is created rather than a connection
|
||
port. This is useful for sending and receiving messages between
|
||
threads of a single process.
|
||
|
||
ULONG Attributes - A set of flags that control the port object
|
||
attributes.
|
||
|
||
None of the standard values are relevant for this call.
|
||
Connection ports cannot be inherited, are always placed in the
|
||
system handle table and are exclusive to the creating process.
|
||
This field must be zero. Future implementations might support
|
||
specifying the OBJ_PERMANENT attribute.
|
||
|
||
MaxMessageLength - Specifies the maximum length of messages sent or
|
||
received on communication ports created from this connection
|
||
port. The value of this parameter cannot exceed
|
||
MAX_PORTMSG_LENGTH bytes.
|
||
|
||
MaxPoolUsage - Specifies the maximum amount of NonPaged pool used for
|
||
message storage.
|
||
|
||
Waitable - Specifies if the event used by the port can be use to wait
|
||
for LPC messages to arrive asynchronously.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - An appropriate status value
|
||
|
||
--*/
|
||
|
||
{
|
||
PLPCP_PORT_OBJECT ConnectionPort;
|
||
HANDLE Handle;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
PUNICODE_STRING NamePtr;
|
||
UNICODE_STRING CapturedObjectName;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER (MaxPoolUsage);
|
||
|
||
//
|
||
// Get previous processor mode and probe output arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
RtlInitUnicodeString( &CapturedObjectName, NULL );
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
try {
|
||
|
||
ProbeForWriteHandle( PortHandle );
|
||
|
||
ProbeForReadSmallStructure( ObjectAttributes,
|
||
sizeof( OBJECT_ATTRIBUTES ),
|
||
sizeof( ULONG ));
|
||
|
||
NamePtr = ObjectAttributes->ObjectName;
|
||
|
||
if (NamePtr != NULL) {
|
||
|
||
CapturedObjectName = ProbeAndReadStructure( NamePtr,
|
||
UNICODE_STRING );
|
||
}
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
return( GetExceptionCode() );
|
||
}
|
||
|
||
} else {
|
||
|
||
if (ObjectAttributes->ObjectName != NULL) {
|
||
|
||
CapturedObjectName = *(ObjectAttributes->ObjectName);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Make the null buffer indicate an unspecified port name
|
||
//
|
||
|
||
if (CapturedObjectName.Length == 0) {
|
||
|
||
CapturedObjectName.Buffer = NULL;
|
||
}
|
||
|
||
//
|
||
// Allocate and initialize a port object. If an object name was
|
||
// specified, then this is a connection port. Otherwise this is an
|
||
// unconnected communication port that a process can use to communicate
|
||
// between threads.
|
||
//
|
||
|
||
Status = ObCreateObject( PreviousMode,
|
||
(Waitable ? LpcWaitablePortObjectType
|
||
: LpcPortObjectType),
|
||
ObjectAttributes,
|
||
PreviousMode,
|
||
NULL,
|
||
(Waitable ? sizeof( LPCP_PORT_OBJECT )
|
||
: FIELD_OFFSET( LPCP_PORT_OBJECT, WaitEvent )),
|
||
0,
|
||
0,
|
||
(PVOID *)&ConnectionPort );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
return( Status );
|
||
}
|
||
|
||
//
|
||
// Zero out the connection port object and then initialize its fields
|
||
//
|
||
|
||
RtlZeroMemory( ConnectionPort, (Waitable ? sizeof( LPCP_PORT_OBJECT )
|
||
: FIELD_OFFSET( LPCP_PORT_OBJECT, WaitEvent )));
|
||
|
||
ConnectionPort->ConnectionPort = ConnectionPort;
|
||
ConnectionPort->Creator = PsGetCurrentThread()->Cid;
|
||
|
||
InitializeListHead( &ConnectionPort->LpcReplyChainHead );
|
||
|
||
InitializeListHead( &ConnectionPort->LpcDataInfoChainHead );
|
||
|
||
//
|
||
// Named ports get a connection message queue.
|
||
//
|
||
|
||
if (CapturedObjectName.Buffer == NULL) {
|
||
|
||
ConnectionPort->Flags = UNCONNECTED_COMMUNICATION_PORT;
|
||
ConnectionPort->ConnectedPort = ConnectionPort;
|
||
ConnectionPort->ServerProcess = NULL;
|
||
|
||
} else {
|
||
|
||
ConnectionPort->Flags = SERVER_CONNECTION_PORT;
|
||
|
||
ObReferenceObject( PsGetCurrentProcess() );
|
||
ConnectionPort->ServerProcess = PsGetCurrentProcess();
|
||
}
|
||
|
||
if ( Waitable ) {
|
||
|
||
ConnectionPort->Flags |= PORT_WAITABLE;
|
||
}
|
||
|
||
//
|
||
// All ports get a request message queue.
|
||
//
|
||
|
||
Status = LpcpInitializePortQueue( ConnectionPort );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
ObDereferenceObject( ConnectionPort );
|
||
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// For a waitable port, create the KEVENT that will
|
||
// be used to signal clients
|
||
//
|
||
|
||
if (ConnectionPort->Flags & PORT_WAITABLE) {
|
||
|
||
KeInitializeEvent( &ConnectionPort->WaitEvent,
|
||
NotificationEvent,
|
||
FALSE );
|
||
}
|
||
|
||
//
|
||
// Set the maximum message length and connection info length based on the
|
||
// zone block size less the structs overhead.
|
||
//
|
||
|
||
ConnectionPort->MaxMessageLength = (USHORT) (LpcpGetMaxMessageLength() -
|
||
FIELD_OFFSET( LPCP_MESSAGE, Request ));
|
||
|
||
ConnectionPort->MaxConnectionInfoLength = (USHORT) (ConnectionPort->MaxMessageLength -
|
||
sizeof( PORT_MESSAGE ) -
|
||
sizeof( LPCP_CONNECTION_MESSAGE ));
|
||
|
||
#if DBG
|
||
LpcpTrace(( "Created port %ws (%x) - MaxMsgLen == %x MaxConnectInfoLen == %x\n",
|
||
CapturedObjectName.Buffer == NULL ? L"** UnNamed **" : ObjectAttributes->ObjectName->Buffer,
|
||
ConnectionPort,
|
||
ConnectionPort->MaxMessageLength,
|
||
ConnectionPort->MaxConnectionInfoLength ));
|
||
#endif
|
||
|
||
//
|
||
// Sanity check that the max message length being asked for is not
|
||
// greater than the max message length possible in the system
|
||
//
|
||
|
||
if (ConnectionPort->MaxMessageLength < MaxMessageLength) {
|
||
|
||
#if DBG
|
||
LpcpPrint(( "MaxMessageLength granted is %x but requested %x\n",
|
||
ConnectionPort->MaxMessageLength,
|
||
MaxMessageLength ));
|
||
DbgBreakPoint();
|
||
#endif
|
||
|
||
ObDereferenceObject( ConnectionPort );
|
||
|
||
return STATUS_INVALID_PARAMETER_4;
|
||
}
|
||
|
||
//
|
||
// Save the MaxMessageLength to the connection port
|
||
//
|
||
|
||
ConnectionPort->MaxMessageLength = (USHORT) MaxMessageLength;
|
||
|
||
//
|
||
// Sanity check that the max connection info length being asked for is
|
||
// not greater than the maximum possible in the system
|
||
//
|
||
|
||
if (ConnectionPort->MaxConnectionInfoLength < MaxConnectionInfoLength) {
|
||
|
||
#if DBG
|
||
LpcpPrint(( "MaxConnectionInfoLength granted is %x but requested %x\n",
|
||
ConnectionPort->MaxConnectionInfoLength,
|
||
MaxConnectionInfoLength ));
|
||
DbgBreakPoint();
|
||
#endif
|
||
|
||
ObDereferenceObject( ConnectionPort );
|
||
|
||
return STATUS_INVALID_PARAMETER_3;
|
||
}
|
||
|
||
//
|
||
// Insert connection 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. Finally, return the system server status.
|
||
//
|
||
|
||
Status = ObInsertObject( ConnectionPort,
|
||
NULL,
|
||
PORT_ALL_ACCESS,
|
||
0,
|
||
(PVOID *)NULL,
|
||
&Handle );
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// Set the output variable protected against access faults
|
||
//
|
||
|
||
try {
|
||
|
||
*PortHandle = Handle;
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
NtClose( Handle );
|
||
|
||
Status = GetExceptionCode();
|
||
}
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return Status;
|
||
}
|
||
|
||
|