2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/send.c
5 * PURPOSE: Local Procedure Call: Sending (Requests)
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* PUBLIC FUNCTIONS **********************************************************/
22 LpcRequestPort(IN PVOID PortObject
,
23 IN PPORT_MESSAGE LpcMessage
)
25 PLPCP_PORT_OBJECT Port
= PortObject
, QueuePort
, ConnectionPort
= NULL
;
27 PLPCP_MESSAGE Message
;
28 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
29 PETHREAD Thread
= PsGetCurrentThread();
33 LPCTRACE(LPC_SEND_DEBUG
, "Port: %p. Message: %p\n", Port
, LpcMessage
);
35 /* Check if this is a non-datagram message */
36 if (LpcMessage
->u2
.s2
.Type
)
38 /* Get the message type */
39 MessageType
= LpcpGetMessageType(LpcMessage
);
42 if ((MessageType
< LPC_DATAGRAM
) || (MessageType
> LPC_CLIENT_DIED
))
45 return STATUS_INVALID_PARAMETER
;
48 /* Mark this as a kernel-mode message only if we really came from it */
49 if ((PreviousMode
== KernelMode
) &&
50 (LpcMessage
->u2
.s2
.Type
& LPC_KERNELMODE_MESSAGE
))
52 /* We did, this is a kernel mode message */
53 MessageType
|= LPC_KERNELMODE_MESSAGE
;
58 /* This is a datagram */
59 MessageType
= LPC_DATAGRAM
;
62 /* Can't have data information on this type of call */
63 if (LpcMessage
->u2
.s2
.DataInfoOffset
) return STATUS_INVALID_PARAMETER
;
65 /* Validate the message length */
66 if (((ULONG
)LpcMessage
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
67 ((ULONG
)LpcMessage
->u1
.s1
.TotalLength
<= (ULONG
)LpcMessage
->u1
.s1
.DataLength
))
70 return STATUS_PORT_MESSAGE_TOO_LONG
;
73 /* Allocate a new message */
74 Message
= LpcpAllocateFromPortZone();
75 if (!Message
) return STATUS_NO_MEMORY
;
77 /* Clear the context */
78 Message
->RepliedToThread
= NULL
;
79 Message
->PortContext
= NULL
;
81 /* Copy the message */
82 LpcpMoveMessage(&Message
->Request
,
88 /* Acquire the LPC lock */
89 KeAcquireGuardedMutex(&LpcpLock
);
91 /* Check if this is anything but a connection port */
92 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
94 /* The queue port is the connected port */
95 QueuePort
= Port
->ConnectedPort
;
98 /* Check if this is a client port */
99 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) == LPCP_CLIENT_PORT
)
101 /* Then copy the context */
102 Message
->PortContext
= QueuePort
->PortContext
;
103 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
107 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
108 return STATUS_PORT_DISCONNECTED
;
111 else if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_COMMUNICATION_PORT
)
113 /* Any other kind of port, use the connection port */
114 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
118 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
119 return STATUS_PORT_DISCONNECTED
;
123 /* If we have a connection port, reference it */
124 if (ConnectionPort
) ObReferenceObject(ConnectionPort
);
129 /* For connection ports, use the port itself */
130 QueuePort
= PortObject
;
133 /* Make sure we have a port */
136 /* Generate the Message ID and set it */
137 Message
->Request
.MessageId
= LpcpNextMessageId
++;
138 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
139 Message
->Request
.CallbackId
= 0;
141 /* No Message ID for the thread */
142 Thread
->LpcReplyMessageId
= 0;
144 /* Insert the message in our chain */
145 InsertTailList(&QueuePort
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
147 /* Release the lock and the semaphore */
148 KeEnterCriticalRegion();
149 KeReleaseGuardedMutex(&LpcpLock
);
150 LpcpCompleteWait(QueuePort
->MsgQueue
.Semaphore
);
152 /* If this is a waitable port, wake it up */
153 if (QueuePort
->Flags
& LPCP_WAITABLE_PORT
)
156 KeSetEvent(&QueuePort
->WaitEvent
, IO_NO_INCREMENT
, FALSE
);
159 KeLeaveCriticalRegion();
162 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
163 LPCTRACE(LPC_SEND_DEBUG
, "Port: %p. Message: %p\n", QueuePort
, Message
);
164 return STATUS_SUCCESS
;
167 /* If we got here, then free the message and fail */
168 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
169 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
170 return STATUS_PORT_DISCONNECTED
;
178 LpcRequestWaitReplyPort(IN PVOID PortObject
,
179 IN PPORT_MESSAGE LpcRequest
,
180 OUT PPORT_MESSAGE LpcReply
)
182 PLPCP_PORT_OBJECT Port
, QueuePort
, ReplyPort
, ConnectionPort
= NULL
;
183 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
184 NTSTATUS Status
= STATUS_SUCCESS
;
185 PLPCP_MESSAGE Message
;
186 PETHREAD Thread
= PsGetCurrentThread();
187 BOOLEAN Callback
= FALSE
;
188 PKSEMAPHORE Semaphore
;
192 Port
= (PLPCP_PORT_OBJECT
)PortObject
;
194 LPCTRACE(LPC_SEND_DEBUG
,
195 "Port: %p. Messages: %p/%p. Type: %lx\n",
199 LpcpGetMessageType(LpcRequest
));
201 /* Check if the thread is dying */
202 if (Thread
->LpcExitThreadCalled
) return STATUS_THREAD_IS_TERMINATING
;
204 /* Check if this is an LPC Request */
205 MessageType
= LpcpGetMessageType(LpcRequest
);
211 /* Assume LPC request */
212 MessageType
= LPC_REQUEST
;
215 /* LPC request callback */
218 /* This is a callback */
223 case LPC_CLIENT_DIED
:
224 case LPC_PORT_CLOSED
:
226 case LPC_DEBUG_EVENT
:
227 case LPC_ERROR_EVENT
:
234 /* Invalid message type */
235 return STATUS_INVALID_PARAMETER
;
238 /* Set the request type */
239 LpcRequest
->u2
.s2
.Type
= MessageType
;
241 /* Validate the message length */
242 if (((ULONG
)LpcRequest
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
243 ((ULONG
)LpcRequest
->u1
.s1
.TotalLength
<= (ULONG
)LpcRequest
->u1
.s1
.DataLength
))
246 return STATUS_PORT_MESSAGE_TOO_LONG
;
249 /* Allocate a message from the port zone */
250 Message
= LpcpAllocateFromPortZone();
253 /* Fail if we couldn't allocate a message */
254 return STATUS_NO_MEMORY
;
257 /* Check if this is a callback */
261 Semaphore
= NULL
; // we'd use the Thread Semaphore here
263 return STATUS_NOT_IMPLEMENTED
;
267 /* No callback, just copy the message */
268 LpcpMoveMessage(&Message
->Request
,
274 /* Acquire the LPC lock */
275 KeAcquireGuardedMutex(&LpcpLock
);
277 /* Right now clear the port context */
278 Message
->PortContext
= NULL
;
280 /* Check if this is a not connection port */
281 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
283 /* We want the connected port */
284 QueuePort
= Port
->ConnectedPort
;
287 /* We have no connected port, fail */
288 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
289 return STATUS_PORT_DISCONNECTED
;
292 /* This will be the rundown port */
293 ReplyPort
= QueuePort
;
295 /* Check if this is a communication port */
296 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) == LPCP_CLIENT_PORT
)
298 /* Copy the port context and use the connection port */
299 Message
->PortContext
= QueuePort
->PortContext
;
300 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
304 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
305 return STATUS_PORT_DISCONNECTED
;
308 else if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) !=
309 LPCP_COMMUNICATION_PORT
)
311 /* Use the connection port for anything but communication ports */
312 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
316 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
317 return STATUS_PORT_DISCONNECTED
;
321 /* Reference the connection port if it exists */
322 if (ConnectionPort
) ObReferenceObject(ConnectionPort
);
326 /* Otherwise, for a connection port, use the same port object */
327 QueuePort
= ReplyPort
= Port
;
330 /* No reply thread */
331 Message
->RepliedToThread
= NULL
;
332 Message
->SenderPort
= Port
;
334 /* Generate the Message ID and set it */
335 Message
->Request
.MessageId
= LpcpNextMessageId
++;
336 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
337 Message
->Request
.CallbackId
= 0;
339 /* Set the message ID for our thread now */
340 Thread
->LpcReplyMessageId
= Message
->Request
.MessageId
;
341 Thread
->LpcReplyMessage
= NULL
;
343 /* Insert the message in our chain */
344 InsertTailList(&QueuePort
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
345 InsertTailList(&ReplyPort
->LpcReplyChainHead
, &Thread
->LpcReplyChain
);
346 LpcpSetPortToThread(Thread
, Port
);
348 /* Release the lock and get the semaphore we'll use later */
349 KeEnterCriticalRegion();
350 KeReleaseGuardedMutex(&LpcpLock
);
351 Semaphore
= QueuePort
->MsgQueue
.Semaphore
;
353 /* If this is a waitable port, wake it up */
354 if (QueuePort
->Flags
& LPCP_WAITABLE_PORT
)
357 KeSetEvent(&QueuePort
->WaitEvent
, IO_NO_INCREMENT
, FALSE
);
361 /* Now release the semaphore */
362 LpcpCompleteWait(Semaphore
);
363 KeLeaveCriticalRegion();
365 /* And let's wait for the reply */
366 LpcpReplyWait(&Thread
->LpcReplySemaphore
, PreviousMode
);
368 /* Acquire the LPC lock */
369 KeAcquireGuardedMutex(&LpcpLock
);
371 /* Get the LPC Message and clear our thread's reply data */
372 Message
= LpcpGetMessageFromThread(Thread
);
373 Thread
->LpcReplyMessage
= NULL
;
374 Thread
->LpcReplyMessageId
= 0;
376 /* Check if we have anything on the reply chain*/
377 if (!IsListEmpty(&Thread
->LpcReplyChain
))
379 /* Remove this thread and reinitialize the list */
380 RemoveEntryList(&Thread
->LpcReplyChain
);
381 InitializeListHead(&Thread
->LpcReplyChain
);
384 /* Release the lock */
385 KeReleaseGuardedMutex(&LpcpLock
);
387 /* Check if we got a reply */
388 if (Status
== STATUS_SUCCESS
)
390 /* Check if we have a valid message */
393 LPCTRACE(LPC_SEND_DEBUG
,
394 "Reply Messages: %p/%p\n",
396 (&Message
->Request
) + 1);
398 /* Move the message */
399 LpcpMoveMessage(LpcReply
,
401 (&Message
->Request
) + 1,
405 /* Acquire the lock */
406 KeAcquireGuardedMutex(&LpcpLock
);
408 /* Check if we replied to a thread */
409 if (Message
->RepliedToThread
)
412 ObDereferenceObject(Message
->RepliedToThread
);
413 Message
->RepliedToThread
= NULL
;
416 /* Free the message */
417 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
421 /* We don't have a reply */
422 Status
= STATUS_LPC_REPLY_LOST
;
427 /* The wait failed, free the message */
428 if (Message
) LpcpFreeToPortZone(Message
, 0);
432 LPCTRACE(LPC_SEND_DEBUG
,
433 "Port: %p. Status: %d\n",
437 /* Dereference the connection port */
438 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
447 NtRequestPort(IN HANDLE PortHandle
,
448 IN PPORT_MESSAGE LpcRequest
)
451 PLPCP_PORT_OBJECT Port
, QueuePort
, ConnectionPort
= NULL
;
453 PLPCP_MESSAGE Message
;
454 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
455 PETHREAD Thread
= PsGetCurrentThread();
459 LPCTRACE(LPC_SEND_DEBUG
,
460 "Handle: %p. Message: %p. Type: %lx\n",
463 LpcpGetMessageType(LpcRequest
));
465 /* Get the message type */
466 MessageType
= LpcRequest
->u2
.s2
.Type
| LPC_DATAGRAM
;
468 /* Can't have data information on this type of call */
469 if (LpcRequest
->u2
.s2
.DataInfoOffset
) return STATUS_INVALID_PARAMETER
;
471 /* Validate the length */
472 if (((ULONG
)LpcRequest
->u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
473 (ULONG
)LpcRequest
->u1
.s1
.TotalLength
)
476 return STATUS_INVALID_PARAMETER
;
479 /* Reference the object */
480 Status
= ObReferenceObjectByHandle(PortHandle
,
486 if (!NT_SUCCESS(Status
)) return Status
;
488 /* Validate the message length */
489 if (((ULONG
)LpcRequest
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
490 ((ULONG
)LpcRequest
->u1
.s1
.TotalLength
<= (ULONG
)LpcRequest
->u1
.s1
.DataLength
))
493 ObDereferenceObject(Port
);
494 return STATUS_PORT_MESSAGE_TOO_LONG
;
497 /* Allocate a message from the port zone */
498 Message
= LpcpAllocateFromPortZone();
501 /* Fail if we couldn't allocate a message */
502 ObDereferenceObject(Port
);
503 return STATUS_NO_MEMORY
;
506 /* No callback, just copy the message */
510 LpcpMoveMessage(&Message
->Request
,
516 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
519 LpcpFreeToPortZone(Message
, 0);
520 ObDereferenceObject(Port
);
521 _SEH2_YIELD(return _SEH2_GetExceptionCode());
525 /* Acquire the LPC lock */
526 KeAcquireGuardedMutex(&LpcpLock
);
528 /* Right now clear the port context */
529 Message
->PortContext
= NULL
;
531 /* Check if this is a not connection port */
532 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
534 /* We want the connected port */
535 QueuePort
= Port
->ConnectedPort
;
538 /* We have no connected port, fail */
539 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
540 ObDereferenceObject(Port
);
541 return STATUS_PORT_DISCONNECTED
;
544 /* Check if this is a communication port */
545 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) == LPCP_CLIENT_PORT
)
547 /* Copy the port context and use the connection port */
548 Message
->PortContext
= QueuePort
->PortContext
;
549 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
553 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
554 ObDereferenceObject(Port
);
555 return STATUS_PORT_DISCONNECTED
;
558 else if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) !=
559 LPCP_COMMUNICATION_PORT
)
561 /* Use the connection port for anything but communication ports */
562 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
566 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
567 ObDereferenceObject(Port
);
568 return STATUS_PORT_DISCONNECTED
;
572 /* Reference the connection port if it exists */
573 if (ConnectionPort
) ObReferenceObject(ConnectionPort
);
577 /* Otherwise, for a connection port, use the same port object */
581 /* Reference QueuePort if we have it */
582 if (QueuePort
&& ObReferenceObjectSafe(QueuePort
))
584 /* Set sender's port */
585 Message
->SenderPort
= Port
;
587 /* Generate the Message ID and set it */
588 Message
->Request
.MessageId
= LpcpNextMessageId
++;
589 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
590 Message
->Request
.CallbackId
= 0;
592 /* No Message ID for the thread */
593 Thread
->LpcReplyMessageId
= 0;
595 /* Insert the message in our chain */
596 InsertTailList(&QueuePort
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
598 /* Release the lock and the semaphore */
599 KeEnterCriticalRegion();
600 KeReleaseGuardedMutex(&LpcpLock
);
601 LpcpCompleteWait(QueuePort
->MsgQueue
.Semaphore
);
603 /* If this is a waitable port, wake it up */
604 if (QueuePort
->Flags
& LPCP_WAITABLE_PORT
)
607 KeSetEvent(&QueuePort
->WaitEvent
, IO_NO_INCREMENT
, FALSE
);
610 KeLeaveCriticalRegion();
612 /* Dereference objects */
613 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
614 ObDereferenceObject(QueuePort
);
615 ObDereferenceObject(Port
);
616 LPCTRACE(LPC_SEND_DEBUG
, "Port: %p. Message: %p\n", QueuePort
, Message
);
617 return STATUS_SUCCESS
;
620 Status
= STATUS_PORT_DISCONNECTED
;
622 /* All done with a failure*/
623 LPCTRACE(LPC_SEND_DEBUG
,
624 "Port: %p. Status: %d\n",
628 /* The wait failed, free the message */
629 if (Message
) LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
631 ObDereferenceObject(Port
);
632 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
638 LpcpVerifyMessageDataInfo(
639 _In_ PPORT_MESSAGE Message
,
640 _Out_ PULONG NumberOfDataEntries
)
642 PLPCP_DATA_INFO DataInfo
;
645 /* Check if we have no data info at all */
646 if (Message
->u2
.s2
.DataInfoOffset
== 0)
648 *NumberOfDataEntries
= 0;
649 return STATUS_SUCCESS
;
652 /* Make sure the data info structure is within the message */
653 if (((ULONG
)Message
->u1
.s1
.TotalLength
<
654 sizeof(PORT_MESSAGE
) + sizeof(LPCP_DATA_INFO
)) ||
655 ((ULONG
)Message
->u2
.s2
.DataInfoOffset
< sizeof(PORT_MESSAGE
)) ||
656 ((ULONG
)Message
->u2
.s2
.DataInfoOffset
>
657 ((ULONG
)Message
->u1
.s1
.TotalLength
- sizeof(LPCP_DATA_INFO
))))
659 return STATUS_INVALID_PARAMETER
;
662 /* Get a pointer to the data info */
663 DataInfo
= LpcpGetDataInfoFromMessage(Message
);
665 /* Make sure the full data info with all entries is within the message */
666 EndOfEntries
= (PUCHAR
)&DataInfo
->Entries
[DataInfo
->NumberOfEntries
];
667 if ((EndOfEntries
> ((PUCHAR
)Message
+ (ULONG
)Message
->u1
.s1
.TotalLength
)) ||
668 (EndOfEntries
< (PUCHAR
)Message
))
670 return STATUS_INVALID_PARAMETER
;
673 *NumberOfDataEntries
= DataInfo
->NumberOfEntries
;
674 return STATUS_SUCCESS
;
682 NtRequestWaitReplyPort(IN HANDLE PortHandle
,
683 IN PPORT_MESSAGE LpcRequest
,
684 IN OUT PPORT_MESSAGE LpcReply
)
686 PORT_MESSAGE LocalLpcRequest
;
687 ULONG NumberOfDataEntries
;
688 PLPCP_PORT_OBJECT Port
, QueuePort
, ReplyPort
, ConnectionPort
= NULL
;
689 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
691 PLPCP_MESSAGE Message
;
692 PETHREAD Thread
= PsGetCurrentThread();
694 PKSEMAPHORE Semaphore
;
696 PLPCP_DATA_INFO DataInfo
;
698 LPCTRACE(LPC_SEND_DEBUG
,
699 "Handle: %p. Messages: %p/%p. Type: %lx\n",
703 LpcpGetMessageType(LpcRequest
));
705 /* Check if the thread is dying */
706 if (Thread
->LpcExitThreadCalled
) return STATUS_THREAD_IS_TERMINATING
;
708 /* Check for user mode access */
709 if (PreviousMode
!= KernelMode
)
713 /* Probe the full request message and copy the base structure */
714 ProbeForRead(LpcRequest
, sizeof(*LpcRequest
), sizeof(ULONG
));
715 ProbeForRead(LpcRequest
, LpcRequest
->u1
.s1
.TotalLength
, sizeof(ULONG
));
716 LocalLpcRequest
= *LpcRequest
;
718 /* Probe the reply message for write */
719 ProbeForWrite(LpcReply
, sizeof(*LpcReply
), sizeof(ULONG
));
721 /* Make sure the data entries in the request message are valid */
722 Status
= LpcpVerifyMessageDataInfo(LpcRequest
, &NumberOfDataEntries
);
723 if (!NT_SUCCESS(Status
))
725 DPRINT1("LpcpVerifyMessageDataInfo failed\n");
729 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
731 DPRINT1("Got exception\n");
732 return _SEH2_GetExceptionCode();
738 LocalLpcRequest
= *LpcRequest
;
739 Status
= LpcpVerifyMessageDataInfo(LpcRequest
, &NumberOfDataEntries
);
740 if (!NT_SUCCESS(Status
))
742 DPRINT1("LpcpVerifyMessageDataInfo failed\n");
747 /* This flag is undocumented. Remove it before continuing */
748 LocalLpcRequest
.u2
.s2
.Type
&= ~0x4000;
750 /* Check if this is an LPC Request */
751 if (LpcpGetMessageType(&LocalLpcRequest
) == LPC_REQUEST
)
753 /* Then it's a callback */
756 else if (LpcpGetMessageType(&LocalLpcRequest
))
758 /* This is a not kernel-mode message */
759 DPRINT1("Not a kernel-mode message!\n");
760 return STATUS_INVALID_PARAMETER
;
764 /* This is a kernel-mode message without a callback */
765 LocalLpcRequest
.u2
.s2
.Type
|= LPC_REQUEST
;
769 /* Get the message type */
770 MessageType
= LocalLpcRequest
.u2
.s2
.Type
;
772 /* Due to the above probe, we know that TotalLength is positive */
773 NT_ASSERT(LocalLpcRequest
.u1
.s1
.TotalLength
>= 0);
775 /* Validate the length */
776 if ((((ULONG
)(USHORT
)LocalLpcRequest
.u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
777 (ULONG
)LocalLpcRequest
.u1
.s1
.TotalLength
))
780 DPRINT1("Invalid message length: %u, %u\n",
781 LocalLpcRequest
.u1
.s1
.DataLength
,
782 LocalLpcRequest
.u1
.s1
.TotalLength
);
783 return STATUS_INVALID_PARAMETER
;
786 /* Reference the object */
787 Status
= ObReferenceObjectByHandle(PortHandle
,
793 if (!NT_SUCCESS(Status
)) return Status
;
795 /* Validate the message length */
796 if (((ULONG
)LocalLpcRequest
.u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
797 ((ULONG
)LocalLpcRequest
.u1
.s1
.TotalLength
<= (ULONG
)LocalLpcRequest
.u1
.s1
.DataLength
))
800 DPRINT1("Invalid message length: %u, %u\n",
801 LocalLpcRequest
.u1
.s1
.DataLength
,
802 LocalLpcRequest
.u1
.s1
.TotalLength
);
803 ObDereferenceObject(Port
);
804 return STATUS_PORT_MESSAGE_TOO_LONG
;
807 /* Allocate a message from the port zone */
808 Message
= LpcpAllocateFromPortZone();
811 /* Fail if we couldn't allocate a message */
812 DPRINT1("Failed to allocate a message!\n");
813 ObDereferenceObject(Port
);
814 return STATUS_NO_MEMORY
;
817 /* Check if this is a callback */
821 Semaphore
= NULL
; // we'd use the Thread Semaphore here
826 /* No callback, just copy the message */
829 /* Check if we have data info entries */
830 if (LpcRequest
->u2
.s2
.DataInfoOffset
!= 0)
832 /* Get the data info and check if the number of entries matches
834 DataInfo
= LpcpGetDataInfoFromMessage(LpcRequest
);
835 if (DataInfo
->NumberOfEntries
!= NumberOfDataEntries
)
837 LpcpFreeToPortZone(Message
, 0);
838 ObDereferenceObject(Port
);
839 DPRINT1("NumberOfEntries has changed: %u, %u\n",
840 DataInfo
->NumberOfEntries
, NumberOfDataEntries
);
841 return STATUS_INVALID_PARAMETER
;
846 LpcpMoveMessage(&Message
->Request
,
852 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
855 DPRINT1("Got exception!\n");
856 LpcpFreeToPortZone(Message
, 0);
857 ObDereferenceObject(Port
);
858 _SEH2_YIELD(return _SEH2_GetExceptionCode());
862 /* Acquire the LPC lock */
863 KeAcquireGuardedMutex(&LpcpLock
);
865 /* Right now clear the port context */
866 Message
->PortContext
= NULL
;
868 /* Check if this is a not connection port */
869 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
871 /* We want the connected port */
872 QueuePort
= Port
->ConnectedPort
;
875 /* We have no connected port, fail */
876 DPRINT1("No connected port\n");
877 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
878 ObDereferenceObject(Port
);
879 return STATUS_PORT_DISCONNECTED
;
882 /* This will be the rundown port */
883 ReplyPort
= QueuePort
;
885 /* Check if this is a client port */
886 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) == LPCP_CLIENT_PORT
)
888 /* Copy the port context */
889 Message
->PortContext
= QueuePort
->PortContext
;
892 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_COMMUNICATION_PORT
)
894 /* Use the connection port for anything but communication ports */
895 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
899 DPRINT1("No connection port\n");
900 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
901 ObDereferenceObject(Port
);
902 return STATUS_PORT_DISCONNECTED
;
906 /* Reference the connection port if it exists */
907 if (ConnectionPort
) ObReferenceObject(ConnectionPort
);
911 /* Otherwise, for a connection port, use the same port object */
912 QueuePort
= ReplyPort
= Port
;
915 /* No reply thread */
916 Message
->RepliedToThread
= NULL
;
917 Message
->SenderPort
= Port
;
919 /* Generate the Message ID and set it */
920 Message
->Request
.MessageId
= LpcpNextMessageId
++;
921 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
922 Message
->Request
.CallbackId
= 0;
924 /* Set the message ID for our thread now */
925 Thread
->LpcReplyMessageId
= Message
->Request
.MessageId
;
926 Thread
->LpcReplyMessage
= NULL
;
928 /* Insert the message in our chain */
929 InsertTailList(&QueuePort
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
930 InsertTailList(&ReplyPort
->LpcReplyChainHead
, &Thread
->LpcReplyChain
);
931 LpcpSetPortToThread(Thread
, Port
);
933 /* Release the lock and get the semaphore we'll use later */
934 KeEnterCriticalRegion();
935 KeReleaseGuardedMutex(&LpcpLock
);
936 Semaphore
= QueuePort
->MsgQueue
.Semaphore
;
938 /* If this is a waitable port, wake it up */
939 if (QueuePort
->Flags
& LPCP_WAITABLE_PORT
)
942 KeSetEvent(&QueuePort
->WaitEvent
, IO_NO_INCREMENT
, FALSE
);
946 /* Now release the semaphore */
947 LpcpCompleteWait(Semaphore
);
948 KeLeaveCriticalRegion();
950 /* And let's wait for the reply */
951 LpcpReplyWait(&Thread
->LpcReplySemaphore
, PreviousMode
);
953 /* Acquire the LPC lock */
954 KeAcquireGuardedMutex(&LpcpLock
);
956 /* Get the LPC Message and clear our thread's reply data */
957 Message
= LpcpGetMessageFromThread(Thread
);
958 Thread
->LpcReplyMessage
= NULL
;
959 Thread
->LpcReplyMessageId
= 0;
961 /* Check if we have anything on the reply chain*/
962 if (!IsListEmpty(&Thread
->LpcReplyChain
))
964 /* Remove this thread and reinitialize the list */
965 RemoveEntryList(&Thread
->LpcReplyChain
);
966 InitializeListHead(&Thread
->LpcReplyChain
);
969 /* Release the lock */
970 KeReleaseGuardedMutex(&LpcpLock
);
972 /* Check if we got a reply */
973 if (Status
== STATUS_SUCCESS
)
975 /* Check if we have a valid message */
978 LPCTRACE(LPC_SEND_DEBUG
,
979 "Reply Messages: %p/%p\n",
981 (&Message
->Request
) + 1);
983 /* Move the message */
986 LpcpMoveMessage(LpcReply
,
988 (&Message
->Request
) + 1,
992 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
994 DPRINT1("Got exception!\n");
995 Status
= _SEH2_GetExceptionCode();
999 /* Check if this is an LPC request with data information */
1000 if ((LpcpGetMessageType(&Message
->Request
) == LPC_REQUEST
) &&
1001 (Message
->Request
.u2
.s2
.DataInfoOffset
))
1003 /* Save the data information */
1004 LpcpSaveDataInfoMessage(Port
, Message
, 0);
1008 /* Otherwise, just free it */
1009 LpcpFreeToPortZone(Message
, 0);
1014 /* We don't have a reply */
1015 Status
= STATUS_LPC_REPLY_LOST
;
1020 /* The wait failed, free the message */
1021 if (Message
) LpcpFreeToPortZone(Message
, 0);
1025 LPCTRACE(LPC_SEND_DEBUG
,
1026 "Port: %p. Status: %d\n",
1029 ObDereferenceObject(Port
);
1030 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);