Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[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 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;
187 USHORT MessageType;
188 PLPCP_MESSAGE Message;
189 BOOLEAN Callback = FALSE;
190 PKSEMAPHORE Semaphore;
191
192 PAGED_CODE();
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, assume LPC request */
209 case 0:
210 MessageType = LPC_REQUEST;
211 break;
212
213 /* LPC request callback */
214 case LPC_REQUEST:
215 Callback = TRUE;
216 break;
217
218 /* Anything else, nothing to do */
219 case LPC_CLIENT_DIED:
220 case LPC_PORT_CLOSED:
221 case LPC_EXCEPTION:
222 case LPC_DEBUG_EVENT:
223 case LPC_ERROR_EVENT:
224 break;
225
226 /* Invalid message type */
227 default:
228 return STATUS_INVALID_PARAMETER;
229 }
230
231 /* Set the request type */
232 LpcRequest->u2.s2.Type = MessageType;
233
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))
237 {
238 /* Fail */
239 return STATUS_PORT_MESSAGE_TOO_LONG;
240 }
241
242 /* Allocate a message from the port zone */
243 Message = LpcpAllocateFromPortZone();
244 if (!Message)
245 {
246 /* Fail if we couldn't allocate a message */
247 return STATUS_NO_MEMORY;
248 }
249
250 /* Check if this is a callback */
251 if (Callback)
252 {
253 /* FIXME: TODO */
254 Semaphore = NULL; // we'd use the Thread Semaphore here
255 ASSERT(FALSE);
256 return STATUS_NOT_IMPLEMENTED;
257 }
258 else
259 {
260 /* No callback, just copy the message */
261 LpcpMoveMessage(&Message->Request,
262 LpcRequest,
263 LpcRequest + 1,
264 0,
265 &Thread->Cid);
266
267 /* Acquire the LPC lock */
268 KeAcquireGuardedMutex(&LpcpLock);
269
270 /* Right now clear the port context */
271 Message->PortContext = NULL;
272
273 /* Check if this is a not connection port */
274 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
275 {
276 /* We want the connected port */
277 QueuePort = Port->ConnectedPort;
278 if (!QueuePort)
279 {
280 /* We have no connected port, fail */
281 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
282 return STATUS_PORT_DISCONNECTED;
283 }
284
285 /* This will be the rundown port */
286 ReplyPort = QueuePort;
287
288 /* Check if this is a communication port */
289 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
290 {
291 /* Copy the port context and use the connection port */
292 Message->PortContext = QueuePort->PortContext;
293 ConnectionPort = QueuePort = Port->ConnectionPort;
294 if (!ConnectionPort)
295 {
296 /* Fail */
297 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
298 return STATUS_PORT_DISCONNECTED;
299 }
300 }
301 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
302 LPCP_COMMUNICATION_PORT)
303 {
304 /* Use the connection port for anything but communication ports */
305 ConnectionPort = QueuePort = Port->ConnectionPort;
306 if (!ConnectionPort)
307 {
308 /* Fail */
309 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
310 return STATUS_PORT_DISCONNECTED;
311 }
312 }
313
314 /* Reference the connection port if it exists */
315 if (ConnectionPort) ObReferenceObject(ConnectionPort);
316 }
317 else
318 {
319 /* Otherwise, for a connection port, use the same port object */
320 QueuePort = ReplyPort = Port;
321 }
322
323 /* No reply thread */
324 Message->RepliedToThread = NULL;
325 Message->SenderPort = Port;
326
327 /* Generate the Message ID and set it */
328 Message->Request.MessageId = LpcpNextMessageId++;
329 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
330 Message->Request.CallbackId = 0;
331
332 /* Set the message ID for our thread now */
333 Thread->LpcReplyMessageId = Message->Request.MessageId;
334 Thread->LpcReplyMessage = NULL;
335
336 /* Insert the message in our chain */
337 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
338 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
339 LpcpSetPortToThread(Thread, Port);
340
341 /* Release the lock and get the semaphore we'll use later */
342 KeEnterCriticalRegion();
343 KeReleaseGuardedMutex(&LpcpLock);
344 Semaphore = QueuePort->MsgQueue.Semaphore;
345
346 /* If this is a waitable port, wake it up */
347 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
348 {
349 /* Wake it */
350 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
351 }
352 }
353
354 /* Now release the semaphore */
355 LpcpCompleteWait(Semaphore);
356 KeLeaveCriticalRegion();
357
358 /* And let's wait for the reply */
359 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
360
361 /* Acquire the LPC lock */
362 KeAcquireGuardedMutex(&LpcpLock);
363
364 /* Get the LPC Message and clear our thread's reply data */
365 Message = LpcpGetMessageFromThread(Thread);
366 Thread->LpcReplyMessage = NULL;
367 Thread->LpcReplyMessageId = 0;
368
369 /* Check if we have anything on the reply chain*/
370 if (!IsListEmpty(&Thread->LpcReplyChain))
371 {
372 /* Remove this thread and reinitialize the list */
373 RemoveEntryList(&Thread->LpcReplyChain);
374 InitializeListHead(&Thread->LpcReplyChain);
375 }
376
377 /* Release the lock */
378 KeReleaseGuardedMutex(&LpcpLock);
379
380 /* Check if we got a reply */
381 if (Status == STATUS_SUCCESS)
382 {
383 /* Check if we have a valid message */
384 if (Message)
385 {
386 LPCTRACE(LPC_SEND_DEBUG,
387 "Reply Messages: %p/%p\n",
388 &Message->Request,
389 (&Message->Request) + 1);
390
391 /* Move the message */
392 LpcpMoveMessage(LpcReply,
393 &Message->Request,
394 (&Message->Request) + 1,
395 0,
396 NULL);
397
398 /* Acquire the lock */
399 KeAcquireGuardedMutex(&LpcpLock);
400
401 /* Check if we replied to a thread */
402 if (Message->RepliedToThread)
403 {
404 /* Dereference */
405 ObDereferenceObject(Message->RepliedToThread);
406 Message->RepliedToThread = NULL;
407 }
408
409 /* Free the message */
410 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
411 }
412 else
413 {
414 /* We don't have a reply */
415 Status = STATUS_LPC_REPLY_LOST;
416 }
417 }
418 else
419 {
420 /* The wait failed, free the message */
421 if (Message) LpcpFreeToPortZone(Message, 0);
422 }
423
424 /* All done */
425 LPCTRACE(LPC_SEND_DEBUG,
426 "Port: %p. Status: %d\n",
427 Port,
428 Status);
429
430 /* Dereference the connection port */
431 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
432 return Status;
433 }
434
435 /*
436 * @implemented
437 */
438 NTSTATUS
439 NTAPI
440 NtRequestPort(IN HANDLE PortHandle,
441 IN PPORT_MESSAGE LpcRequest)
442 {
443 NTSTATUS Status;
444 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
445 PETHREAD Thread = PsGetCurrentThread();
446 PORT_MESSAGE CapturedLpcRequest;
447 PLPCP_PORT_OBJECT Port, QueuePort, ConnectionPort = NULL;
448 ULONG MessageType;
449 PLPCP_MESSAGE Message;
450
451 PAGED_CODE();
452 LPCTRACE(LPC_SEND_DEBUG,
453 "Handle: %p. Message: %p. Type: %lx\n",
454 PortHandle,
455 LpcRequest,
456 LpcpGetMessageType(LpcRequest));
457
458 /* Check if the call comes from user mode */
459 if (PreviousMode != KernelMode)
460 {
461 _SEH2_TRY
462 {
463 /* Probe and capture the LpcRequest */
464 ProbeForRead(LpcRequest, sizeof(*LpcRequest), sizeof(ULONG));
465 CapturedLpcRequest = *(volatile PORT_MESSAGE*)LpcRequest;
466 }
467 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
468 {
469 _SEH2_YIELD(return _SEH2_GetExceptionCode());
470 }
471 _SEH2_END;
472 }
473 else
474 {
475 /* Access the LpcRequest directly */
476 CapturedLpcRequest = *LpcRequest;
477 }
478
479 /* Get the message type */
480 MessageType = CapturedLpcRequest.u2.s2.Type | LPC_DATAGRAM;
481
482 /* Can't have data information on this type of call */
483 if (CapturedLpcRequest.u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER;
484
485 /* Validate the length */
486 if (((ULONG)CapturedLpcRequest.u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
487 (ULONG)CapturedLpcRequest.u1.s1.TotalLength)
488 {
489 /* Fail */
490 return STATUS_INVALID_PARAMETER;
491 }
492
493 /* Reference the object */
494 Status = ObReferenceObjectByHandle(PortHandle,
495 0,
496 LpcPortObjectType,
497 PreviousMode,
498 (PVOID*)&Port,
499 NULL);
500 if (!NT_SUCCESS(Status)) return Status;
501
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))
505 {
506 /* Fail */
507 ObDereferenceObject(Port);
508 return STATUS_PORT_MESSAGE_TOO_LONG;
509 }
510
511 /* Allocate a message from the port zone */
512 Message = LpcpAllocateFromPortZone();
513 if (!Message)
514 {
515 /* Fail if we couldn't allocate a message */
516 ObDereferenceObject(Port);
517 return STATUS_NO_MEMORY;
518 }
519
520 /* No callback, just copy the message */
521 _SEH2_TRY
522 {
523 /* Copy it */
524 LpcpMoveMessage(&Message->Request,
525 &CapturedLpcRequest,
526 LpcRequest + 1,
527 MessageType,
528 &Thread->Cid);
529 }
530 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
531 {
532 /* Cleanup and return the exception code */
533 LpcpFreeToPortZone(Message, 0);
534 ObDereferenceObject(Port);
535 _SEH2_YIELD(return _SEH2_GetExceptionCode());
536 }
537 _SEH2_END;
538
539 /* Acquire the LPC lock */
540 KeAcquireGuardedMutex(&LpcpLock);
541
542 /* Right now clear the port context */
543 Message->PortContext = NULL;
544
545 /* Check if this is a not connection port */
546 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
547 {
548 /* We want the connected port */
549 QueuePort = Port->ConnectedPort;
550 if (!QueuePort)
551 {
552 /* We have no connected port, fail */
553 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
554 ObDereferenceObject(Port);
555 return STATUS_PORT_DISCONNECTED;
556 }
557
558 /* Check if this is a communication port */
559 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
560 {
561 /* Copy the port context and use the connection port */
562 Message->PortContext = QueuePort->PortContext;
563 ConnectionPort = QueuePort = Port->ConnectionPort;
564 if (!ConnectionPort)
565 {
566 /* Fail */
567 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
568 ObDereferenceObject(Port);
569 return STATUS_PORT_DISCONNECTED;
570 }
571 }
572 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
573 {
574 /* Use the connection port for anything but communication ports */
575 ConnectionPort = QueuePort = Port->ConnectionPort;
576 if (!ConnectionPort)
577 {
578 /* Fail */
579 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
580 ObDereferenceObject(Port);
581 return STATUS_PORT_DISCONNECTED;
582 }
583 }
584
585 /* Reference the connection port if it exists */
586 if (ConnectionPort) ObReferenceObject(ConnectionPort);
587 }
588 else
589 {
590 /* Otherwise, for a connection port, use the same port object */
591 QueuePort = Port;
592 }
593
594 /* Reference QueuePort if we have it */
595 if (QueuePort && ObReferenceObjectSafe(QueuePort))
596 {
597 /* Set sender's port */
598 Message->SenderPort = Port;
599
600 /* Generate the Message ID and set it */
601 Message->Request.MessageId = LpcpNextMessageId++;
602 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
603 Message->Request.CallbackId = 0;
604
605 /* No Message ID for the thread */
606 Thread->LpcReplyMessageId = 0;
607
608 /* Insert the message in our chain */
609 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
610
611 /* Release the lock and the semaphore */
612 KeEnterCriticalRegion();
613 KeReleaseGuardedMutex(&LpcpLock);
614 LpcpCompleteWait(QueuePort->MsgQueue.Semaphore);
615
616 /* If this is a waitable port, wake it up */
617 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
618 {
619 /* Wake it */
620 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
621 }
622
623 KeLeaveCriticalRegion();
624
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;
631 }
632
633 Status = STATUS_PORT_DISCONNECTED;
634
635 /* All done with a failure*/
636 LPCTRACE(LPC_SEND_DEBUG,
637 "Port: %p. Status: %d\n",
638 Port,
639 Status);
640
641 /* The wait failed, free the message */
642 if (Message) LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
643
644 ObDereferenceObject(Port);
645 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
646 return Status;
647 }
648
649 NTSTATUS
650 NTAPI
651 LpcpVerifyMessageDataInfo(
652 _In_ PPORT_MESSAGE Message,
653 _Out_ PULONG NumberOfDataEntries)
654 {
655 PLPCP_DATA_INFO DataInfo;
656 PUCHAR EndOfEntries;
657
658 /* Check if we have no data info at all */
659 if (Message->u2.s2.DataInfoOffset == 0)
660 {
661 *NumberOfDataEntries = 0;
662 return STATUS_SUCCESS;
663 }
664
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))))
671 {
672 return STATUS_INVALID_PARAMETER;
673 }
674
675 /* Get a pointer to the data info */
676 DataInfo = LpcpGetDataInfoFromMessage(Message);
677
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))
682 {
683 return STATUS_INVALID_PARAMETER;
684 }
685
686 *NumberOfDataEntries = DataInfo->NumberOfEntries;
687 return STATUS_SUCCESS;
688 }
689
690 /*
691 * @implemented
692 */
693 NTSTATUS
694 NTAPI
695 NtRequestWaitReplyPort(IN HANDLE PortHandle,
696 IN PPORT_MESSAGE LpcRequest,
697 IN OUT PPORT_MESSAGE LpcReply)
698 {
699 NTSTATUS Status;
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();
706 BOOLEAN Callback;
707 PKSEMAPHORE Semaphore;
708 ULONG MessageType;
709 PLPCP_DATA_INFO DataInfo;
710
711 PAGED_CODE();
712 LPCTRACE(LPC_SEND_DEBUG,
713 "Handle: %p. Messages: %p/%p. Type: %lx\n",
714 PortHandle,
715 LpcRequest,
716 LpcReply,
717 LpcpGetMessageType(LpcRequest));
718
719 /* Check if the thread is dying */
720 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
721
722 /* Check for user mode access */
723 if (PreviousMode != KernelMode)
724 {
725 _SEH2_TRY
726 {
727 /* Probe and capture the LpcRequest */
728 ProbeForRead(LpcRequest, sizeof(*LpcRequest), sizeof(ULONG));
729 CapturedLpcRequest = *(volatile PORT_MESSAGE*)LpcRequest;
730
731 /* Probe the reply message for write */
732 ProbeForWrite(LpcReply, sizeof(*LpcReply), sizeof(ULONG));
733
734 /* Make sure the data entries in the request message are valid */
735 Status = LpcpVerifyMessageDataInfo(LpcRequest, &NumberOfDataEntries);
736 if (!NT_SUCCESS(Status))
737 {
738 DPRINT1("LpcpVerifyMessageDataInfo failed\n");
739 _SEH2_YIELD(return Status);
740 }
741 }
742 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
743 {
744 DPRINT1("Got exception\n");
745 _SEH2_YIELD(return _SEH2_GetExceptionCode());
746 }
747 _SEH2_END;
748 }
749 else
750 {
751 CapturedLpcRequest = *LpcRequest;
752 Status = LpcpVerifyMessageDataInfo(LpcRequest, &NumberOfDataEntries);
753 if (!NT_SUCCESS(Status))
754 {
755 DPRINT1("LpcpVerifyMessageDataInfo failed\n");
756 return Status;
757 }
758 }
759
760 /* This flag is undocumented. Remove it before continuing */
761 CapturedLpcRequest.u2.s2.Type &= ~0x4000;
762
763 /* Check if this is an LPC Request */
764 if (LpcpGetMessageType(&CapturedLpcRequest) == LPC_REQUEST)
765 {
766 /* Then it's a callback */
767 Callback = TRUE;
768 }
769 else if (LpcpGetMessageType(&CapturedLpcRequest))
770 {
771 /* This is a not kernel-mode message */
772 DPRINT1("Not a kernel-mode message!\n");
773 return STATUS_INVALID_PARAMETER;
774 }
775 else
776 {
777 /* This is a kernel-mode message without a callback */
778 CapturedLpcRequest.u2.s2.Type |= LPC_REQUEST;
779 Callback = FALSE;
780 }
781
782 /* Get the message type */
783 MessageType = CapturedLpcRequest.u2.s2.Type;
784
785 /* Due to the above probe, we know that TotalLength is positive */
786 ASSERT(CapturedLpcRequest.u1.s1.TotalLength >= 0);
787
788 /* Validate the length */
789 if ((((ULONG)(USHORT)CapturedLpcRequest.u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
790 (ULONG)CapturedLpcRequest.u1.s1.TotalLength))
791 {
792 /* Fail */
793 DPRINT1("Invalid message length: %u, %u\n",
794 CapturedLpcRequest.u1.s1.DataLength,
795 CapturedLpcRequest.u1.s1.TotalLength);
796 return STATUS_INVALID_PARAMETER;
797 }
798
799 /* Reference the object */
800 Status = ObReferenceObjectByHandle(PortHandle,
801 0,
802 LpcPortObjectType,
803 PreviousMode,
804 (PVOID*)&Port,
805 NULL);
806 if (!NT_SUCCESS(Status)) return Status;
807
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))
811 {
812 /* Fail */
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;
818 }
819
820 /* Allocate a message from the port zone */
821 Message = LpcpAllocateFromPortZone();
822 if (!Message)
823 {
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;
828 }
829
830 /* Check if this is a callback */
831 if (Callback)
832 {
833 /* FIXME: TODO */
834 Semaphore = NULL; // we'd use the Thread Semaphore here
835 ASSERT(FALSE);
836 }
837 else
838 {
839 /* No callback, just copy the message */
840 _SEH2_TRY
841 {
842 /* Check if we have data info entries */
843 if (LpcRequest->u2.s2.DataInfoOffset != 0)
844 {
845 /* Get the data info and check if the number of entries matches
846 what we expect */
847 DataInfo = LpcpGetDataInfoFromMessage(LpcRequest);
848 if (DataInfo->NumberOfEntries != NumberOfDataEntries)
849 {
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);
855 }
856 }
857
858 /* Copy it */
859 LpcpMoveMessage(&Message->Request,
860 &CapturedLpcRequest,
861 LpcRequest + 1,
862 MessageType,
863 &Thread->Cid);
864 }
865 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
866 {
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());
872 }
873 _SEH2_END;
874
875 /* Acquire the LPC lock */
876 KeAcquireGuardedMutex(&LpcpLock);
877
878 /* Right now clear the port context */
879 Message->PortContext = NULL;
880
881 /* Check if this is a not connection port */
882 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
883 {
884 /* We want the connected port */
885 QueuePort = Port->ConnectedPort;
886 if (!QueuePort)
887 {
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;
893 }
894
895 /* This will be the rundown port */
896 ReplyPort = QueuePort;
897
898 /* Check if this is a client port */
899 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
900 {
901 /* Copy the port context */
902 Message->PortContext = QueuePort->PortContext;
903 }
904
905 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
906 {
907 /* Use the connection port for anything but communication ports */
908 ConnectionPort = QueuePort = Port->ConnectionPort;
909 if (!ConnectionPort)
910 {
911 /* Fail */
912 DPRINT1("No connection port\n");
913 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
914 ObDereferenceObject(Port);
915 return STATUS_PORT_DISCONNECTED;
916 }
917 }
918
919 /* Reference the connection port if it exists */
920 if (ConnectionPort) ObReferenceObject(ConnectionPort);
921 }
922 else
923 {
924 /* Otherwise, for a connection port, use the same port object */
925 QueuePort = ReplyPort = Port;
926 }
927
928 /* No reply thread */
929 Message->RepliedToThread = NULL;
930 Message->SenderPort = Port;
931
932 /* Generate the Message ID and set it */
933 Message->Request.MessageId = LpcpNextMessageId++;
934 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
935 Message->Request.CallbackId = 0;
936
937 /* Set the message ID for our thread now */
938 Thread->LpcReplyMessageId = Message->Request.MessageId;
939 Thread->LpcReplyMessage = NULL;
940
941 /* Insert the message in our chain */
942 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
943 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
944 LpcpSetPortToThread(Thread, Port);
945
946 /* Release the lock and get the semaphore we'll use later */
947 KeEnterCriticalRegion();
948 KeReleaseGuardedMutex(&LpcpLock);
949 Semaphore = QueuePort->MsgQueue.Semaphore;
950
951 /* If this is a waitable port, wake it up */
952 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
953 {
954 /* Wake it */
955 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
956 }
957 }
958
959 /* Now release the semaphore */
960 LpcpCompleteWait(Semaphore);
961 KeLeaveCriticalRegion();
962
963 /* And let's wait for the reply */
964 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
965
966 /* Acquire the LPC lock */
967 KeAcquireGuardedMutex(&LpcpLock);
968
969 /* Get the LPC Message and clear our thread's reply data */
970 Message = LpcpGetMessageFromThread(Thread);
971 Thread->LpcReplyMessage = NULL;
972 Thread->LpcReplyMessageId = 0;
973
974 /* Check if we have anything on the reply chain*/
975 if (!IsListEmpty(&Thread->LpcReplyChain))
976 {
977 /* Remove this thread and reinitialize the list */
978 RemoveEntryList(&Thread->LpcReplyChain);
979 InitializeListHead(&Thread->LpcReplyChain);
980 }
981
982 /* Release the lock */
983 KeReleaseGuardedMutex(&LpcpLock);
984
985 /* Check if we got a reply */
986 if (Status == STATUS_SUCCESS)
987 {
988 /* Check if we have a valid message */
989 if (Message)
990 {
991 LPCTRACE(LPC_SEND_DEBUG,
992 "Reply Messages: %p/%p\n",
993 &Message->Request,
994 (&Message->Request) + 1);
995
996 /* Move the message */
997 _SEH2_TRY
998 {
999 LpcpMoveMessage(LpcReply,
1000 &Message->Request,
1001 (&Message->Request) + 1,
1002 0,
1003 NULL);
1004 }
1005 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1006 {
1007 DPRINT1("Got exception!\n");
1008 Status = _SEH2_GetExceptionCode();
1009 }
1010 _SEH2_END;
1011
1012 /* Check if this is an LPC request with data information */
1013 if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) &&
1014 (Message->Request.u2.s2.DataInfoOffset))
1015 {
1016 /* Save the data information */
1017 LpcpSaveDataInfoMessage(Port, Message, 0);
1018 }
1019 else
1020 {
1021 /* Otherwise, just free it */
1022 LpcpFreeToPortZone(Message, 0);
1023 }
1024 }
1025 else
1026 {
1027 /* We don't have a reply */
1028 Status = STATUS_LPC_REPLY_LOST;
1029 }
1030 }
1031 else
1032 {
1033 /* The wait failed, free the message */
1034 if (Message) LpcpFreeToPortZone(Message, 0);
1035 }
1036
1037 /* All done */
1038 LPCTRACE(LPC_SEND_DEBUG,
1039 "Port: %p. Status: %d\n",
1040 Port,
1041 Status);
1042 ObDereferenceObject(Port);
1043 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
1044 return Status;
1045 }
1046
1047 /* EOF */