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