* Sync with trunk r64401.
[reactos.git] / ntoskrnl / lpc / send.c
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* PUBLIC FUNCTIONS **********************************************************/
16
17 /*
18 * @implemented
19 */
20 NTSTATUS
21 NTAPI
22 LpcRequestPort(IN PVOID PortObject,
23 IN PPORT_MESSAGE LpcMessage)
24 {
25 PLPCP_PORT_OBJECT Port = PortObject, QueuePort, ConnectionPort = NULL;
26 ULONG MessageType;
27 PLPCP_MESSAGE Message;
28 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
29 PETHREAD Thread = PsGetCurrentThread();
30
31 PAGED_CODE();
32
33 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", Port, LpcMessage);
34
35 /* Check if this is a non-datagram message */
36 if (LpcMessage->u2.s2.Type)
37 {
38 /* Get the message type */
39 MessageType = LpcpGetMessageType(LpcMessage);
40
41 /* Validate it */
42 if ((MessageType < LPC_DATAGRAM) || (MessageType > LPC_CLIENT_DIED))
43 {
44 /* Fail */
45 return STATUS_INVALID_PARAMETER;
46 }
47
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))
51 {
52 /* We did, this is a kernel mode message */
53 MessageType |= LPC_KERNELMODE_MESSAGE;
54 }
55 }
56 else
57 {
58 /* This is a datagram */
59 MessageType = LPC_DATAGRAM;
60 }
61
62 /* Can't have data information on this type of call */
63 if (LpcMessage->u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER;
64
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))
68 {
69 /* Fail */
70 return STATUS_PORT_MESSAGE_TOO_LONG;
71 }
72
73 /* Allocate a new message */
74 Message = LpcpAllocateFromPortZone();
75 if (!Message) return STATUS_NO_MEMORY;
76
77 /* Clear the context */
78 Message->RepliedToThread = NULL;
79 Message->PortContext = NULL;
80
81 /* Copy the message */
82 LpcpMoveMessage(&Message->Request,
83 LpcMessage,
84 LpcMessage + 1,
85 MessageType,
86 &Thread->Cid);
87
88 /* Acquire the LPC lock */
89 KeAcquireGuardedMutex(&LpcpLock);
90
91 /* Check if this is anything but a connection port */
92 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
93 {
94 /* The queue port is the connected port */
95 QueuePort = Port->ConnectedPort;
96 if (QueuePort)
97 {
98 /* Check if this is a client port */
99 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
100 {
101 /* Then copy the context */
102 Message->PortContext = QueuePort->PortContext;
103 ConnectionPort = QueuePort = Port->ConnectionPort;
104 if (!ConnectionPort)
105 {
106 /* Fail */
107 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
108 return STATUS_PORT_DISCONNECTED;
109 }
110 }
111 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
112 {
113 /* Any other kind of port, use the connection port */
114 ConnectionPort = QueuePort = Port->ConnectionPort;
115 if (!ConnectionPort)
116 {
117 /* Fail */
118 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
119 return STATUS_PORT_DISCONNECTED;
120 }
121 }
122
123 /* If we have a connection port, reference it */
124 if (ConnectionPort) ObReferenceObject(ConnectionPort);
125 }
126 }
127 else
128 {
129 /* For connection ports, use the port itself */
130 QueuePort = PortObject;
131 }
132
133 /* Make sure we have a port */
134 if (QueuePort)
135 {
136 /* Generate the Message ID and set it */
137 Message->Request.MessageId = LpcpNextMessageId++;
138 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
139 Message->Request.CallbackId = 0;
140
141 /* No Message ID for the thread */
142 Thread->LpcReplyMessageId = 0;
143
144 /* Insert the message in our chain */
145 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
146
147 /* Release the lock and the semaphore */
148 KeEnterCriticalRegion();
149 KeReleaseGuardedMutex(&LpcpLock);
150 LpcpCompleteWait(QueuePort->MsgQueue.Semaphore);
151
152 /* If this is a waitable port, wake it up */
153 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
154 {
155 /* Wake it */
156 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
157 }
158
159 KeLeaveCriticalRegion();
160
161 /* We're done */
162 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
163 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message);
164 return STATUS_SUCCESS;
165 }
166
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;
171 }
172
173 /*
174 * @implemented
175 */
176 NTSTATUS
177 NTAPI
178 LpcRequestWaitReplyPort(IN PVOID PortObject,
179 IN PPORT_MESSAGE LpcRequest,
180 OUT PPORT_MESSAGE LpcReply)
181 {
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;
189 USHORT MessageType;
190 PAGED_CODE();
191
192 Port = (PLPCP_PORT_OBJECT)PortObject;
193
194 LPCTRACE(LPC_SEND_DEBUG,
195 "Port: %p. Messages: %p/%p. Type: %lx\n",
196 Port,
197 LpcRequest,
198 LpcReply,
199 LpcpGetMessageType(LpcRequest));
200
201 /* Check if the thread is dying */
202 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
203
204 /* Check if this is an LPC Request */
205 MessageType = LpcpGetMessageType(LpcRequest);
206 switch (MessageType)
207 {
208 /* No type */
209 case 0:
210
211 /* Assume LPC request */
212 MessageType = LPC_REQUEST;
213 break;
214
215 /* LPC request callback */
216 case LPC_REQUEST:
217
218 /* This is a callback */
219 Callback = TRUE;
220 break;
221
222 /* Anything else */
223 case LPC_CLIENT_DIED:
224 case LPC_PORT_CLOSED:
225 case LPC_EXCEPTION:
226 case LPC_DEBUG_EVENT:
227 case LPC_ERROR_EVENT:
228
229 /* Nothing to do */
230 break;
231
232 default:
233
234 /* Invalid message type */
235 return STATUS_INVALID_PARAMETER;
236 }
237
238 /* Set the request type */
239 LpcRequest->u2.s2.Type = MessageType;
240
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))
244 {
245 /* Fail */
246 return STATUS_PORT_MESSAGE_TOO_LONG;
247 }
248
249 /* Allocate a message from the port zone */
250 Message = LpcpAllocateFromPortZone();
251 if (!Message)
252 {
253 /* Fail if we couldn't allocate a message */
254 return STATUS_NO_MEMORY;
255 }
256
257 /* Check if this is a callback */
258 if (Callback)
259 {
260 /* FIXME: TODO */
261 Semaphore = NULL; // we'd use the Thread Semaphore here
262 ASSERT(FALSE);
263 return STATUS_NOT_IMPLEMENTED;
264 }
265 else
266 {
267 /* No callback, just copy the message */
268 LpcpMoveMessage(&Message->Request,
269 LpcRequest,
270 LpcRequest + 1,
271 0,
272 &Thread->Cid);
273
274 /* Acquire the LPC lock */
275 KeAcquireGuardedMutex(&LpcpLock);
276
277 /* Right now clear the port context */
278 Message->PortContext = NULL;
279
280 /* Check if this is a not connection port */
281 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
282 {
283 /* We want the connected port */
284 QueuePort = Port->ConnectedPort;
285 if (!QueuePort)
286 {
287 /* We have no connected port, fail */
288 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
289 return STATUS_PORT_DISCONNECTED;
290 }
291
292 /* This will be the rundown port */
293 ReplyPort = QueuePort;
294
295 /* Check if this is a communication port */
296 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
297 {
298 /* Copy the port context and use the connection port */
299 Message->PortContext = QueuePort->PortContext;
300 ConnectionPort = QueuePort = Port->ConnectionPort;
301 if (!ConnectionPort)
302 {
303 /* Fail */
304 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
305 return STATUS_PORT_DISCONNECTED;
306 }
307 }
308 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
309 LPCP_COMMUNICATION_PORT)
310 {
311 /* Use the connection port for anything but communication ports */
312 ConnectionPort = QueuePort = Port->ConnectionPort;
313 if (!ConnectionPort)
314 {
315 /* Fail */
316 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
317 return STATUS_PORT_DISCONNECTED;
318 }
319 }
320
321 /* Reference the connection port if it exists */
322 if (ConnectionPort) ObReferenceObject(ConnectionPort);
323 }
324 else
325 {
326 /* Otherwise, for a connection port, use the same port object */
327 QueuePort = ReplyPort = Port;
328 }
329
330 /* No reply thread */
331 Message->RepliedToThread = NULL;
332 Message->SenderPort = Port;
333
334 /* Generate the Message ID and set it */
335 Message->Request.MessageId = LpcpNextMessageId++;
336 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
337 Message->Request.CallbackId = 0;
338
339 /* Set the message ID for our thread now */
340 Thread->LpcReplyMessageId = Message->Request.MessageId;
341 Thread->LpcReplyMessage = NULL;
342
343 /* Insert the message in our chain */
344 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
345 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
346 LpcpSetPortToThread(Thread, Port);
347
348 /* Release the lock and get the semaphore we'll use later */
349 KeEnterCriticalRegion();
350 KeReleaseGuardedMutex(&LpcpLock);
351 Semaphore = QueuePort->MsgQueue.Semaphore;
352
353 /* If this is a waitable port, wake it up */
354 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
355 {
356 /* Wake it */
357 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
358 }
359 }
360
361 /* Now release the semaphore */
362 LpcpCompleteWait(Semaphore);
363 KeLeaveCriticalRegion();
364
365 /* And let's wait for the reply */
366 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
367
368 /* Acquire the LPC lock */
369 KeAcquireGuardedMutex(&LpcpLock);
370
371 /* Get the LPC Message and clear our thread's reply data */
372 Message = LpcpGetMessageFromThread(Thread);
373 Thread->LpcReplyMessage = NULL;
374 Thread->LpcReplyMessageId = 0;
375
376 /* Check if we have anything on the reply chain*/
377 if (!IsListEmpty(&Thread->LpcReplyChain))
378 {
379 /* Remove this thread and reinitialize the list */
380 RemoveEntryList(&Thread->LpcReplyChain);
381 InitializeListHead(&Thread->LpcReplyChain);
382 }
383
384 /* Release the lock */
385 KeReleaseGuardedMutex(&LpcpLock);
386
387 /* Check if we got a reply */
388 if (Status == STATUS_SUCCESS)
389 {
390 /* Check if we have a valid message */
391 if (Message)
392 {
393 LPCTRACE(LPC_SEND_DEBUG,
394 "Reply Messages: %p/%p\n",
395 &Message->Request,
396 (&Message->Request) + 1);
397
398 /* Move the message */
399 LpcpMoveMessage(LpcReply,
400 &Message->Request,
401 (&Message->Request) + 1,
402 0,
403 NULL);
404
405 /* Acquire the lock */
406 KeAcquireGuardedMutex(&LpcpLock);
407
408 /* Check if we replied to a thread */
409 if (Message->RepliedToThread)
410 {
411 /* Dereference */
412 ObDereferenceObject(Message->RepliedToThread);
413 Message->RepliedToThread = NULL;
414 }
415
416 /* Free the message */
417 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
418 }
419 else
420 {
421 /* We don't have a reply */
422 Status = STATUS_LPC_REPLY_LOST;
423 }
424 }
425 else
426 {
427 /* The wait failed, free the message */
428 if (Message) LpcpFreeToPortZone(Message, 0);
429 }
430
431 /* All done */
432 LPCTRACE(LPC_SEND_DEBUG,
433 "Port: %p. Status: %d\n",
434 Port,
435 Status);
436
437 /* Dereference the connection port */
438 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
439 return Status;
440 }
441
442 /*
443 * @implemented
444 */
445 NTSTATUS
446 NTAPI
447 NtRequestPort(IN HANDLE PortHandle,
448 IN PPORT_MESSAGE LpcRequest)
449 {
450 NTSTATUS Status;
451 PLPCP_PORT_OBJECT Port, QueuePort, ConnectionPort = NULL;
452 ULONG MessageType;
453 PLPCP_MESSAGE Message;
454 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
455 PETHREAD Thread = PsGetCurrentThread();
456
457 PAGED_CODE();
458
459 LPCTRACE(LPC_SEND_DEBUG,
460 "Handle: %p. Message: %p. Type: %lx\n",
461 PortHandle,
462 LpcRequest,
463 LpcpGetMessageType(LpcRequest));
464
465 /* Get the message type */
466 MessageType = LpcRequest->u2.s2.Type | LPC_DATAGRAM;
467
468 /* Can't have data information on this type of call */
469 if (LpcRequest->u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER;
470
471 /* Validate the length */
472 if (((ULONG)LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
473 (ULONG)LpcRequest->u1.s1.TotalLength)
474 {
475 /* Fail */
476 return STATUS_INVALID_PARAMETER;
477 }
478
479 /* Reference the object */
480 Status = ObReferenceObjectByHandle(PortHandle,
481 0,
482 LpcPortObjectType,
483 PreviousMode,
484 (PVOID*)&Port,
485 NULL);
486 if (!NT_SUCCESS(Status)) return Status;
487
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))
491 {
492 /* Fail */
493 ObDereferenceObject(Port);
494 return STATUS_PORT_MESSAGE_TOO_LONG;
495 }
496
497 /* Allocate a message from the port zone */
498 Message = LpcpAllocateFromPortZone();
499 if (!Message)
500 {
501 /* Fail if we couldn't allocate a message */
502 ObDereferenceObject(Port);
503 return STATUS_NO_MEMORY;
504 }
505
506 /* No callback, just copy the message */
507 _SEH2_TRY
508 {
509 /* Copy it */
510 LpcpMoveMessage(&Message->Request,
511 LpcRequest,
512 LpcRequest + 1,
513 MessageType,
514 &Thread->Cid);
515 }
516 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
517 {
518 /* Fail */
519 LpcpFreeToPortZone(Message, 0);
520 ObDereferenceObject(Port);
521 _SEH2_YIELD(return _SEH2_GetExceptionCode());
522 }
523 _SEH2_END;
524
525 /* Acquire the LPC lock */
526 KeAcquireGuardedMutex(&LpcpLock);
527
528 /* Right now clear the port context */
529 Message->PortContext = NULL;
530
531 /* Check if this is a not connection port */
532 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
533 {
534 /* We want the connected port */
535 QueuePort = Port->ConnectedPort;
536 if (!QueuePort)
537 {
538 /* We have no connected port, fail */
539 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
540 ObDereferenceObject(Port);
541 return STATUS_PORT_DISCONNECTED;
542 }
543
544 /* Check if this is a communication port */
545 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
546 {
547 /* Copy the port context and use the connection port */
548 Message->PortContext = QueuePort->PortContext;
549 ConnectionPort = QueuePort = Port->ConnectionPort;
550 if (!ConnectionPort)
551 {
552 /* Fail */
553 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
554 ObDereferenceObject(Port);
555 return STATUS_PORT_DISCONNECTED;
556 }
557 }
558 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
559 LPCP_COMMUNICATION_PORT)
560 {
561 /* Use the connection port for anything but communication ports */
562 ConnectionPort = QueuePort = Port->ConnectionPort;
563 if (!ConnectionPort)
564 {
565 /* Fail */
566 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
567 ObDereferenceObject(Port);
568 return STATUS_PORT_DISCONNECTED;
569 }
570 }
571
572 /* Reference the connection port if it exists */
573 if (ConnectionPort) ObReferenceObject(ConnectionPort);
574 }
575 else
576 {
577 /* Otherwise, for a connection port, use the same port object */
578 QueuePort = Port;
579 }
580
581 /* Reference QueuePort if we have it */
582 if (QueuePort && ObReferenceObjectSafe(QueuePort))
583 {
584 /* Set sender's port */
585 Message->SenderPort = Port;
586
587 /* Generate the Message ID and set it */
588 Message->Request.MessageId = LpcpNextMessageId++;
589 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
590 Message->Request.CallbackId = 0;
591
592 /* No Message ID for the thread */
593 Thread->LpcReplyMessageId = 0;
594
595 /* Insert the message in our chain */
596 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
597
598 /* Release the lock and the semaphore */
599 KeEnterCriticalRegion();
600 KeReleaseGuardedMutex(&LpcpLock);
601 LpcpCompleteWait(QueuePort->MsgQueue.Semaphore);
602
603 /* If this is a waitable port, wake it up */
604 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
605 {
606 /* Wake it */
607 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
608 }
609
610 KeLeaveCriticalRegion();
611
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;
618 }
619
620 Status = STATUS_PORT_DISCONNECTED;
621
622 /* All done with a failure*/
623 LPCTRACE(LPC_SEND_DEBUG,
624 "Port: %p. Status: %d\n",
625 Port,
626 Status);
627
628 /* The wait failed, free the message */
629 if (Message) LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
630
631 ObDereferenceObject(Port);
632 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
633 return Status;
634 }
635
636 NTSTATUS
637 NTAPI
638 LpcpVerifyMessageDataInfo(
639 _In_ PPORT_MESSAGE Message,
640 _Out_ PULONG NumberOfDataEntries)
641 {
642 PLPCP_DATA_INFO DataInfo;
643 PUCHAR EndOfEntries;
644
645 /* Check if we have no data info at all */
646 if (Message->u2.s2.DataInfoOffset == 0)
647 {
648 *NumberOfDataEntries = 0;
649 return STATUS_SUCCESS;
650 }
651
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))))
658 {
659 return STATUS_INVALID_PARAMETER;
660 }
661
662 /* Get a pointer to the data info */
663 DataInfo = LpcpGetDataInfoFromMessage(Message);
664
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))
669 {
670 return STATUS_INVALID_PARAMETER;
671 }
672
673 *NumberOfDataEntries = DataInfo->NumberOfEntries;
674 return STATUS_SUCCESS;
675 }
676
677 /*
678 * @implemented
679 */
680 NTSTATUS
681 NTAPI
682 NtRequestWaitReplyPort(IN HANDLE PortHandle,
683 IN PPORT_MESSAGE LpcRequest,
684 IN OUT PPORT_MESSAGE LpcReply)
685 {
686 PORT_MESSAGE LocalLpcRequest;
687 ULONG NumberOfDataEntries;
688 PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL;
689 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
690 NTSTATUS Status;
691 PLPCP_MESSAGE Message;
692 PETHREAD Thread = PsGetCurrentThread();
693 BOOLEAN Callback;
694 PKSEMAPHORE Semaphore;
695 ULONG MessageType;
696 PLPCP_DATA_INFO DataInfo;
697 PAGED_CODE();
698 LPCTRACE(LPC_SEND_DEBUG,
699 "Handle: %p. Messages: %p/%p. Type: %lx\n",
700 PortHandle,
701 LpcRequest,
702 LpcReply,
703 LpcpGetMessageType(LpcRequest));
704
705 /* Check if the thread is dying */
706 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
707
708 /* Check for user mode access */
709 if (PreviousMode != KernelMode)
710 {
711 _SEH2_TRY
712 {
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;
717
718 /* Probe the reply message for write */
719 ProbeForWrite(LpcReply, sizeof(*LpcReply), sizeof(ULONG));
720
721 /* Make sure the data entries in the request message are valid */
722 Status = LpcpVerifyMessageDataInfo(LpcRequest, &NumberOfDataEntries);
723 if (!NT_SUCCESS(Status))
724 {
725 DPRINT1("LpcpVerifyMessageDataInfo failed\n");
726 return Status;
727 }
728 }
729 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
730 {
731 DPRINT1("Got exception\n");
732 return _SEH2_GetExceptionCode();
733 }
734 _SEH2_END;
735 }
736 else
737 {
738 LocalLpcRequest = *LpcRequest;
739 Status = LpcpVerifyMessageDataInfo(LpcRequest, &NumberOfDataEntries);
740 if (!NT_SUCCESS(Status))
741 {
742 DPRINT1("LpcpVerifyMessageDataInfo failed\n");
743 return Status;
744 }
745 }
746
747 /* Check if this is an LPC Request */
748 if (LpcpGetMessageType(&LocalLpcRequest) == LPC_REQUEST)
749 {
750 /* Then it's a callback */
751 Callback = TRUE;
752 }
753 else if (LpcpGetMessageType(&LocalLpcRequest))
754 {
755 /* This is a not kernel-mode message */
756 DPRINT1("Not a kernel-mode message!\n");
757 return STATUS_INVALID_PARAMETER;
758 }
759 else
760 {
761 /* This is a kernel-mode message without a callback */
762 LocalLpcRequest.u2.s2.Type |= LPC_REQUEST;
763 Callback = FALSE;
764 }
765
766 /* Get the message type */
767 MessageType = LocalLpcRequest.u2.s2.Type;
768
769 /* Due to the above probe, we know that TotalLength is positive */
770 NT_ASSERT(LocalLpcRequest.u1.s1.TotalLength >= 0);
771
772 /* Validate the length */
773 if ((((ULONG)(USHORT)LocalLpcRequest.u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
774 (ULONG)LocalLpcRequest.u1.s1.TotalLength))
775 {
776 /* Fail */
777 DPRINT1("Invalid message length: %u, %u\n",
778 LocalLpcRequest.u1.s1.DataLength,
779 LocalLpcRequest.u1.s1.TotalLength);
780 return STATUS_INVALID_PARAMETER;
781 }
782
783 /* Reference the object */
784 Status = ObReferenceObjectByHandle(PortHandle,
785 0,
786 LpcPortObjectType,
787 PreviousMode,
788 (PVOID*)&Port,
789 NULL);
790 if (!NT_SUCCESS(Status)) return Status;
791
792 /* Validate the message length */
793 if (((ULONG)LocalLpcRequest.u1.s1.TotalLength > Port->MaxMessageLength) ||
794 ((ULONG)LocalLpcRequest.u1.s1.TotalLength <= (ULONG)LocalLpcRequest.u1.s1.DataLength))
795 {
796 /* Fail */
797 DPRINT1("Invalid message length: %u, %u\n",
798 LocalLpcRequest.u1.s1.DataLength,
799 LocalLpcRequest.u1.s1.TotalLength);
800 ObDereferenceObject(Port);
801 return STATUS_PORT_MESSAGE_TOO_LONG;
802 }
803
804 /* Allocate a message from the port zone */
805 Message = LpcpAllocateFromPortZone();
806 if (!Message)
807 {
808 /* Fail if we couldn't allocate a message */
809 DPRINT1("Failed to allocate a message!\n");
810 ObDereferenceObject(Port);
811 return STATUS_NO_MEMORY;
812 }
813
814 /* Check if this is a callback */
815 if (Callback)
816 {
817 /* FIXME: TODO */
818 Semaphore = NULL; // we'd use the Thread Semaphore here
819 ASSERT(FALSE);
820 }
821 else
822 {
823 /* No callback, just copy the message */
824 _SEH2_TRY
825 {
826 /* Check if we have data info entries */
827 if (LpcRequest->u2.s2.DataInfoOffset != 0)
828 {
829 /* Get the data info and check if the number of entries matches
830 what we expect */
831 DataInfo = LpcpGetDataInfoFromMessage(LpcRequest);
832 if (DataInfo->NumberOfEntries != NumberOfDataEntries)
833 {
834 LpcpFreeToPortZone(Message, 0);
835 ObDereferenceObject(Port);
836 DPRINT1("NumberOfEntries has changed: %u, %u\n",
837 DataInfo->NumberOfEntries, NumberOfDataEntries);
838 return STATUS_INVALID_PARAMETER;
839 }
840 }
841
842 /* Copy it */
843 LpcpMoveMessage(&Message->Request,
844 LpcRequest,
845 LpcRequest + 1,
846 MessageType,
847 &Thread->Cid);
848 }
849 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
850 {
851 /* Fail */
852 DPRINT1("Got exception!\n");
853 LpcpFreeToPortZone(Message, 0);
854 ObDereferenceObject(Port);
855 _SEH2_YIELD(return _SEH2_GetExceptionCode());
856 }
857 _SEH2_END;
858
859 /* Acquire the LPC lock */
860 KeAcquireGuardedMutex(&LpcpLock);
861
862 /* Right now clear the port context */
863 Message->PortContext = NULL;
864
865 /* Check if this is a not connection port */
866 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
867 {
868 /* We want the connected port */
869 QueuePort = Port->ConnectedPort;
870 if (!QueuePort)
871 {
872 /* We have no connected port, fail */
873 DPRINT1("No connected port\n");
874 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
875 ObDereferenceObject(Port);
876 return STATUS_PORT_DISCONNECTED;
877 }
878
879 /* This will be the rundown port */
880 ReplyPort = QueuePort;
881
882 /* Check if this is a client port */
883 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
884 {
885 /* Copy the port context */
886 Message->PortContext = QueuePort->PortContext;
887 }
888
889 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
890 {
891 /* Use the connection port for anything but communication ports */
892 ConnectionPort = QueuePort = Port->ConnectionPort;
893 if (!ConnectionPort)
894 {
895 /* Fail */
896 DPRINT1("No connection port\n");
897 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
898 ObDereferenceObject(Port);
899 return STATUS_PORT_DISCONNECTED;
900 }
901 }
902
903 /* Reference the connection port if it exists */
904 if (ConnectionPort) ObReferenceObject(ConnectionPort);
905 }
906 else
907 {
908 /* Otherwise, for a connection port, use the same port object */
909 QueuePort = ReplyPort = Port;
910 }
911
912 /* No reply thread */
913 Message->RepliedToThread = NULL;
914 Message->SenderPort = Port;
915
916 /* Generate the Message ID and set it */
917 Message->Request.MessageId = LpcpNextMessageId++;
918 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
919 Message->Request.CallbackId = 0;
920
921 /* Set the message ID for our thread now */
922 Thread->LpcReplyMessageId = Message->Request.MessageId;
923 Thread->LpcReplyMessage = NULL;
924
925 /* Insert the message in our chain */
926 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
927 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
928 LpcpSetPortToThread(Thread, Port);
929
930 /* Release the lock and get the semaphore we'll use later */
931 KeEnterCriticalRegion();
932 KeReleaseGuardedMutex(&LpcpLock);
933 Semaphore = QueuePort->MsgQueue.Semaphore;
934
935 /* If this is a waitable port, wake it up */
936 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
937 {
938 /* Wake it */
939 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
940 }
941 }
942
943 /* Now release the semaphore */
944 LpcpCompleteWait(Semaphore);
945 KeLeaveCriticalRegion();
946
947 /* And let's wait for the reply */
948 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
949
950 /* Acquire the LPC lock */
951 KeAcquireGuardedMutex(&LpcpLock);
952
953 /* Get the LPC Message and clear our thread's reply data */
954 Message = LpcpGetMessageFromThread(Thread);
955 Thread->LpcReplyMessage = NULL;
956 Thread->LpcReplyMessageId = 0;
957
958 /* Check if we have anything on the reply chain*/
959 if (!IsListEmpty(&Thread->LpcReplyChain))
960 {
961 /* Remove this thread and reinitialize the list */
962 RemoveEntryList(&Thread->LpcReplyChain);
963 InitializeListHead(&Thread->LpcReplyChain);
964 }
965
966 /* Release the lock */
967 KeReleaseGuardedMutex(&LpcpLock);
968
969 /* Check if we got a reply */
970 if (Status == STATUS_SUCCESS)
971 {
972 /* Check if we have a valid message */
973 if (Message)
974 {
975 LPCTRACE(LPC_SEND_DEBUG,
976 "Reply Messages: %p/%p\n",
977 &Message->Request,
978 (&Message->Request) + 1);
979
980 /* Move the message */
981 _SEH2_TRY
982 {
983 LpcpMoveMessage(LpcReply,
984 &Message->Request,
985 (&Message->Request) + 1,
986 0,
987 NULL);
988 }
989 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
990 {
991 DPRINT1("Got exception!\n");
992 Status = _SEH2_GetExceptionCode();
993 }
994 _SEH2_END;
995
996 /* Check if this is an LPC request with data information */
997 if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) &&
998 (Message->Request.u2.s2.DataInfoOffset))
999 {
1000 /* Save the data information */
1001 LpcpSaveDataInfoMessage(Port, Message, 0);
1002 }
1003 else
1004 {
1005 /* Otherwise, just free it */
1006 LpcpFreeToPortZone(Message, 0);
1007 }
1008 }
1009 else
1010 {
1011 /* We don't have a reply */
1012 Status = STATUS_LPC_REPLY_LOST;
1013 }
1014 }
1015 else
1016 {
1017 /* The wait failed, free the message */
1018 if (Message) LpcpFreeToPortZone(Message, 0);
1019 }
1020
1021 /* All done */
1022 LPCTRACE(LPC_SEND_DEBUG,
1023 "Port: %p. Status: %d\n",
1024 Port,
1025 Status);
1026 ObDereferenceObject(Port);
1027 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
1028 return Status;
1029 }
1030
1031 /* EOF */