/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/lpc/send.c * PURPOSE: Local Procedure Call: Sending (Requests) * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include /* PUBLIC FUNCTIONS **********************************************************/ /* * @implemented */ NTSTATUS NTAPI LpcRequestPort(IN PVOID PortObject, IN PPORT_MESSAGE LpcMessage) { PLPCP_PORT_OBJECT Port = PortObject, QueuePort, ConnectionPort = NULL; ULONG MessageType; PLPCP_MESSAGE Message; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PETHREAD Thread = PsGetCurrentThread(); PAGED_CODE(); LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", Port, LpcMessage); /* Check if this is a non-datagram message */ if (LpcMessage->u2.s2.Type) { /* Get the message type */ MessageType = LpcpGetMessageType(LpcMessage); /* Validate it */ if ((MessageType < LPC_DATAGRAM) || (MessageType > LPC_CLIENT_DIED)) { /* Fail */ return STATUS_INVALID_PARAMETER; } /* Mark this as a kernel-mode message only if we really came from it */ if ((PreviousMode == KernelMode) && (LpcMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE)) { /* We did, this is a kernel mode message */ MessageType |= LPC_KERNELMODE_MESSAGE; } } else { /* This is a datagram */ MessageType = LPC_DATAGRAM; } /* Can't have data information on this type of call */ if (LpcMessage->u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER; /* Validate the message length */ if (((ULONG)LpcMessage->u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)LpcMessage->u1.s1.TotalLength <= (ULONG)LpcMessage->u1.s1.DataLength)) { /* Fail */ return STATUS_PORT_MESSAGE_TOO_LONG; } /* Allocate a new message */ Message = LpcpAllocateFromPortZone(); if (!Message) return STATUS_NO_MEMORY; /* Clear the context */ Message->RepliedToThread = NULL; Message->PortContext = NULL; /* Copy the message */ LpcpMoveMessage(&Message->Request, LpcMessage, LpcMessage + 1, MessageType, &Thread->Cid); /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Check if this is anything but a connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* The queue port is the connected port */ QueuePort = Port->ConnectedPort; if (QueuePort) { /* Check if this is a client port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Then copy the context */ Message->PortContext = QueuePort->PortContext; ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); return STATUS_PORT_DISCONNECTED; } } else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Any other kind of port, use the connection port */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); return STATUS_PORT_DISCONNECTED; } } /* If we have a connection port, reference it */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } } else { /* For connection ports, use the port itself */ QueuePort = PortObject; } /* Make sure we have a port */ if (QueuePort) { /* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0; /* No Message ID for the thread */ Thread->LpcReplyMessageId = 0; /* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); /* Release the lock and the semaphore */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); LpcpCompleteWait(QueuePort->MsgQueue.Semaphore); /* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); } KeLeaveCriticalRegion(); /* We're done */ if (ConnectionPort) ObDereferenceObject(ConnectionPort); LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message); return STATUS_SUCCESS; } /* If we got here, then free the message and fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); if (ConnectionPort) ObDereferenceObject(ConnectionPort); return STATUS_PORT_DISCONNECTED; } /* * @implemented */ NTSTATUS NTAPI LpcRequestWaitReplyPort(IN PVOID PortObject, IN PPORT_MESSAGE LpcRequest, OUT PPORT_MESSAGE LpcReply) { NTSTATUS Status = STATUS_SUCCESS; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PETHREAD Thread = PsGetCurrentThread(); PLPCP_PORT_OBJECT Port = (PLPCP_PORT_OBJECT)PortObject; PLPCP_PORT_OBJECT QueuePort, ReplyPort, ConnectionPort = NULL; USHORT MessageType; PLPCP_MESSAGE Message; BOOLEAN Callback = FALSE; PKSEMAPHORE Semaphore; PAGED_CODE(); LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Messages: %p/%p. Type: %lx\n", Port, LpcRequest, LpcReply, LpcpGetMessageType(LpcRequest)); /* Check if the thread is dying */ if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING; /* Check if this is an LPC Request */ MessageType = LpcpGetMessageType(LpcRequest); switch (MessageType) { /* No type, assume LPC request */ case 0: MessageType = LPC_REQUEST; break; /* LPC request callback */ case LPC_REQUEST: Callback = TRUE; break; /* Anything else, nothing to do */ case LPC_CLIENT_DIED: case LPC_PORT_CLOSED: case LPC_EXCEPTION: case LPC_DEBUG_EVENT: case LPC_ERROR_EVENT: break; /* Invalid message type */ default: return STATUS_INVALID_PARAMETER; } /* Set the request type */ LpcRequest->u2.s2.Type = MessageType; /* Validate the message length */ if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength)) { /* Fail */ return STATUS_PORT_MESSAGE_TOO_LONG; } /* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ return STATUS_NO_MEMORY; } /* Check if this is a callback */ if (Callback) { /* FIXME: TODO */ Semaphore = NULL; // we'd use the Thread Semaphore here ASSERT(FALSE); return STATUS_NOT_IMPLEMENTED; } else { /* No callback, just copy the message */ LpcpMoveMessage(&Message->Request, LpcRequest, LpcRequest + 1, 0, &Thread->Cid); /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Right now clear the port context */ Message->PortContext = NULL; /* Check if this is a not connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* We want the connected port */ QueuePort = Port->ConnectedPort; if (!QueuePort) { /* We have no connected port, fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); return STATUS_PORT_DISCONNECTED; } /* This will be the rundown port */ ReplyPort = QueuePort; /* Check if this is a communication port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Copy the port context and use the connection port */ Message->PortContext = QueuePort->PortContext; ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); return STATUS_PORT_DISCONNECTED; } } else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Use the connection port for anything but communication ports */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); return STATUS_PORT_DISCONNECTED; } } /* Reference the connection port if it exists */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } else { /* Otherwise, for a connection port, use the same port object */ QueuePort = ReplyPort = Port; } /* No reply thread */ Message->RepliedToThread = NULL; Message->SenderPort = Port; /* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0; /* Set the message ID for our thread now */ Thread->LpcReplyMessageId = Message->Request.MessageId; Thread->LpcReplyMessage = NULL; /* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain); LpcpSetPortToThread(Thread, Port); /* Release the lock and get the semaphore we'll use later */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); Semaphore = QueuePort->MsgQueue.Semaphore; /* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); } } /* Now release the semaphore */ LpcpCompleteWait(Semaphore); KeLeaveCriticalRegion(); /* And let's wait for the reply */ LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode); /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Get the LPC Message and clear our thread's reply data */ Message = LpcpGetMessageFromThread(Thread); Thread->LpcReplyMessage = NULL; Thread->LpcReplyMessageId = 0; /* Check if we have anything on the reply chain*/ if (!IsListEmpty(&Thread->LpcReplyChain)) { /* Remove this thread and reinitialize the list */ RemoveEntryList(&Thread->LpcReplyChain); InitializeListHead(&Thread->LpcReplyChain); } /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); /* Check if we got a reply */ if (Status == STATUS_SUCCESS) { /* Check if we have a valid message */ if (Message) { LPCTRACE(LPC_SEND_DEBUG, "Reply Messages: %p/%p\n", &Message->Request, (&Message->Request) + 1); /* Move the message */ LpcpMoveMessage(LpcReply, &Message->Request, (&Message->Request) + 1, 0, NULL); /* Acquire the lock */ KeAcquireGuardedMutex(&LpcpLock); /* Check if we replied to a thread */ if (Message->RepliedToThread) { /* Dereference */ ObDereferenceObject(Message->RepliedToThread); Message->RepliedToThread = NULL; } /* Free the message */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); } else { /* We don't have a reply */ Status = STATUS_LPC_REPLY_LOST; } } else { /* The wait failed, free the message */ if (Message) LpcpFreeToPortZone(Message, 0); } /* All done */ LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Status: %d\n", Port, Status); /* Dereference the connection port */ if (ConnectionPort) ObDereferenceObject(ConnectionPort); return Status; } /* * @implemented */ NTSTATUS NTAPI NtRequestPort(IN HANDLE PortHandle, IN PPORT_MESSAGE LpcRequest) { NTSTATUS Status; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PETHREAD Thread = PsGetCurrentThread(); PORT_MESSAGE CapturedLpcRequest; PLPCP_PORT_OBJECT Port, QueuePort, ConnectionPort = NULL; ULONG MessageType; PLPCP_MESSAGE Message; PAGED_CODE(); LPCTRACE(LPC_SEND_DEBUG, "Handle: %p. Message: %p. Type: %lx\n", PortHandle, LpcRequest, LpcpGetMessageType(LpcRequest)); /* Check if the call comes from user mode */ if (PreviousMode != KernelMode) { _SEH2_TRY { /* Probe and capture the LpcRequest */ ProbeForRead(LpcRequest, sizeof(*LpcRequest), sizeof(ULONG)); CapturedLpcRequest = *(volatile PORT_MESSAGE*)LpcRequest; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else { /* Access the LpcRequest directly */ CapturedLpcRequest = *LpcRequest; } /* Get the message type */ MessageType = CapturedLpcRequest.u2.s2.Type | LPC_DATAGRAM; /* Can't have data information on this type of call */ if (CapturedLpcRequest.u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER; /* Validate the length */ if (((ULONG)CapturedLpcRequest.u1.s1.DataLength + sizeof(PORT_MESSAGE)) > (ULONG)CapturedLpcRequest.u1.s1.TotalLength) { /* Fail */ return STATUS_INVALID_PARAMETER; } /* Reference the object */ Status = ObReferenceObjectByHandle(PortHandle, 0, LpcPortObjectType, PreviousMode, (PVOID*)&Port, NULL); if (!NT_SUCCESS(Status)) return Status; /* Validate the message length */ if (((ULONG)CapturedLpcRequest.u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)CapturedLpcRequest.u1.s1.TotalLength <= (ULONG)CapturedLpcRequest.u1.s1.DataLength)) { /* Fail */ ObDereferenceObject(Port); return STATUS_PORT_MESSAGE_TOO_LONG; } /* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ ObDereferenceObject(Port); return STATUS_NO_MEMORY; } /* No callback, just copy the message */ _SEH2_TRY { /* Copy it */ LpcpMoveMessage(&Message->Request, &CapturedLpcRequest, LpcRequest + 1, MessageType, &Thread->Cid); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Cleanup and return the exception code */ LpcpFreeToPortZone(Message, 0); ObDereferenceObject(Port); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Right now clear the port context */ Message->PortContext = NULL; /* Check if this is a not connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* We want the connected port */ QueuePort = Port->ConnectedPort; if (!QueuePort) { /* We have no connected port, fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } /* Check if this is a communication port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Copy the port context and use the connection port */ Message->PortContext = QueuePort->PortContext; ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } } else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Use the connection port for anything but communication ports */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } } /* Reference the connection port if it exists */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } else { /* Otherwise, for a connection port, use the same port object */ QueuePort = Port; } /* Reference QueuePort if we have it */ if (QueuePort && ObReferenceObjectSafe(QueuePort)) { /* Set sender's port */ Message->SenderPort = Port; /* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0; /* No Message ID for the thread */ Thread->LpcReplyMessageId = 0; /* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); /* Release the lock and the semaphore */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); LpcpCompleteWait(QueuePort->MsgQueue.Semaphore); /* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); } KeLeaveCriticalRegion(); /* Dereference objects */ if (ConnectionPort) ObDereferenceObject(ConnectionPort); ObDereferenceObject(QueuePort); ObDereferenceObject(Port); LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message); return STATUS_SUCCESS; } Status = STATUS_PORT_DISCONNECTED; /* All done with a failure*/ LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Status: %d\n", Port, Status); /* The wait failed, free the message */ if (Message) LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(Port); if (ConnectionPort) ObDereferenceObject(ConnectionPort); return Status; } NTSTATUS NTAPI LpcpVerifyMessageDataInfo( _In_ PPORT_MESSAGE Message, _Out_ PULONG NumberOfDataEntries) { PLPCP_DATA_INFO DataInfo; PUCHAR EndOfEntries; /* Check if we have no data info at all */ if (Message->u2.s2.DataInfoOffset == 0) { *NumberOfDataEntries = 0; return STATUS_SUCCESS; } /* Make sure the data info structure is within the message */ if (((ULONG)Message->u1.s1.TotalLength < sizeof(PORT_MESSAGE) + sizeof(LPCP_DATA_INFO)) || ((ULONG)Message->u2.s2.DataInfoOffset < sizeof(PORT_MESSAGE)) || ((ULONG)Message->u2.s2.DataInfoOffset > ((ULONG)Message->u1.s1.TotalLength - sizeof(LPCP_DATA_INFO)))) { return STATUS_INVALID_PARAMETER; } /* Get a pointer to the data info */ DataInfo = LpcpGetDataInfoFromMessage(Message); /* Make sure the full data info with all entries is within the message */ EndOfEntries = (PUCHAR)&DataInfo->Entries[DataInfo->NumberOfEntries]; if ((EndOfEntries > ((PUCHAR)Message + (ULONG)Message->u1.s1.TotalLength)) || (EndOfEntries < (PUCHAR)Message)) { return STATUS_INVALID_PARAMETER; } *NumberOfDataEntries = DataInfo->NumberOfEntries; return STATUS_SUCCESS; } /* * @implemented */ NTSTATUS NTAPI NtRequestWaitReplyPort(IN HANDLE PortHandle, IN PPORT_MESSAGE LpcRequest, IN OUT PPORT_MESSAGE LpcReply) { NTSTATUS Status; PORT_MESSAGE CapturedLpcRequest; ULONG NumberOfDataEntries; PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL; PLPCP_MESSAGE Message; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PETHREAD Thread = PsGetCurrentThread(); BOOLEAN Callback; PKSEMAPHORE Semaphore; ULONG MessageType; PLPCP_DATA_INFO DataInfo; PAGED_CODE(); LPCTRACE(LPC_SEND_DEBUG, "Handle: %p. Messages: %p/%p. Type: %lx\n", PortHandle, LpcRequest, LpcReply, LpcpGetMessageType(LpcRequest)); /* Check if the thread is dying */ if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING; /* Check for user mode access */ if (PreviousMode != KernelMode) { _SEH2_TRY { /* Probe and capture the LpcRequest */ ProbeForRead(LpcRequest, sizeof(*LpcRequest), sizeof(ULONG)); CapturedLpcRequest = *(volatile PORT_MESSAGE*)LpcRequest; /* Probe the reply message for write */ ProbeForWrite(LpcReply, sizeof(*LpcReply), sizeof(ULONG)); /* Make sure the data entries in the request message are valid */ Status = LpcpVerifyMessageDataInfo(LpcRequest, &NumberOfDataEntries); if (!NT_SUCCESS(Status)) { DPRINT1("LpcpVerifyMessageDataInfo failed\n"); _SEH2_YIELD(return Status); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPRINT1("Got exception\n"); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else { CapturedLpcRequest = *LpcRequest; Status = LpcpVerifyMessageDataInfo(LpcRequest, &NumberOfDataEntries); if (!NT_SUCCESS(Status)) { DPRINT1("LpcpVerifyMessageDataInfo failed\n"); return Status; } } /* This flag is undocumented. Remove it before continuing */ CapturedLpcRequest.u2.s2.Type &= ~0x4000; /* Check if this is an LPC Request */ if (LpcpGetMessageType(&CapturedLpcRequest) == LPC_REQUEST) { /* Then it's a callback */ Callback = TRUE; } else if (LpcpGetMessageType(&CapturedLpcRequest)) { /* This is a not kernel-mode message */ DPRINT1("Not a kernel-mode message!\n"); return STATUS_INVALID_PARAMETER; } else { /* This is a kernel-mode message without a callback */ CapturedLpcRequest.u2.s2.Type |= LPC_REQUEST; Callback = FALSE; } /* Get the message type */ MessageType = CapturedLpcRequest.u2.s2.Type; /* Due to the above probe, we know that TotalLength is positive */ ASSERT(CapturedLpcRequest.u1.s1.TotalLength >= 0); /* Validate the length */ if ((((ULONG)(USHORT)CapturedLpcRequest.u1.s1.DataLength + sizeof(PORT_MESSAGE)) > (ULONG)CapturedLpcRequest.u1.s1.TotalLength)) { /* Fail */ DPRINT1("Invalid message length: %u, %u\n", CapturedLpcRequest.u1.s1.DataLength, CapturedLpcRequest.u1.s1.TotalLength); return STATUS_INVALID_PARAMETER; } /* Reference the object */ Status = ObReferenceObjectByHandle(PortHandle, 0, LpcPortObjectType, PreviousMode, (PVOID*)&Port, NULL); if (!NT_SUCCESS(Status)) return Status; /* Validate the message length */ if (((ULONG)CapturedLpcRequest.u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)CapturedLpcRequest.u1.s1.TotalLength <= (ULONG)CapturedLpcRequest.u1.s1.DataLength)) { /* Fail */ DPRINT1("Invalid message length: %u, %u\n", CapturedLpcRequest.u1.s1.DataLength, CapturedLpcRequest.u1.s1.TotalLength); ObDereferenceObject(Port); return STATUS_PORT_MESSAGE_TOO_LONG; } /* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ DPRINT1("Failed to allocate a message!\n"); ObDereferenceObject(Port); return STATUS_NO_MEMORY; } /* Check if this is a callback */ if (Callback) { /* FIXME: TODO */ Semaphore = NULL; // we'd use the Thread Semaphore here ASSERT(FALSE); } else { /* No callback, just copy the message */ _SEH2_TRY { /* Check if we have data info entries */ if (LpcRequest->u2.s2.DataInfoOffset != 0) { /* Get the data info and check if the number of entries matches what we expect */ DataInfo = LpcpGetDataInfoFromMessage(LpcRequest); if (DataInfo->NumberOfEntries != NumberOfDataEntries) { LpcpFreeToPortZone(Message, 0); ObDereferenceObject(Port); DPRINT1("NumberOfEntries has changed: %u, %u\n", DataInfo->NumberOfEntries, NumberOfDataEntries); _SEH2_YIELD(return STATUS_INVALID_PARAMETER); } } /* Copy it */ LpcpMoveMessage(&Message->Request, &CapturedLpcRequest, LpcRequest + 1, MessageType, &Thread->Cid); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Cleanup and return the exception code */ DPRINT1("Got exception!\n"); LpcpFreeToPortZone(Message, 0); ObDereferenceObject(Port); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Right now clear the port context */ Message->PortContext = NULL; /* Check if this is a not connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* We want the connected port */ QueuePort = Port->ConnectedPort; if (!QueuePort) { /* We have no connected port, fail */ DPRINT1("No connected port\n"); LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } /* This will be the rundown port */ ReplyPort = QueuePort; /* Check if this is a client port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Copy the port context */ Message->PortContext = QueuePort->PortContext; } if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Use the connection port for anything but communication ports */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ DPRINT1("No connection port\n"); LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } } /* Reference the connection port if it exists */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } else { /* Otherwise, for a connection port, use the same port object */ QueuePort = ReplyPort = Port; } /* No reply thread */ Message->RepliedToThread = NULL; Message->SenderPort = Port; /* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0; /* Set the message ID for our thread now */ Thread->LpcReplyMessageId = Message->Request.MessageId; Thread->LpcReplyMessage = NULL; /* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain); LpcpSetPortToThread(Thread, Port); /* Release the lock and get the semaphore we'll use later */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); Semaphore = QueuePort->MsgQueue.Semaphore; /* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); } } /* Now release the semaphore */ LpcpCompleteWait(Semaphore); KeLeaveCriticalRegion(); /* And let's wait for the reply */ LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode); /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Get the LPC Message and clear our thread's reply data */ Message = LpcpGetMessageFromThread(Thread); Thread->LpcReplyMessage = NULL; Thread->LpcReplyMessageId = 0; /* Check if we have anything on the reply chain*/ if (!IsListEmpty(&Thread->LpcReplyChain)) { /* Remove this thread and reinitialize the list */ RemoveEntryList(&Thread->LpcReplyChain); InitializeListHead(&Thread->LpcReplyChain); } /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); /* Check if we got a reply */ if (Status == STATUS_SUCCESS) { /* Check if we have a valid message */ if (Message) { LPCTRACE(LPC_SEND_DEBUG, "Reply Messages: %p/%p\n", &Message->Request, (&Message->Request) + 1); /* Move the message */ _SEH2_TRY { LpcpMoveMessage(LpcReply, &Message->Request, (&Message->Request) + 1, 0, NULL); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPRINT1("Got exception!\n"); Status = _SEH2_GetExceptionCode(); } _SEH2_END; /* Check if this is an LPC request with data information */ if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) && (Message->Request.u2.s2.DataInfoOffset)) { /* Save the data information */ LpcpSaveDataInfoMessage(Port, Message, 0); } else { /* Otherwise, just free it */ LpcpFreeToPortZone(Message, 0); } } else { /* We don't have a reply */ Status = STATUS_LPC_REPLY_LOST; } } else { /* The wait failed, free the message */ if (Message) LpcpFreeToPortZone(Message, 0); } /* All done */ LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Status: %d\n", Port, Status); ObDereferenceObject(Port); if (ConnectionPort) ObDereferenceObject(ConnectionPort); return Status; } /* EOF */