2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/complete.c
5 * PURPOSE: Local Procedure Call: Connection Completion
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* PRIVATE FUNCTIONS *********************************************************/
19 LpcpPrepareToWakeClient(IN PETHREAD Thread
)
23 /* Make sure the thread isn't dying and it has a valid chain */
24 if (!(Thread
->LpcExitThreadCalled
) &&
25 !(IsListEmpty(&Thread
->LpcReplyChain
)))
27 /* Remove it from the list and reinitialize it */
28 RemoveEntryList(&Thread
->LpcReplyChain
);
29 InitializeListHead(&Thread
->LpcReplyChain
);
33 /* PUBLIC FUNCTIONS **********************************************************/
40 NtAcceptConnectPort(OUT PHANDLE PortHandle
,
41 IN PVOID PortContext OPTIONAL
,
42 IN PPORT_MESSAGE ReplyMessage
,
43 IN BOOLEAN AcceptConnection
,
44 IN PPORT_VIEW ServerView
,
45 IN PREMOTE_PORT_VIEW ClientView
)
47 PLPCP_PORT_OBJECT ConnectionPort
, ServerPort
, ClientPort
;
48 PVOID ClientSectionToMap
= NULL
;
50 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
52 ULONG ConnectionInfoLength
;
53 PLPCP_MESSAGE Message
;
54 PLPCP_CONNECTION_MESSAGE ConnectMessage
;
55 PEPROCESS ClientProcess
;
56 PETHREAD ClientThread
;
57 LARGE_INTEGER SectionOffset
;
61 LPCTRACE(LPC_COMPLETE_DEBUG
,
62 "Context: %p. Message: %p. Accept: %lx. Views: %p/%p\n",
69 /* Check if the call comes from user mode */
70 if (PreviousMode
!= KernelMode
)
72 /* Enter SEH for probing the parameters */
75 ProbeForWriteHandle(PortHandle
);
77 /* Probe the basic ReplyMessage structure */
78 ProbeForRead(ReplyMessage
, sizeof(PORT_MESSAGE
), sizeof(ULONG
));
80 /* Grab some values */
81 ClientId
= ReplyMessage
->ClientId
;
82 MessageId
= ReplyMessage
->MessageId
;
83 ConnectionInfoLength
= ReplyMessage
->u1
.s1
.DataLength
;
85 /* Probe the connection info */
86 ProbeForRead(ReplyMessage
+ 1, ConnectionInfoLength
, 1);
88 /* The following parameters are optional */
89 if (ServerView
!= NULL
)
91 ProbeForWrite(ServerView
, sizeof(PORT_VIEW
), sizeof(ULONG
));
93 /* Validate the size of the server view */
94 if (ServerView
->Length
!= sizeof(PORT_VIEW
))
97 _SEH2_YIELD(return STATUS_INVALID_PARAMETER
);
101 if (ClientView
!= NULL
)
103 ProbeForWrite(ClientView
, sizeof(REMOTE_PORT_VIEW
), sizeof(ULONG
));
105 /* Validate the size of the client view */
106 if (ClientView
->Length
!= sizeof(REMOTE_PORT_VIEW
))
109 _SEH2_YIELD(return STATUS_INVALID_PARAMETER
);
113 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
115 /* There was an exception, return the exception code */
116 _SEH2_YIELD(return _SEH2_GetExceptionCode());
122 /* Grab some values */
123 ClientId
= ReplyMessage
->ClientId
;
124 MessageId
= ReplyMessage
->MessageId
;
125 ConnectionInfoLength
= ReplyMessage
->u1
.s1
.DataLength
;
127 /* Validate the size of the server view */
128 if ((ServerView
) && (ServerView
->Length
!= sizeof(PORT_VIEW
)))
131 return STATUS_INVALID_PARAMETER
;
134 /* Validate the size of the client view */
135 if ((ClientView
) && (ClientView
->Length
!= sizeof(REMOTE_PORT_VIEW
)))
138 return STATUS_INVALID_PARAMETER
;
142 /* Get the client process and thread */
143 Status
= PsLookupProcessThreadByCid(&ClientId
,
146 if (!NT_SUCCESS(Status
)) return Status
;
148 /* Acquire the LPC Lock */
149 KeAcquireGuardedMutex(&LpcpLock
);
151 /* Make sure that the client wants a reply, and this is the right one */
152 if (!(LpcpGetMessageFromThread(ClientThread
)) ||
154 (ClientThread
->LpcReplyMessageId
!= MessageId
))
156 /* Not the reply asked for, or no reply wanted, fail */
157 KeReleaseGuardedMutex(&LpcpLock
);
158 ObDereferenceObject(ClientProcess
);
159 ObDereferenceObject(ClientThread
);
160 return STATUS_REPLY_MESSAGE_MISMATCH
;
163 /* Now get the message and connection message */
164 Message
= LpcpGetMessageFromThread(ClientThread
);
165 ConnectMessage
= (PLPCP_CONNECTION_MESSAGE
)(Message
+ 1);
167 /* Get the client and connection port as well */
168 ClientPort
= ConnectMessage
->ClientPort
;
169 ConnectionPort
= ClientPort
->ConnectionPort
;
171 /* Make sure that the reply is being sent to the proper server process */
172 if (ConnectionPort
->ServerProcess
!= PsGetCurrentProcess())
174 /* It's not, so fail */
175 KeReleaseGuardedMutex(&LpcpLock
);
176 ObDereferenceObject(ClientProcess
);
177 ObDereferenceObject(ClientThread
);
178 return STATUS_REPLY_MESSAGE_MISMATCH
;
181 /* At this point, don't let other accept attempts happen */
182 ClientThread
->LpcReplyMessage
= NULL
;
183 ClientThread
->LpcReplyMessageId
= 0;
185 /* Clear the client port for now as well, then release the lock */
186 ConnectMessage
->ClientPort
= NULL
;
187 KeReleaseGuardedMutex(&LpcpLock
);
189 /* Check the connection information length */
190 if (ConnectionInfoLength
> ConnectionPort
->MaxConnectionInfoLength
)
192 /* Normalize it since it's too large */
193 ConnectionInfoLength
= ConnectionPort
->MaxConnectionInfoLength
;
196 /* Set the sizes of our reply message */
197 Message
->Request
.u1
.s1
.DataLength
= (CSHORT
)ConnectionInfoLength
+
198 sizeof(LPCP_CONNECTION_MESSAGE
);
199 Message
->Request
.u1
.s1
.TotalLength
= sizeof(LPCP_MESSAGE
) +
200 Message
->Request
.u1
.s1
.DataLength
;
202 /* Setup the reply message */
203 Message
->Request
.u2
.s2
.Type
= LPC_REPLY
;
204 Message
->Request
.u2
.s2
.DataInfoOffset
= 0;
205 Message
->Request
.ClientId
= ClientId
;
206 Message
->Request
.MessageId
= MessageId
;
207 Message
->Request
.ClientViewSize
= 0;
211 RtlCopyMemory(ConnectMessage
+ 1, ReplyMessage
+ 1, ConnectionInfoLength
);
213 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
215 Status
= _SEH2_GetExceptionCode();
216 _SEH2_YIELD(goto Cleanup
);
220 /* At this point, if the caller refused the connection, go to cleanup */
221 if (!AcceptConnection
)
223 DPRINT1("LPC connection was refused\n");
227 /* Otherwise, create the actual port */
228 Status
= ObCreateObject(PreviousMode
,
233 sizeof(LPCP_PORT_OBJECT
),
236 (PVOID
*)&ServerPort
);
237 if (!NT_SUCCESS(Status
)) goto Cleanup
;
240 RtlZeroMemory(ServerPort
, sizeof(LPCP_PORT_OBJECT
));
241 ServerPort
->PortContext
= PortContext
;
242 ServerPort
->Flags
= LPCP_COMMUNICATION_PORT
;
243 ServerPort
->MaxMessageLength
= ConnectionPort
->MaxMessageLength
;
244 InitializeListHead(&ServerPort
->LpcReplyChainHead
);
245 InitializeListHead(&ServerPort
->LpcDataInfoChainHead
);
247 /* Reference the connection port until we're fully setup */
248 ObReferenceObject(ConnectionPort
);
250 /* Link the ports together */
251 ServerPort
->ConnectionPort
= ConnectionPort
;
252 ServerPort
->ConnectedPort
= ClientPort
;
253 ClientPort
->ConnectedPort
= ServerPort
;
255 /* Also set the creator CID */
256 ServerPort
->Creator
= PsGetCurrentThread()->Cid
;
257 ClientPort
->Creator
= Message
->Request
.ClientId
;
259 /* Get the section associated and then clear it, while inside the lock */
260 KeAcquireGuardedMutex(&LpcpLock
);
261 ClientSectionToMap
= ConnectMessage
->SectionToMap
;
262 ConnectMessage
->SectionToMap
= NULL
;
263 KeReleaseGuardedMutex(&LpcpLock
);
265 /* Now check if there's a client section */
266 if (ClientSectionToMap
)
268 /* Setup the offset */
269 SectionOffset
.QuadPart
= ConnectMessage
->ClientView
.SectionOffset
;
271 /* Map the section */
272 Status
= MmMapViewOfSection(ClientSectionToMap
,
273 PsGetCurrentProcess(),
274 &ServerPort
->ClientSectionBase
,
278 &ConnectMessage
->ClientView
.ViewSize
,
283 /* Update the offset and check for mapping status */
284 ConnectMessage
->ClientView
.SectionOffset
= SectionOffset
.LowPart
;
285 if (NT_SUCCESS(Status
))
287 /* Set the view base */
288 ConnectMessage
->ClientView
.ViewRemoteBase
= ServerPort
->
291 /* Save and reference the mapping process */
292 ServerPort
->MappingProcess
= PsGetCurrentProcess();
293 ObReferenceObject(ServerPort
->MappingProcess
);
297 /* Otherwise, quit */
298 ObDereferenceObject(ServerPort
);
299 DPRINT1("Client section mapping failed: %lx\n", Status
);
300 DPRINT1("View base, offset, size: %p %lx %p\n",
301 ServerPort
->ClientSectionBase
,
302 ConnectMessage
->ClientView
.ViewSize
,
308 /* Check if there's a server section */
315 /* Reference the server port until it's fully inserted */
316 ObReferenceObject(ServerPort
);
318 /* Insert the server port in the namespace */
319 Status
= ObInsertObject(ServerPort
,
325 if (!NT_SUCCESS(Status
))
327 /* We failed, remove the extra reference and cleanup */
328 ObDereferenceObject(ServerPort
);
332 /* Enter SEH to write back the results */
335 /* Check if the caller gave a client view */
339 ClientView
->ViewBase
= ConnectMessage
->ClientView
.ViewRemoteBase
;
340 ClientView
->ViewSize
= ConnectMessage
->ClientView
.ViewSize
;
343 /* Return the handle to user mode */
344 *PortHandle
= Handle
;
346 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
348 /* Cleanup and return the exception code */
349 ObCloseHandle(Handle
, UserMode
);
350 ObDereferenceObject(ServerPort
);
351 Status
= _SEH2_GetExceptionCode();
352 _SEH2_YIELD(goto Cleanup
);
356 LPCTRACE(LPC_COMPLETE_DEBUG
,
357 "Handle: %p. Messages: %p/%p. Ports: %p/%p/%p\n",
365 /* If there was no port context, use the handle by default */
366 if (!PortContext
) ServerPort
->PortContext
= Handle
;
367 ServerPort
->ClientThread
= ClientThread
;
369 /* Set this message as the LPC Reply message while holding the lock */
370 KeAcquireGuardedMutex(&LpcpLock
);
371 ClientThread
->LpcReplyMessage
= Message
;
372 KeReleaseGuardedMutex(&LpcpLock
);
374 /* Clear the thread pointer so it doesn't get cleaned later */
377 /* Remove the extra reference we had added */
378 ObDereferenceObject(ServerPort
);
381 /* If there was a section, dereference it */
382 if (ClientSectionToMap
) ObDereferenceObject(ClientSectionToMap
);
384 /* Check if we got here while still having a client thread */
387 /* FIXME: Complex cleanup code */
391 /* Dereference the client port if we have one, and the process */
392 LPCTRACE(LPC_COMPLETE_DEBUG
,
393 "Status: %lx. Thread: %p. Process: [%.16s]\n",
396 ClientProcess
->ImageFileName
);
397 if (ClientPort
) ObDereferenceObject(ClientPort
);
398 ObDereferenceObject(ClientProcess
);
407 NtCompleteConnectPort(IN HANDLE PortHandle
)
410 PLPCP_PORT_OBJECT Port
;
411 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
414 LPCTRACE(LPC_COMPLETE_DEBUG
, "Handle: %p\n", PortHandle
);
416 /* Get the Port Object */
417 Status
= ObReferenceObjectByHandle(PortHandle
,
423 if (!NT_SUCCESS(Status
)) return Status
;
425 /* Make sure this is a connection port */
426 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_COMMUNICATION_PORT
)
429 ObDereferenceObject(Port
);
430 return STATUS_INVALID_PORT_HANDLE
;
433 /* Acquire the lock */
434 KeAcquireGuardedMutex(&LpcpLock
);
436 /* Make sure we have a client thread */
437 if (!Port
->ClientThread
)
440 KeReleaseGuardedMutex(&LpcpLock
);
441 ObDereferenceObject(Port
);
442 return STATUS_INVALID_PARAMETER
;
446 Thread
= Port
->ClientThread
;
448 /* Make sure it has a reply message */
449 if (!LpcpGetMessageFromThread(Thread
))
451 /* It doesn't, quit */
452 KeReleaseGuardedMutex(&LpcpLock
);
453 ObDereferenceObject(Port
);
454 return STATUS_SUCCESS
;
457 /* Clear the client thread and wake it up */
458 Port
->ClientThread
= NULL
;
459 LpcpPrepareToWakeClient(Thread
);
461 /* Release the lock and wait for an answer */
462 KeReleaseGuardedMutex(&LpcpLock
);
463 LpcpCompleteWait(&Thread
->LpcReplySemaphore
);
465 /* Dereference the Thread and Port and return */
466 ObDereferenceObject(Port
);
467 ObDereferenceObject(Thread
);
468 LPCTRACE(LPC_COMPLETE_DEBUG
, "Port: %p. Thread: %p\n", Port
, Thread
);