Sync to trunk r65566.
[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 /* This flag is undocumented. Remove it before continuing */
748 LocalLpcRequest.u2.s2.Type &= ~0x4000;
749
750 /* Check if this is an LPC Request */
751 if (LpcpGetMessageType(&LocalLpcRequest) == LPC_REQUEST)
752 {
753 /* Then it's a callback */
754 Callback = TRUE;
755 }
756 else if (LpcpGetMessageType(&LocalLpcRequest))
757 {
758 /* This is a not kernel-mode message */
759 DPRINT1("Not a kernel-mode message!\n");
760 return STATUS_INVALID_PARAMETER;
761 }
762 else
763 {
764 /* This is a kernel-mode message without a callback */
765 LocalLpcRequest.u2.s2.Type |= LPC_REQUEST;
766 Callback = FALSE;
767 }
768
769 /* Get the message type */
770 MessageType = LocalLpcRequest.u2.s2.Type;
771
772 /* Due to the above probe, we know that TotalLength is positive */
773 NT_ASSERT(LocalLpcRequest.u1.s1.TotalLength >= 0);
774
775 /* Validate the length */
776 if ((((ULONG)(USHORT)LocalLpcRequest.u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
777 (ULONG)LocalLpcRequest.u1.s1.TotalLength))
778 {
779 /* Fail */
780 DPRINT1("Invalid message length: %u, %u\n",
781 LocalLpcRequest.u1.s1.DataLength,
782 LocalLpcRequest.u1.s1.TotalLength);
783 return STATUS_INVALID_PARAMETER;
784 }
785
786 /* Reference the object */
787 Status = ObReferenceObjectByHandle(PortHandle,
788 0,
789 LpcPortObjectType,
790 PreviousMode,
791 (PVOID*)&Port,
792 NULL);
793 if (!NT_SUCCESS(Status)) return Status;
794
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))
798 {
799 /* Fail */
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;
805 }
806
807 /* Allocate a message from the port zone */
808 Message = LpcpAllocateFromPortZone();
809 if (!Message)
810 {
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;
815 }
816
817 /* Check if this is a callback */
818 if (Callback)
819 {
820 /* FIXME: TODO */
821 Semaphore = NULL; // we'd use the Thread Semaphore here
822 ASSERT(FALSE);
823 }
824 else
825 {
826 /* No callback, just copy the message */
827 _SEH2_TRY
828 {
829 /* Check if we have data info entries */
830 if (LpcRequest->u2.s2.DataInfoOffset != 0)
831 {
832 /* Get the data info and check if the number of entries matches
833 what we expect */
834 DataInfo = LpcpGetDataInfoFromMessage(LpcRequest);
835 if (DataInfo->NumberOfEntries != NumberOfDataEntries)
836 {
837 LpcpFreeToPortZone(Message, 0);
838 ObDereferenceObject(Port);
839 DPRINT1("NumberOfEntries has changed: %u, %u\n",
840 DataInfo->NumberOfEntries, NumberOfDataEntries);
841 return STATUS_INVALID_PARAMETER;
842 }
843 }
844
845 /* Copy it */
846 LpcpMoveMessage(&Message->Request,
847 LpcRequest,
848 LpcRequest + 1,
849 MessageType,
850 &Thread->Cid);
851 }
852 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
853 {
854 /* Fail */
855 DPRINT1("Got exception!\n");
856 LpcpFreeToPortZone(Message, 0);
857 ObDereferenceObject(Port);
858 _SEH2_YIELD(return _SEH2_GetExceptionCode());
859 }
860 _SEH2_END;
861
862 /* Acquire the LPC lock */
863 KeAcquireGuardedMutex(&LpcpLock);
864
865 /* Right now clear the port context */
866 Message->PortContext = NULL;
867
868 /* Check if this is a not connection port */
869 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
870 {
871 /* We want the connected port */
872 QueuePort = Port->ConnectedPort;
873 if (!QueuePort)
874 {
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;
880 }
881
882 /* This will be the rundown port */
883 ReplyPort = QueuePort;
884
885 /* Check if this is a client port */
886 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
887 {
888 /* Copy the port context */
889 Message->PortContext = QueuePort->PortContext;
890 }
891
892 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
893 {
894 /* Use the connection port for anything but communication ports */
895 ConnectionPort = QueuePort = Port->ConnectionPort;
896 if (!ConnectionPort)
897 {
898 /* Fail */
899 DPRINT1("No connection port\n");
900 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
901 ObDereferenceObject(Port);
902 return STATUS_PORT_DISCONNECTED;
903 }
904 }
905
906 /* Reference the connection port if it exists */
907 if (ConnectionPort) ObReferenceObject(ConnectionPort);
908 }
909 else
910 {
911 /* Otherwise, for a connection port, use the same port object */
912 QueuePort = ReplyPort = Port;
913 }
914
915 /* No reply thread */
916 Message->RepliedToThread = NULL;
917 Message->SenderPort = Port;
918
919 /* Generate the Message ID and set it */
920 Message->Request.MessageId = LpcpNextMessageId++;
921 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
922 Message->Request.CallbackId = 0;
923
924 /* Set the message ID for our thread now */
925 Thread->LpcReplyMessageId = Message->Request.MessageId;
926 Thread->LpcReplyMessage = NULL;
927
928 /* Insert the message in our chain */
929 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
930 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
931 LpcpSetPortToThread(Thread, Port);
932
933 /* Release the lock and get the semaphore we'll use later */
934 KeEnterCriticalRegion();
935 KeReleaseGuardedMutex(&LpcpLock);
936 Semaphore = QueuePort->MsgQueue.Semaphore;
937
938 /* If this is a waitable port, wake it up */
939 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
940 {
941 /* Wake it */
942 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
943 }
944 }
945
946 /* Now release the semaphore */
947 LpcpCompleteWait(Semaphore);
948 KeLeaveCriticalRegion();
949
950 /* And let's wait for the reply */
951 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
952
953 /* Acquire the LPC lock */
954 KeAcquireGuardedMutex(&LpcpLock);
955
956 /* Get the LPC Message and clear our thread's reply data */
957 Message = LpcpGetMessageFromThread(Thread);
958 Thread->LpcReplyMessage = NULL;
959 Thread->LpcReplyMessageId = 0;
960
961 /* Check if we have anything on the reply chain*/
962 if (!IsListEmpty(&Thread->LpcReplyChain))
963 {
964 /* Remove this thread and reinitialize the list */
965 RemoveEntryList(&Thread->LpcReplyChain);
966 InitializeListHead(&Thread->LpcReplyChain);
967 }
968
969 /* Release the lock */
970 KeReleaseGuardedMutex(&LpcpLock);
971
972 /* Check if we got a reply */
973 if (Status == STATUS_SUCCESS)
974 {
975 /* Check if we have a valid message */
976 if (Message)
977 {
978 LPCTRACE(LPC_SEND_DEBUG,
979 "Reply Messages: %p/%p\n",
980 &Message->Request,
981 (&Message->Request) + 1);
982
983 /* Move the message */
984 _SEH2_TRY
985 {
986 LpcpMoveMessage(LpcReply,
987 &Message->Request,
988 (&Message->Request) + 1,
989 0,
990 NULL);
991 }
992 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
993 {
994 DPRINT1("Got exception!\n");
995 Status = _SEH2_GetExceptionCode();
996 }
997 _SEH2_END;
998
999 /* Check if this is an LPC request with data information */
1000 if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) &&
1001 (Message->Request.u2.s2.DataInfoOffset))
1002 {
1003 /* Save the data information */
1004 LpcpSaveDataInfoMessage(Port, Message, 0);
1005 }
1006 else
1007 {
1008 /* Otherwise, just free it */
1009 LpcpFreeToPortZone(Message, 0);
1010 }
1011 }
1012 else
1013 {
1014 /* We don't have a reply */
1015 Status = STATUS_LPC_REPLY_LOST;
1016 }
1017 }
1018 else
1019 {
1020 /* The wait failed, free the message */
1021 if (Message) LpcpFreeToPortZone(Message, 0);
1022 }
1023
1024 /* All done */
1025 LPCTRACE(LPC_SEND_DEBUG,
1026 "Port: %p. Status: %d\n",
1027 Port,
1028 Status);
1029 ObDereferenceObject(Port);
1030 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
1031 return Status;
1032 }
1033
1034 /* EOF */