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 NTSTATUS Status
= STATUS_SUCCESS
;
183 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
184 PETHREAD Thread
= PsGetCurrentThread();
185 PLPCP_PORT_OBJECT Port
= (PLPCP_PORT_OBJECT
)PortObject
;
186 PLPCP_PORT_OBJECT QueuePort
, ReplyPort
, ConnectionPort
= NULL
;
188 PLPCP_MESSAGE Message
;
189 BOOLEAN Callback
= FALSE
;
190 PKSEMAPHORE Semaphore
;
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
);
208 /* No type, assume LPC request */
210 MessageType
= LPC_REQUEST
;
213 /* LPC request callback */
218 /* Anything else, nothing to do */
219 case LPC_CLIENT_DIED
:
220 case LPC_PORT_CLOSED
:
222 case LPC_DEBUG_EVENT
:
223 case LPC_ERROR_EVENT
:
226 /* Invalid message type */
228 return STATUS_INVALID_PARAMETER
;
231 /* Set the request type */
232 LpcRequest
->u2
.s2
.Type
= MessageType
;
234 /* Validate the message length */
235 if (((ULONG
)LpcRequest
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
236 ((ULONG
)LpcRequest
->u1
.s1
.TotalLength
<= (ULONG
)LpcRequest
->u1
.s1
.DataLength
))
239 return STATUS_PORT_MESSAGE_TOO_LONG
;
242 /* Allocate a message from the port zone */
243 Message
= LpcpAllocateFromPortZone();
246 /* Fail if we couldn't allocate a message */
247 return STATUS_NO_MEMORY
;
250 /* Check if this is a callback */
254 Semaphore
= NULL
; // we'd use the Thread Semaphore here
256 return STATUS_NOT_IMPLEMENTED
;
260 /* No callback, just copy the message */
261 LpcpMoveMessage(&Message
->Request
,
267 /* Acquire the LPC lock */
268 KeAcquireGuardedMutex(&LpcpLock
);
270 /* Right now clear the port context */
271 Message
->PortContext
= NULL
;
273 /* Check if this is a not connection port */
274 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
276 /* We want the connected port */
277 QueuePort
= Port
->ConnectedPort
;
280 /* We have no connected port, fail */
281 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
282 return STATUS_PORT_DISCONNECTED
;
285 /* This will be the rundown port */
286 ReplyPort
= QueuePort
;
288 /* Check if this is a communication port */
289 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) == LPCP_CLIENT_PORT
)
291 /* Copy the port context and use the connection port */
292 Message
->PortContext
= QueuePort
->PortContext
;
293 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
297 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
298 return STATUS_PORT_DISCONNECTED
;
301 else if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) !=
302 LPCP_COMMUNICATION_PORT
)
304 /* Use the connection port for anything but communication ports */
305 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
309 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
310 return STATUS_PORT_DISCONNECTED
;
314 /* Reference the connection port if it exists */
315 if (ConnectionPort
) ObReferenceObject(ConnectionPort
);
319 /* Otherwise, for a connection port, use the same port object */
320 QueuePort
= ReplyPort
= Port
;
323 /* No reply thread */
324 Message
->RepliedToThread
= NULL
;
325 Message
->SenderPort
= Port
;
327 /* Generate the Message ID and set it */
328 Message
->Request
.MessageId
= LpcpNextMessageId
++;
329 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
330 Message
->Request
.CallbackId
= 0;
332 /* Set the message ID for our thread now */
333 Thread
->LpcReplyMessageId
= Message
->Request
.MessageId
;
334 Thread
->LpcReplyMessage
= NULL
;
336 /* Insert the message in our chain */
337 InsertTailList(&QueuePort
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
338 InsertTailList(&ReplyPort
->LpcReplyChainHead
, &Thread
->LpcReplyChain
);
339 LpcpSetPortToThread(Thread
, Port
);
341 /* Release the lock and get the semaphore we'll use later */
342 KeEnterCriticalRegion();
343 KeReleaseGuardedMutex(&LpcpLock
);
344 Semaphore
= QueuePort
->MsgQueue
.Semaphore
;
346 /* If this is a waitable port, wake it up */
347 if (QueuePort
->Flags
& LPCP_WAITABLE_PORT
)
350 KeSetEvent(&QueuePort
->WaitEvent
, IO_NO_INCREMENT
, FALSE
);
354 /* Now release the semaphore */
355 LpcpCompleteWait(Semaphore
);
356 KeLeaveCriticalRegion();
358 /* And let's wait for the reply */
359 LpcpReplyWait(&Thread
->LpcReplySemaphore
, PreviousMode
);
361 /* Acquire the LPC lock */
362 KeAcquireGuardedMutex(&LpcpLock
);
364 /* Get the LPC Message and clear our thread's reply data */
365 Message
= LpcpGetMessageFromThread(Thread
);
366 Thread
->LpcReplyMessage
= NULL
;
367 Thread
->LpcReplyMessageId
= 0;
369 /* Check if we have anything on the reply chain*/
370 if (!IsListEmpty(&Thread
->LpcReplyChain
))
372 /* Remove this thread and reinitialize the list */
373 RemoveEntryList(&Thread
->LpcReplyChain
);
374 InitializeListHead(&Thread
->LpcReplyChain
);
377 /* Release the lock */
378 KeReleaseGuardedMutex(&LpcpLock
);
380 /* Check if we got a reply */
381 if (Status
== STATUS_SUCCESS
)
383 /* Check if we have a valid message */
386 LPCTRACE(LPC_SEND_DEBUG
,
387 "Reply Messages: %p/%p\n",
389 (&Message
->Request
) + 1);
391 /* Move the message */
392 LpcpMoveMessage(LpcReply
,
394 (&Message
->Request
) + 1,
398 /* Acquire the lock */
399 KeAcquireGuardedMutex(&LpcpLock
);
401 /* Check if we replied to a thread */
402 if (Message
->RepliedToThread
)
405 ObDereferenceObject(Message
->RepliedToThread
);
406 Message
->RepliedToThread
= NULL
;
409 /* Free the message */
410 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
414 /* We don't have a reply */
415 Status
= STATUS_LPC_REPLY_LOST
;
420 /* The wait failed, free the message */
421 if (Message
) LpcpFreeToPortZone(Message
, 0);
425 LPCTRACE(LPC_SEND_DEBUG
,
426 "Port: %p. Status: %d\n",
430 /* Dereference the connection port */
431 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
440 NtRequestPort(IN HANDLE PortHandle
,
441 IN PPORT_MESSAGE LpcRequest
)
444 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
445 PETHREAD Thread
= PsGetCurrentThread();
446 PORT_MESSAGE CapturedLpcRequest
;
447 PLPCP_PORT_OBJECT Port
, QueuePort
, ConnectionPort
= NULL
;
449 PLPCP_MESSAGE Message
;
452 LPCTRACE(LPC_SEND_DEBUG
,
453 "Handle: %p. Message: %p. Type: %lx\n",
456 LpcpGetMessageType(LpcRequest
));
458 /* Check if the call comes from user mode */
459 if (PreviousMode
!= KernelMode
)
463 /* Probe and capture the LpcRequest */
464 ProbeForRead(LpcRequest
, sizeof(*LpcRequest
), sizeof(ULONG
));
465 CapturedLpcRequest
= *(volatile PORT_MESSAGE
*)LpcRequest
;
467 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
469 _SEH2_YIELD(return _SEH2_GetExceptionCode());
475 /* Access the LpcRequest directly */
476 CapturedLpcRequest
= *LpcRequest
;
479 /* Get the message type */
480 MessageType
= CapturedLpcRequest
.u2
.s2
.Type
| LPC_DATAGRAM
;
482 /* Can't have data information on this type of call */
483 if (CapturedLpcRequest
.u2
.s2
.DataInfoOffset
) return STATUS_INVALID_PARAMETER
;
485 /* Validate the length */
486 if (((ULONG
)CapturedLpcRequest
.u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
487 (ULONG
)CapturedLpcRequest
.u1
.s1
.TotalLength
)
490 return STATUS_INVALID_PARAMETER
;
493 /* Reference the object */
494 Status
= ObReferenceObjectByHandle(PortHandle
,
500 if (!NT_SUCCESS(Status
)) return Status
;
502 /* Validate the message length */
503 if (((ULONG
)CapturedLpcRequest
.u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
504 ((ULONG
)CapturedLpcRequest
.u1
.s1
.TotalLength
<= (ULONG
)CapturedLpcRequest
.u1
.s1
.DataLength
))
507 ObDereferenceObject(Port
);
508 return STATUS_PORT_MESSAGE_TOO_LONG
;
511 /* Allocate a message from the port zone */
512 Message
= LpcpAllocateFromPortZone();
515 /* Fail if we couldn't allocate a message */
516 ObDereferenceObject(Port
);
517 return STATUS_NO_MEMORY
;
520 /* No callback, just copy the message */
524 LpcpMoveMessage(&Message
->Request
,
530 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
532 /* Cleanup and return the exception code */
533 LpcpFreeToPortZone(Message
, 0);
534 ObDereferenceObject(Port
);
535 _SEH2_YIELD(return _SEH2_GetExceptionCode());
539 /* Acquire the LPC lock */
540 KeAcquireGuardedMutex(&LpcpLock
);
542 /* Right now clear the port context */
543 Message
->PortContext
= NULL
;
545 /* Check if this is a not connection port */
546 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
548 /* We want the connected port */
549 QueuePort
= Port
->ConnectedPort
;
552 /* We have no connected port, fail */
553 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
554 ObDereferenceObject(Port
);
555 return STATUS_PORT_DISCONNECTED
;
558 /* Check if this is a communication port */
559 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) == LPCP_CLIENT_PORT
)
561 /* Copy the port context and use the connection port */
562 Message
->PortContext
= QueuePort
->PortContext
;
563 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
567 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
568 ObDereferenceObject(Port
);
569 return STATUS_PORT_DISCONNECTED
;
572 else if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_COMMUNICATION_PORT
)
574 /* Use the connection port for anything but communication ports */
575 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
579 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
580 ObDereferenceObject(Port
);
581 return STATUS_PORT_DISCONNECTED
;
585 /* Reference the connection port if it exists */
586 if (ConnectionPort
) ObReferenceObject(ConnectionPort
);
590 /* Otherwise, for a connection port, use the same port object */
594 /* Reference QueuePort if we have it */
595 if (QueuePort
&& ObReferenceObjectSafe(QueuePort
))
597 /* Set sender's port */
598 Message
->SenderPort
= Port
;
600 /* Generate the Message ID and set it */
601 Message
->Request
.MessageId
= LpcpNextMessageId
++;
602 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
603 Message
->Request
.CallbackId
= 0;
605 /* No Message ID for the thread */
606 Thread
->LpcReplyMessageId
= 0;
608 /* Insert the message in our chain */
609 InsertTailList(&QueuePort
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
611 /* Release the lock and the semaphore */
612 KeEnterCriticalRegion();
613 KeReleaseGuardedMutex(&LpcpLock
);
614 LpcpCompleteWait(QueuePort
->MsgQueue
.Semaphore
);
616 /* If this is a waitable port, wake it up */
617 if (QueuePort
->Flags
& LPCP_WAITABLE_PORT
)
620 KeSetEvent(&QueuePort
->WaitEvent
, IO_NO_INCREMENT
, FALSE
);
623 KeLeaveCriticalRegion();
625 /* Dereference objects */
626 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
627 ObDereferenceObject(QueuePort
);
628 ObDereferenceObject(Port
);
629 LPCTRACE(LPC_SEND_DEBUG
, "Port: %p. Message: %p\n", QueuePort
, Message
);
630 return STATUS_SUCCESS
;
633 Status
= STATUS_PORT_DISCONNECTED
;
635 /* All done with a failure*/
636 LPCTRACE(LPC_SEND_DEBUG
,
637 "Port: %p. Status: %d\n",
641 /* The wait failed, free the message */
642 if (Message
) LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
644 ObDereferenceObject(Port
);
645 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
651 LpcpVerifyMessageDataInfo(
652 _In_ PPORT_MESSAGE Message
,
653 _Out_ PULONG NumberOfDataEntries
)
655 PLPCP_DATA_INFO DataInfo
;
658 /* Check if we have no data info at all */
659 if (Message
->u2
.s2
.DataInfoOffset
== 0)
661 *NumberOfDataEntries
= 0;
662 return STATUS_SUCCESS
;
665 /* Make sure the data info structure is within the message */
666 if (((ULONG
)Message
->u1
.s1
.TotalLength
<
667 sizeof(PORT_MESSAGE
) + sizeof(LPCP_DATA_INFO
)) ||
668 ((ULONG
)Message
->u2
.s2
.DataInfoOffset
< sizeof(PORT_MESSAGE
)) ||
669 ((ULONG
)Message
->u2
.s2
.DataInfoOffset
>
670 ((ULONG
)Message
->u1
.s1
.TotalLength
- sizeof(LPCP_DATA_INFO
))))
672 return STATUS_INVALID_PARAMETER
;
675 /* Get a pointer to the data info */
676 DataInfo
= LpcpGetDataInfoFromMessage(Message
);
678 /* Make sure the full data info with all entries is within the message */
679 EndOfEntries
= (PUCHAR
)&DataInfo
->Entries
[DataInfo
->NumberOfEntries
];
680 if ((EndOfEntries
> ((PUCHAR
)Message
+ (ULONG
)Message
->u1
.s1
.TotalLength
)) ||
681 (EndOfEntries
< (PUCHAR
)Message
))
683 return STATUS_INVALID_PARAMETER
;
686 *NumberOfDataEntries
= DataInfo
->NumberOfEntries
;
687 return STATUS_SUCCESS
;
695 NtRequestWaitReplyPort(IN HANDLE PortHandle
,
696 IN PPORT_MESSAGE LpcRequest
,
697 IN OUT PPORT_MESSAGE LpcReply
)
700 PORT_MESSAGE CapturedLpcRequest
;
701 ULONG NumberOfDataEntries
;
702 PLPCP_PORT_OBJECT Port
, QueuePort
, ReplyPort
, ConnectionPort
= NULL
;
703 PLPCP_MESSAGE Message
;
704 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
705 PETHREAD Thread
= PsGetCurrentThread();
707 PKSEMAPHORE Semaphore
;
709 PLPCP_DATA_INFO DataInfo
;
712 LPCTRACE(LPC_SEND_DEBUG
,
713 "Handle: %p. Messages: %p/%p. Type: %lx\n",
717 LpcpGetMessageType(LpcRequest
));
719 /* Check if the thread is dying */
720 if (Thread
->LpcExitThreadCalled
) return STATUS_THREAD_IS_TERMINATING
;
722 /* Check for user mode access */
723 if (PreviousMode
!= KernelMode
)
727 /* Probe and capture the LpcRequest */
728 ProbeForRead(LpcRequest
, sizeof(*LpcRequest
), sizeof(ULONG
));
729 CapturedLpcRequest
= *(volatile PORT_MESSAGE
*)LpcRequest
;
731 /* Probe the reply message for write */
732 ProbeForWrite(LpcReply
, sizeof(*LpcReply
), sizeof(ULONG
));
734 /* Make sure the data entries in the request message are valid */
735 Status
= LpcpVerifyMessageDataInfo(LpcRequest
, &NumberOfDataEntries
);
736 if (!NT_SUCCESS(Status
))
738 DPRINT1("LpcpVerifyMessageDataInfo failed\n");
739 _SEH2_YIELD(return Status
);
742 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
744 DPRINT1("Got exception\n");
745 _SEH2_YIELD(return _SEH2_GetExceptionCode());
751 CapturedLpcRequest
= *LpcRequest
;
752 Status
= LpcpVerifyMessageDataInfo(LpcRequest
, &NumberOfDataEntries
);
753 if (!NT_SUCCESS(Status
))
755 DPRINT1("LpcpVerifyMessageDataInfo failed\n");
760 /* This flag is undocumented. Remove it before continuing */
761 CapturedLpcRequest
.u2
.s2
.Type
&= ~0x4000;
763 /* Check if this is an LPC Request */
764 if (LpcpGetMessageType(&CapturedLpcRequest
) == LPC_REQUEST
)
766 /* Then it's a callback */
769 else if (LpcpGetMessageType(&CapturedLpcRequest
))
771 /* This is a not kernel-mode message */
772 DPRINT1("Not a kernel-mode message!\n");
773 return STATUS_INVALID_PARAMETER
;
777 /* This is a kernel-mode message without a callback */
778 CapturedLpcRequest
.u2
.s2
.Type
|= LPC_REQUEST
;
782 /* Get the message type */
783 MessageType
= CapturedLpcRequest
.u2
.s2
.Type
;
785 /* Due to the above probe, we know that TotalLength is positive */
786 ASSERT(CapturedLpcRequest
.u1
.s1
.TotalLength
>= 0);
788 /* Validate the length */
789 if ((((ULONG
)(USHORT
)CapturedLpcRequest
.u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
790 (ULONG
)CapturedLpcRequest
.u1
.s1
.TotalLength
))
793 DPRINT1("Invalid message length: %u, %u\n",
794 CapturedLpcRequest
.u1
.s1
.DataLength
,
795 CapturedLpcRequest
.u1
.s1
.TotalLength
);
796 return STATUS_INVALID_PARAMETER
;
799 /* Reference the object */
800 Status
= ObReferenceObjectByHandle(PortHandle
,
806 if (!NT_SUCCESS(Status
)) return Status
;
808 /* Validate the message length */
809 if (((ULONG
)CapturedLpcRequest
.u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
810 ((ULONG
)CapturedLpcRequest
.u1
.s1
.TotalLength
<= (ULONG
)CapturedLpcRequest
.u1
.s1
.DataLength
))
813 DPRINT1("Invalid message length: %u, %u\n",
814 CapturedLpcRequest
.u1
.s1
.DataLength
,
815 CapturedLpcRequest
.u1
.s1
.TotalLength
);
816 ObDereferenceObject(Port
);
817 return STATUS_PORT_MESSAGE_TOO_LONG
;
820 /* Allocate a message from the port zone */
821 Message
= LpcpAllocateFromPortZone();
824 /* Fail if we couldn't allocate a message */
825 DPRINT1("Failed to allocate a message!\n");
826 ObDereferenceObject(Port
);
827 return STATUS_NO_MEMORY
;
830 /* Check if this is a callback */
834 Semaphore
= NULL
; // we'd use the Thread Semaphore here
839 /* No callback, just copy the message */
842 /* Check if we have data info entries */
843 if (LpcRequest
->u2
.s2
.DataInfoOffset
!= 0)
845 /* Get the data info and check if the number of entries matches
847 DataInfo
= LpcpGetDataInfoFromMessage(LpcRequest
);
848 if (DataInfo
->NumberOfEntries
!= NumberOfDataEntries
)
850 LpcpFreeToPortZone(Message
, 0);
851 ObDereferenceObject(Port
);
852 DPRINT1("NumberOfEntries has changed: %u, %u\n",
853 DataInfo
->NumberOfEntries
, NumberOfDataEntries
);
854 _SEH2_YIELD(return STATUS_INVALID_PARAMETER
);
859 LpcpMoveMessage(&Message
->Request
,
865 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
867 /* Cleanup and return the exception code */
868 DPRINT1("Got exception!\n");
869 LpcpFreeToPortZone(Message
, 0);
870 ObDereferenceObject(Port
);
871 _SEH2_YIELD(return _SEH2_GetExceptionCode());
875 /* Acquire the LPC lock */
876 KeAcquireGuardedMutex(&LpcpLock
);
878 /* Right now clear the port context */
879 Message
->PortContext
= NULL
;
881 /* Check if this is a not connection port */
882 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
884 /* We want the connected port */
885 QueuePort
= Port
->ConnectedPort
;
888 /* We have no connected port, fail */
889 DPRINT1("No connected port\n");
890 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
891 ObDereferenceObject(Port
);
892 return STATUS_PORT_DISCONNECTED
;
895 /* This will be the rundown port */
896 ReplyPort
= QueuePort
;
898 /* Check if this is a client port */
899 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) == LPCP_CLIENT_PORT
)
901 /* Copy the port context */
902 Message
->PortContext
= QueuePort
->PortContext
;
905 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_COMMUNICATION_PORT
)
907 /* Use the connection port for anything but communication ports */
908 ConnectionPort
= QueuePort
= Port
->ConnectionPort
;
912 DPRINT1("No connection port\n");
913 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
914 ObDereferenceObject(Port
);
915 return STATUS_PORT_DISCONNECTED
;
919 /* Reference the connection port if it exists */
920 if (ConnectionPort
) ObReferenceObject(ConnectionPort
);
924 /* Otherwise, for a connection port, use the same port object */
925 QueuePort
= ReplyPort
= Port
;
928 /* No reply thread */
929 Message
->RepliedToThread
= NULL
;
930 Message
->SenderPort
= Port
;
932 /* Generate the Message ID and set it */
933 Message
->Request
.MessageId
= LpcpNextMessageId
++;
934 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
935 Message
->Request
.CallbackId
= 0;
937 /* Set the message ID for our thread now */
938 Thread
->LpcReplyMessageId
= Message
->Request
.MessageId
;
939 Thread
->LpcReplyMessage
= NULL
;
941 /* Insert the message in our chain */
942 InsertTailList(&QueuePort
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
943 InsertTailList(&ReplyPort
->LpcReplyChainHead
, &Thread
->LpcReplyChain
);
944 LpcpSetPortToThread(Thread
, Port
);
946 /* Release the lock and get the semaphore we'll use later */
947 KeEnterCriticalRegion();
948 KeReleaseGuardedMutex(&LpcpLock
);
949 Semaphore
= QueuePort
->MsgQueue
.Semaphore
;
951 /* If this is a waitable port, wake it up */
952 if (QueuePort
->Flags
& LPCP_WAITABLE_PORT
)
955 KeSetEvent(&QueuePort
->WaitEvent
, IO_NO_INCREMENT
, FALSE
);
959 /* Now release the semaphore */
960 LpcpCompleteWait(Semaphore
);
961 KeLeaveCriticalRegion();
963 /* And let's wait for the reply */
964 LpcpReplyWait(&Thread
->LpcReplySemaphore
, PreviousMode
);
966 /* Acquire the LPC lock */
967 KeAcquireGuardedMutex(&LpcpLock
);
969 /* Get the LPC Message and clear our thread's reply data */
970 Message
= LpcpGetMessageFromThread(Thread
);
971 Thread
->LpcReplyMessage
= NULL
;
972 Thread
->LpcReplyMessageId
= 0;
974 /* Check if we have anything on the reply chain*/
975 if (!IsListEmpty(&Thread
->LpcReplyChain
))
977 /* Remove this thread and reinitialize the list */
978 RemoveEntryList(&Thread
->LpcReplyChain
);
979 InitializeListHead(&Thread
->LpcReplyChain
);
982 /* Release the lock */
983 KeReleaseGuardedMutex(&LpcpLock
);
985 /* Check if we got a reply */
986 if (Status
== STATUS_SUCCESS
)
988 /* Check if we have a valid message */
991 LPCTRACE(LPC_SEND_DEBUG
,
992 "Reply Messages: %p/%p\n",
994 (&Message
->Request
) + 1);
996 /* Move the message */
999 LpcpMoveMessage(LpcReply
,
1001 (&Message
->Request
) + 1,
1005 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1007 DPRINT1("Got exception!\n");
1008 Status
= _SEH2_GetExceptionCode();
1012 /* Check if this is an LPC request with data information */
1013 if ((LpcpGetMessageType(&Message
->Request
) == LPC_REQUEST
) &&
1014 (Message
->Request
.u2
.s2
.DataInfoOffset
))
1016 /* Save the data information */
1017 LpcpSaveDataInfoMessage(Port
, Message
, 0);
1021 /* Otherwise, just free it */
1022 LpcpFreeToPortZone(Message
, 0);
1027 /* We don't have a reply */
1028 Status
= STATUS_LPC_REPLY_LOST
;
1033 /* The wait failed, free the message */
1034 if (Message
) LpcpFreeToPortZone(Message
, 0);
1038 LPCTRACE(LPC_SEND_DEBUG
,
1039 "Port: %p. Status: %d\n",
1042 ObDereferenceObject(Port
);
1043 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);