[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / lpc / complete.c
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* PRIVATE FUNCTIONS *********************************************************/
16
17 VOID
18 NTAPI
19 LpcpPrepareToWakeClient(IN PETHREAD Thread)
20 {
21 PAGED_CODE();
22
23 /* Make sure the thread isn't dying and it has a valid chain */
24 if (!(Thread->LpcExitThreadCalled) &&
25 !(IsListEmpty(&Thread->LpcReplyChain)))
26 {
27 /* Remove it from the list and reinitialize it */
28 RemoveEntryList(&Thread->LpcReplyChain);
29 InitializeListHead(&Thread->LpcReplyChain);
30 }
31 }
32
33 /* PUBLIC FUNCTIONS **********************************************************/
34
35 /*
36 * @implemented
37 */
38 NTSTATUS
39 NTAPI
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)
46 {
47 PLPCP_PORT_OBJECT ConnectionPort, ServerPort, ClientPort;
48 PVOID ClientSectionToMap = NULL;
49 HANDLE Handle;
50 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
51 NTSTATUS Status;
52 ULONG ConnectionInfoLength;
53 PLPCP_MESSAGE Message;
54 PLPCP_CONNECTION_MESSAGE ConnectMessage;
55 PEPROCESS ClientProcess;
56 PETHREAD ClientThread;
57 LARGE_INTEGER SectionOffset;
58 CLIENT_ID ClientId;
59 ULONG MessageId;
60 PAGED_CODE();
61 LPCTRACE(LPC_COMPLETE_DEBUG,
62 "Context: %p. Message: %p. Accept: %lx. Views: %p/%p\n",
63 PortContext,
64 ReplyMessage,
65 AcceptConnection,
66 ClientView,
67 ServerView);
68
69 /* Check if the call comes from user mode */
70 if (PreviousMode != KernelMode)
71 {
72 /* Enter SEH for probing the parameters */
73 _SEH2_TRY
74 {
75 ProbeForWriteHandle(PortHandle);
76
77 /* Probe the basic ReplyMessage structure */
78 ProbeForRead(ReplyMessage, sizeof(PORT_MESSAGE), sizeof(ULONG));
79
80 /* Grab some values */
81 ClientId = ReplyMessage->ClientId;
82 MessageId = ReplyMessage->MessageId;
83 ConnectionInfoLength = ReplyMessage->u1.s1.DataLength;
84
85 /* Probe the connection info */
86 ProbeForRead(ReplyMessage + 1, ConnectionInfoLength, 1);
87
88 /* The following parameters are optional */
89 if (ServerView != NULL)
90 {
91 ProbeForWrite(ServerView, sizeof(PORT_VIEW), sizeof(ULONG));
92
93 /* Validate the size of the server view */
94 if (ServerView->Length != sizeof(PORT_VIEW))
95 {
96 /* Invalid size */
97 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
98 }
99 }
100
101 if (ClientView != NULL)
102 {
103 ProbeForWrite(ClientView, sizeof(REMOTE_PORT_VIEW), sizeof(ULONG));
104
105 /* Validate the size of the client view */
106 if (ClientView->Length != sizeof(REMOTE_PORT_VIEW))
107 {
108 /* Invalid size */
109 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
110 }
111 }
112 }
113 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
114 {
115 /* There was an exception, return the exception code */
116 _SEH2_YIELD(return _SEH2_GetExceptionCode());
117 }
118 _SEH2_END;
119 }
120 else
121 {
122 /* Grab some values */
123 ClientId = ReplyMessage->ClientId;
124 MessageId = ReplyMessage->MessageId;
125 ConnectionInfoLength = ReplyMessage->u1.s1.DataLength;
126
127 /* Validate the size of the server view */
128 if ((ServerView) && (ServerView->Length != sizeof(PORT_VIEW)))
129 {
130 /* Invalid size */
131 return STATUS_INVALID_PARAMETER;
132 }
133
134 /* Validate the size of the client view */
135 if ((ClientView) && (ClientView->Length != sizeof(REMOTE_PORT_VIEW)))
136 {
137 /* Invalid size */
138 return STATUS_INVALID_PARAMETER;
139 }
140 }
141
142 /* Get the client process and thread */
143 Status = PsLookupProcessThreadByCid(&ClientId,
144 &ClientProcess,
145 &ClientThread);
146 if (!NT_SUCCESS(Status)) return Status;
147
148 /* Acquire the LPC Lock */
149 KeAcquireGuardedMutex(&LpcpLock);
150
151 /* Make sure that the client wants a reply, and this is the right one */
152 if (!(LpcpGetMessageFromThread(ClientThread)) ||
153 !(MessageId) ||
154 (ClientThread->LpcReplyMessageId != MessageId))
155 {
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;
161 }
162
163 /* Now get the message and connection message */
164 Message = LpcpGetMessageFromThread(ClientThread);
165 ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
166
167 /* Get the client and connection port as well */
168 ClientPort = ConnectMessage->ClientPort;
169 ConnectionPort = ClientPort->ConnectionPort;
170
171 /* Make sure that the reply is being sent to the proper server process */
172 if (ConnectionPort->ServerProcess != PsGetCurrentProcess())
173 {
174 /* It's not, so fail */
175 KeReleaseGuardedMutex(&LpcpLock);
176 ObDereferenceObject(ClientProcess);
177 ObDereferenceObject(ClientThread);
178 return STATUS_REPLY_MESSAGE_MISMATCH;
179 }
180
181 /* At this point, don't let other accept attempts happen */
182 ClientThread->LpcReplyMessage = NULL;
183 ClientThread->LpcReplyMessageId = 0;
184
185 /* Clear the client port for now as well, then release the lock */
186 ConnectMessage->ClientPort = NULL;
187 KeReleaseGuardedMutex(&LpcpLock);
188
189 /* Check the connection information length */
190 if (ConnectionInfoLength > ConnectionPort->MaxConnectionInfoLength)
191 {
192 /* Normalize it since it's too large */
193 ConnectionInfoLength = ConnectionPort->MaxConnectionInfoLength;
194 }
195
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;
201
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;
208
209 _SEH2_TRY
210 {
211 RtlCopyMemory(ConnectMessage + 1, ReplyMessage + 1, ConnectionInfoLength);
212 }
213 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
214 {
215 Status = _SEH2_GetExceptionCode();
216 _SEH2_YIELD(goto Cleanup);
217 }
218 _SEH2_END;
219
220 /* At this point, if the caller refused the connection, go to cleanup */
221 if (!AcceptConnection)
222 {
223 DPRINT1("LPC connection was refused\n");
224 goto Cleanup;
225 }
226
227 /* Otherwise, create the actual port */
228 Status = ObCreateObject(PreviousMode,
229 LpcPortObjectType,
230 NULL,
231 PreviousMode,
232 NULL,
233 sizeof(LPCP_PORT_OBJECT),
234 0,
235 0,
236 (PVOID*)&ServerPort);
237 if (!NT_SUCCESS(Status)) goto Cleanup;
238
239 /* Set it up */
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);
246
247 /* Reference the connection port until we're fully setup */
248 ObReferenceObject(ConnectionPort);
249
250 /* Link the ports together */
251 ServerPort->ConnectionPort = ConnectionPort;
252 ServerPort->ConnectedPort = ClientPort;
253 ClientPort->ConnectedPort = ServerPort;
254
255 /* Also set the creator CID */
256 ServerPort->Creator = PsGetCurrentThread()->Cid;
257 ClientPort->Creator = Message->Request.ClientId;
258
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);
264
265 /* Now check if there's a client section */
266 if (ClientSectionToMap)
267 {
268 /* Setup the offset */
269 SectionOffset.QuadPart = ConnectMessage->ClientView.SectionOffset;
270
271 /* Map the section */
272 Status = MmMapViewOfSection(ClientSectionToMap,
273 PsGetCurrentProcess(),
274 &ServerPort->ClientSectionBase,
275 0,
276 0,
277 &SectionOffset,
278 &ConnectMessage->ClientView.ViewSize,
279 ViewUnmap,
280 0,
281 PAGE_READWRITE);
282
283 /* Update the offset and check for mapping status */
284 ConnectMessage->ClientView.SectionOffset = SectionOffset.LowPart;
285 if (NT_SUCCESS(Status))
286 {
287 /* Set the view base */
288 ConnectMessage->ClientView.ViewRemoteBase = ServerPort->
289 ClientSectionBase;
290
291 /* Save and reference the mapping process */
292 ServerPort->MappingProcess = PsGetCurrentProcess();
293 ObReferenceObject(ServerPort->MappingProcess);
294 }
295 else
296 {
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,
303 SectionOffset);
304 goto Cleanup;
305 }
306 }
307
308 /* Check if there's a server section */
309 if (ServerView)
310 {
311 /* FIXME: TODO */
312 ASSERT(FALSE);
313 }
314
315 /* Reference the server port until it's fully inserted */
316 ObReferenceObject(ServerPort);
317
318 /* Insert the server port in the namespace */
319 Status = ObInsertObject(ServerPort,
320 NULL,
321 PORT_ALL_ACCESS,
322 0,
323 NULL,
324 &Handle);
325 if (!NT_SUCCESS(Status))
326 {
327 /* We failed, remove the extra reference and cleanup */
328 ObDereferenceObject(ServerPort);
329 goto Cleanup;
330 }
331
332 /* Enter SEH to write back the results */
333 _SEH2_TRY
334 {
335 /* Check if the caller gave a client view */
336 if (ClientView)
337 {
338 /* Fill it out */
339 ClientView->ViewBase = ConnectMessage->ClientView.ViewRemoteBase;
340 ClientView->ViewSize = ConnectMessage->ClientView.ViewSize;
341 }
342
343 /* Return the handle to user mode */
344 *PortHandle = Handle;
345 }
346 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
347 {
348 /* Cleanup and return the exception code */
349 ObCloseHandle(Handle, UserMode);
350 ObDereferenceObject(ServerPort);
351 Status = _SEH2_GetExceptionCode();
352 _SEH2_YIELD(goto Cleanup);
353 }
354 _SEH2_END;
355
356 LPCTRACE(LPC_COMPLETE_DEBUG,
357 "Handle: %p. Messages: %p/%p. Ports: %p/%p/%p\n",
358 Handle,
359 Message,
360 ConnectMessage,
361 ServerPort,
362 ClientPort,
363 ConnectionPort);
364
365 /* If there was no port context, use the handle by default */
366 if (!PortContext) ServerPort->PortContext = Handle;
367 ServerPort->ClientThread = ClientThread;
368
369 /* Set this message as the LPC Reply message while holding the lock */
370 KeAcquireGuardedMutex(&LpcpLock);
371 ClientThread->LpcReplyMessage = Message;
372 KeReleaseGuardedMutex(&LpcpLock);
373
374 /* Clear the thread pointer so it doesn't get cleaned later */
375 ClientThread = NULL;
376
377 /* Remove the extra reference we had added */
378 ObDereferenceObject(ServerPort);
379
380 Cleanup:
381 /* If there was a section, dereference it */
382 if (ClientSectionToMap) ObDereferenceObject(ClientSectionToMap);
383
384 /* Check if we got here while still having a client thread */
385 if (ClientThread)
386 {
387 /* FIXME: Complex cleanup code */
388 ASSERT(FALSE);
389 }
390
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",
394 Status,
395 ClientThread,
396 ClientProcess->ImageFileName);
397 if (ClientPort) ObDereferenceObject(ClientPort);
398 ObDereferenceObject(ClientProcess);
399 return Status;
400 }
401
402 /*
403 * @implemented
404 */
405 NTSTATUS
406 NTAPI
407 NtCompleteConnectPort(IN HANDLE PortHandle)
408 {
409 NTSTATUS Status;
410 PLPCP_PORT_OBJECT Port;
411 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
412 PETHREAD Thread;
413 PAGED_CODE();
414 LPCTRACE(LPC_COMPLETE_DEBUG, "Handle: %p\n", PortHandle);
415
416 /* Get the Port Object */
417 Status = ObReferenceObjectByHandle(PortHandle,
418 PORT_ALL_ACCESS,
419 LpcPortObjectType,
420 PreviousMode,
421 (PVOID*)&Port,
422 NULL);
423 if (!NT_SUCCESS(Status)) return Status;
424
425 /* Make sure this is a connection port */
426 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
427 {
428 /* It isn't, fail */
429 ObDereferenceObject(Port);
430 return STATUS_INVALID_PORT_HANDLE;
431 }
432
433 /* Acquire the lock */
434 KeAcquireGuardedMutex(&LpcpLock);
435
436 /* Make sure we have a client thread */
437 if (!Port->ClientThread)
438 {
439 /* We don't, fail */
440 KeReleaseGuardedMutex(&LpcpLock);
441 ObDereferenceObject(Port);
442 return STATUS_INVALID_PARAMETER;
443 }
444
445 /* Get the thread */
446 Thread = Port->ClientThread;
447
448 /* Make sure it has a reply message */
449 if (!LpcpGetMessageFromThread(Thread))
450 {
451 /* It doesn't, quit */
452 KeReleaseGuardedMutex(&LpcpLock);
453 ObDereferenceObject(Port);
454 return STATUS_SUCCESS;
455 }
456
457 /* Clear the client thread and wake it up */
458 Port->ClientThread = NULL;
459 LpcpPrepareToWakeClient(Thread);
460
461 /* Release the lock and wait for an answer */
462 KeReleaseGuardedMutex(&LpcpLock);
463 LpcpCompleteWait(&Thread->LpcReplySemaphore);
464
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);
469 return Status;
470 }
471
472 /* EOF */