[NTOS:LPC]
[reactos.git] / reactos / ntoskrnl / lpc / connect.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/connect.c
5 * PURPOSE: Local Procedure Call: Connection Management
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 PVOID
18 NTAPI
19 LpcpFreeConMsg(IN OUT PLPCP_MESSAGE *Message,
20 IN OUT PLPCP_CONNECTION_MESSAGE *ConnectMessage,
21 IN PETHREAD CurrentThread)
22 {
23 PVOID SectionToMap;
24 PLPCP_MESSAGE ReplyMessage;
25
26 /* Acquire the LPC lock */
27 KeAcquireGuardedMutex(&LpcpLock);
28
29 /* Check if the reply chain is not empty */
30 if (!IsListEmpty(&CurrentThread->LpcReplyChain))
31 {
32 /* Remove this entry and re-initialize it */
33 RemoveEntryList(&CurrentThread->LpcReplyChain);
34 InitializeListHead(&CurrentThread->LpcReplyChain);
35 }
36
37 /* Check if there's a reply message */
38 ReplyMessage = LpcpGetMessageFromThread(CurrentThread);
39 if (ReplyMessage)
40 {
41 /* Get the message */
42 *Message = ReplyMessage;
43
44 /* Check if it's got messages */
45 if (!IsListEmpty(&ReplyMessage->Entry))
46 {
47 /* Clear the list */
48 RemoveEntryList(&ReplyMessage->Entry);
49 InitializeListHead(&ReplyMessage->Entry);
50 }
51
52 /* Clear message data */
53 CurrentThread->LpcReceivedMessageId = 0;
54 CurrentThread->LpcReplyMessage = NULL;
55
56 /* Get the connection message and clear the section */
57 *ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(ReplyMessage + 1);
58 SectionToMap = (*ConnectMessage)->SectionToMap;
59 (*ConnectMessage)->SectionToMap = NULL;
60 }
61 else
62 {
63 /* No message to return */
64 *Message = NULL;
65 SectionToMap = NULL;
66 }
67
68 /* Release the lock and return the section */
69 KeReleaseGuardedMutex(&LpcpLock);
70 return SectionToMap;
71 }
72
73 /* PUBLIC FUNCTIONS **********************************************************/
74
75 /*
76 * @implemented
77 */
78 NTSTATUS
79 NTAPI
80 NtSecureConnectPort(OUT PHANDLE PortHandle,
81 IN PUNICODE_STRING PortName,
82 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos,
83 IN OUT PPORT_VIEW ClientView OPTIONAL,
84 IN PSID ServerSid OPTIONAL,
85 IN OUT PREMOTE_PORT_VIEW ServerView OPTIONAL,
86 OUT PULONG MaxMessageLength OPTIONAL,
87 IN OUT PVOID ConnectionInformation OPTIONAL,
88 IN OUT PULONG ConnectionInformationLength OPTIONAL)
89 {
90 NTSTATUS Status = STATUS_SUCCESS;
91 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
92 PETHREAD Thread = PsGetCurrentThread();
93 SECURITY_QUALITY_OF_SERVICE CapturedQos;
94 PORT_VIEW CapturedClientView;
95 PSID CapturedServerSid;
96 ULONG ConnectionInfoLength = 0;
97 PLPCP_PORT_OBJECT Port, ClientPort;
98 PLPCP_MESSAGE Message;
99 PLPCP_CONNECTION_MESSAGE ConnectMessage;
100 ULONG PortMessageLength;
101 HANDLE Handle;
102 PVOID SectionToMap;
103 LARGE_INTEGER SectionOffset;
104 PTOKEN Token;
105 PTOKEN_USER TokenUserInfo;
106
107 PAGED_CODE();
108 LPCTRACE(LPC_CONNECT_DEBUG,
109 "Name: %wZ. SecurityQos: %p. Views: %p/%p. Sid: %p\n",
110 PortName,
111 SecurityQos,
112 ClientView,
113 ServerView,
114 ServerSid);
115
116 /* Check if the call comes from user mode */
117 if (PreviousMode != KernelMode)
118 {
119 /* Enter SEH for probing the parameters */
120 _SEH2_TRY
121 {
122 /* Probe the PortHandle */
123 ProbeForWriteHandle(PortHandle);
124
125 /* Probe and capture the QoS */
126 ProbeForRead(SecurityQos, sizeof(*SecurityQos), sizeof(ULONG));
127 CapturedQos = *(volatile SECURITY_QUALITY_OF_SERVICE*)SecurityQos;
128 /* NOTE: Do not care about CapturedQos.Length */
129
130 /* The following parameters are optional */
131
132 /* Capture the client view */
133 if (ClientView)
134 {
135 ProbeForWrite(ClientView, sizeof(*ClientView), sizeof(ULONG));
136 CapturedClientView = *(volatile PORT_VIEW*)ClientView;
137
138 /* Validate the size of the client view */
139 if (CapturedClientView.Length != sizeof(CapturedClientView))
140 {
141 /* Invalid size */
142 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
143 }
144
145 }
146
147 /* Capture the server view */
148 if (ServerView)
149 {
150 ProbeForWrite(ServerView, sizeof(*ServerView), sizeof(ULONG));
151
152 /* Validate the size of the server view */
153 if (((volatile REMOTE_PORT_VIEW*)ServerView)->Length != sizeof(*ServerView))
154 {
155 /* Invalid size */
156 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
157 }
158 }
159
160 if (MaxMessageLength)
161 ProbeForWriteUlong(MaxMessageLength);
162
163 /* Capture connection information length */
164 if (ConnectionInformationLength)
165 {
166 ProbeForWriteUlong(ConnectionInformationLength);
167 ConnectionInfoLength = *(volatile ULONG*)ConnectionInformationLength;
168 }
169
170 /* Probe the ConnectionInformation */
171 if (ConnectionInformation)
172 ProbeForWrite(ConnectionInformation, ConnectionInfoLength, sizeof(ULONG));
173
174 CapturedServerSid = ServerSid;
175 if (ServerSid != NULL)
176 {
177 /* Capture it */
178 Status = SepCaptureSid(ServerSid,
179 PreviousMode,
180 PagedPool,
181 TRUE,
182 &CapturedServerSid);
183 if (!NT_SUCCESS(Status))
184 {
185 DPRINT1("Failed to capture ServerSid!\n");
186 _SEH2_YIELD(return Status);
187 }
188 }
189 }
190 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
191 {
192 /* There was an exception, return the exception code */
193 _SEH2_YIELD(return _SEH2_GetExceptionCode());
194 }
195 _SEH2_END;
196 }
197 else
198 {
199 CapturedQos = *SecurityQos;
200 /* NOTE: Do not care about CapturedQos.Length */
201
202 /* The following parameters are optional */
203
204 /* Capture the client view */
205 if (ClientView)
206 {
207 /* Validate the size of the client view */
208 if (ClientView->Length != sizeof(*ClientView))
209 {
210 /* Invalid size */
211 return STATUS_INVALID_PARAMETER;
212 }
213 CapturedClientView = *ClientView;
214 }
215
216 /* Capture the server view */
217 if (ServerView)
218 {
219 /* Validate the size of the server view */
220 if (ServerView->Length != sizeof(*ServerView))
221 {
222 /* Invalid size */
223 return STATUS_INVALID_PARAMETER;
224 }
225 }
226
227 /* Capture connection information length */
228 if (ConnectionInformationLength)
229 ConnectionInfoLength = *ConnectionInformationLength;
230
231 CapturedServerSid = ServerSid;
232 }
233
234 /* Get the port */
235 Status = ObReferenceObjectByName(PortName,
236 0,
237 NULL,
238 PORT_CONNECT,
239 LpcPortObjectType,
240 PreviousMode,
241 NULL,
242 (PVOID*)&Port);
243 if (!NT_SUCCESS(Status))
244 {
245 DPRINT1("Failed to reference port '%wZ': 0x%lx\n", PortName, Status);
246
247 if (CapturedServerSid != ServerSid)
248 SepReleaseSid(CapturedServerSid, PreviousMode, TRUE);
249
250 return Status;
251 }
252
253 /* This has to be a connection port */
254 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
255 {
256 /* It isn't, so fail */
257 ObDereferenceObject(Port);
258
259 if (CapturedServerSid != ServerSid)
260 SepReleaseSid(CapturedServerSid, PreviousMode, TRUE);
261
262 return STATUS_INVALID_PORT_HANDLE;
263 }
264
265 /* Check if we have a (captured) SID */
266 if (ServerSid)
267 {
268 /* Make sure that we have a server */
269 if (Port->ServerProcess)
270 {
271 /* Get its token and query user information */
272 Token = PsReferencePrimaryToken(Port->ServerProcess);
273 Status = SeQueryInformationToken(Token, TokenUser, (PVOID*)&TokenUserInfo);
274 PsDereferencePrimaryToken(Token);
275
276 /* Check for success */
277 if (NT_SUCCESS(Status))
278 {
279 /* Compare the SIDs */
280 if (!RtlEqualSid(CapturedServerSid, TokenUserInfo->User.Sid))
281 {
282 /* Fail */
283 Status = STATUS_SERVER_SID_MISMATCH;
284 }
285
286 /* Free token information */
287 ExFreePoolWithTag(TokenUserInfo, TAG_SE);
288 }
289 }
290 else
291 {
292 /* Invalid SID */
293 Status = STATUS_SERVER_SID_MISMATCH;
294 }
295
296 /* Finally release the captured SID, we don't need it anymore */
297 if (CapturedServerSid != ServerSid)
298 SepReleaseSid(CapturedServerSid, PreviousMode, TRUE);
299
300 /* Check if SID failed */
301 if (!NT_SUCCESS(Status))
302 {
303 /* Quit */
304 ObDereferenceObject(Port);
305 return Status;
306 }
307 }
308
309 /* Create the client port */
310 Status = ObCreateObject(PreviousMode,
311 LpcPortObjectType,
312 NULL,
313 PreviousMode,
314 NULL,
315 sizeof(LPCP_PORT_OBJECT),
316 0,
317 0,
318 (PVOID*)&ClientPort);
319 if (!NT_SUCCESS(Status))
320 {
321 /* Failed, dereference the server port and return */
322 ObDereferenceObject(Port);
323 return Status;
324 }
325
326 /*
327 * Setup the client port -- From now on, dereferencing the client port
328 * will automatically dereference the connection port too.
329 */
330 RtlZeroMemory(ClientPort, sizeof(LPCP_PORT_OBJECT));
331 ClientPort->Flags = LPCP_CLIENT_PORT;
332 ClientPort->ConnectionPort = Port;
333 ClientPort->MaxMessageLength = Port->MaxMessageLength;
334 ClientPort->SecurityQos = CapturedQos;
335 InitializeListHead(&ClientPort->LpcReplyChainHead);
336 InitializeListHead(&ClientPort->LpcDataInfoChainHead);
337
338 /* Check if we have dynamic security */
339 if (CapturedQos.ContextTrackingMode == SECURITY_DYNAMIC_TRACKING)
340 {
341 /* Remember that */
342 ClientPort->Flags |= LPCP_SECURITY_DYNAMIC;
343 }
344 else
345 {
346 /* Create our own client security */
347 Status = SeCreateClientSecurity(Thread,
348 &CapturedQos,
349 FALSE,
350 &ClientPort->StaticSecurity);
351 if (!NT_SUCCESS(Status))
352 {
353 /* Security failed, dereference and return */
354 ObDereferenceObject(ClientPort);
355 return Status;
356 }
357 }
358
359 /* Initialize the port queue */
360 Status = LpcpInitializePortQueue(ClientPort);
361 if (!NT_SUCCESS(Status))
362 {
363 /* Failed */
364 ObDereferenceObject(ClientPort);
365 return Status;
366 }
367
368 /* Check if we have a client view */
369 if (ClientView)
370 {
371 /* Get the section handle */
372 Status = ObReferenceObjectByHandle(CapturedClientView.SectionHandle,
373 SECTION_MAP_READ |
374 SECTION_MAP_WRITE,
375 MmSectionObjectType,
376 PreviousMode,
377 (PVOID*)&SectionToMap,
378 NULL);
379 if (!NT_SUCCESS(Status))
380 {
381 /* Fail */
382 ObDereferenceObject(ClientPort);
383 return Status;
384 }
385
386 /* Set the section offset */
387 SectionOffset.QuadPart = CapturedClientView.SectionOffset;
388
389 /* Map it */
390 Status = MmMapViewOfSection(SectionToMap,
391 PsGetCurrentProcess(),
392 &ClientPort->ClientSectionBase,
393 0,
394 0,
395 &SectionOffset,
396 &CapturedClientView.ViewSize,
397 ViewUnmap,
398 0,
399 PAGE_READWRITE);
400
401 /* Update the offset */
402 CapturedClientView.SectionOffset = SectionOffset.LowPart;
403
404 /* Check for failure */
405 if (!NT_SUCCESS(Status))
406 {
407 /* Fail */
408 ObDereferenceObject(SectionToMap);
409 ObDereferenceObject(ClientPort);
410 return Status;
411 }
412
413 /* Update the base */
414 CapturedClientView.ViewBase = ClientPort->ClientSectionBase;
415
416 /* Reference and remember the process */
417 ClientPort->MappingProcess = PsGetCurrentProcess();
418 ObReferenceObject(ClientPort->MappingProcess);
419 }
420 else
421 {
422 /* No section */
423 SectionToMap = NULL;
424 }
425
426 /* Normalize connection information */
427 if (ConnectionInfoLength > Port->MaxConnectionInfoLength)
428 {
429 /* Use the port's maximum allowed value */
430 ConnectionInfoLength = Port->MaxConnectionInfoLength;
431 }
432
433 /* Allocate a message from the port zone */
434 Message = LpcpAllocateFromPortZone();
435 if (!Message)
436 {
437 /* Fail if we couldn't allocate a message */
438 if (SectionToMap) ObDereferenceObject(SectionToMap);
439 ObDereferenceObject(ClientPort);
440 return STATUS_NO_MEMORY;
441 }
442
443 /* Set pointer to the connection message and fill in the CID */
444 ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
445 Message->Request.ClientId = Thread->Cid;
446
447 /* Check if we have a client view */
448 if (ClientView)
449 {
450 /* Set the view size */
451 Message->Request.ClientViewSize = CapturedClientView.ViewSize;
452
453 /* Copy the client view and clear the server view */
454 RtlCopyMemory(&ConnectMessage->ClientView,
455 &CapturedClientView,
456 sizeof(CapturedClientView));
457 RtlZeroMemory(&ConnectMessage->ServerView, sizeof(REMOTE_PORT_VIEW));
458 }
459 else
460 {
461 /* Set the size to 0 and clear the connect message */
462 Message->Request.ClientViewSize = 0;
463 RtlZeroMemory(ConnectMessage, sizeof(LPCP_CONNECTION_MESSAGE));
464 }
465
466 /* Set the section and client port. Port is NULL for now */
467 ConnectMessage->ClientPort = NULL;
468 ConnectMessage->SectionToMap = SectionToMap;
469
470 /* Set the data for the connection request message */
471 Message->Request.u1.s1.DataLength = (CSHORT)ConnectionInfoLength +
472 sizeof(LPCP_CONNECTION_MESSAGE);
473 Message->Request.u1.s1.TotalLength = sizeof(LPCP_MESSAGE) +
474 Message->Request.u1.s1.DataLength;
475 Message->Request.u2.s2.Type = LPC_CONNECTION_REQUEST;
476
477 /* Check if we have connection information */
478 if (ConnectionInformation)
479 {
480 _SEH2_TRY
481 {
482 /* Copy it in */
483 RtlCopyMemory(ConnectMessage + 1,
484 ConnectionInformation,
485 ConnectionInfoLength);
486 }
487 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
488 {
489 /* Cleanup and return the exception code */
490
491 /* Free the message we have */
492 LpcpFreeToPortZone(Message, 0);
493
494 /* Dereference other objects */
495 if (SectionToMap) ObDereferenceObject(SectionToMap);
496 ObDereferenceObject(ClientPort);
497
498 /* Return status */
499 _SEH2_YIELD(return _SEH2_GetExceptionCode());
500 }
501 _SEH2_END;
502 }
503
504 /* Reset the status code */
505 Status = STATUS_SUCCESS;
506
507 /* Acquire the port lock */
508 KeAcquireGuardedMutex(&LpcpLock);
509
510 /* Check if someone already deleted the port name */
511 if (Port->Flags & LPCP_NAME_DELETED)
512 {
513 /* Fail the request */
514 Status = STATUS_OBJECT_NAME_NOT_FOUND;
515 }
516 else
517 {
518 /* Associate no thread yet */
519 Message->RepliedToThread = NULL;
520
521 /* Generate the Message ID and set it */
522 Message->Request.MessageId = LpcpNextMessageId++;
523 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
524 Thread->LpcReplyMessageId = Message->Request.MessageId;
525
526 /* Insert the message into the queue and thread chain */
527 InsertTailList(&Port->MsgQueue.ReceiveHead, &Message->Entry);
528 InsertTailList(&Port->LpcReplyChainHead, &Thread->LpcReplyChain);
529 Thread->LpcReplyMessage = Message;
530
531 /* Now we can finally reference the client port and link it */
532 ObReferenceObject(ClientPort);
533 ConnectMessage->ClientPort = ClientPort;
534
535 /* Enter a critical region */
536 KeEnterCriticalRegion();
537 }
538
539 /* Add another reference to the port */
540 ObReferenceObject(Port);
541
542 /* Release the lock */
543 KeReleaseGuardedMutex(&LpcpLock);
544
545 /* Check for success */
546 if (NT_SUCCESS(Status))
547 {
548 LPCTRACE(LPC_CONNECT_DEBUG,
549 "Messages: %p/%p. Ports: %p/%p. Status: %lx\n",
550 Message,
551 ConnectMessage,
552 Port,
553 ClientPort,
554 Status);
555
556 /* If this is a waitable port, set the event */
557 if (Port->Flags & LPCP_WAITABLE_PORT)
558 KeSetEvent(&Port->WaitEvent, 1, FALSE);
559
560 /* Release the queue semaphore and leave the critical region */
561 LpcpCompleteWait(Port->MsgQueue.Semaphore);
562 KeLeaveCriticalRegion();
563
564 /* Now wait for a reply and set 'Status' */
565 LpcpConnectWait(&Thread->LpcReplySemaphore, PreviousMode);
566 }
567
568 /* Now, always free the connection message */
569 SectionToMap = LpcpFreeConMsg(&Message, &ConnectMessage, Thread);
570
571 /* Check for failure */
572 if (!NT_SUCCESS(Status))
573 {
574 /* Check if the semaphore got signaled in the meantime */
575 if (KeReadStateSemaphore(&Thread->LpcReplySemaphore))
576 {
577 /* Wait on it */
578 KeWaitForSingleObject(&Thread->LpcReplySemaphore,
579 WrExecutive,
580 KernelMode,
581 FALSE,
582 NULL);
583 }
584
585 goto Failure;
586 }
587
588 /* Check if we got a message back */
589 if (Message)
590 {
591 /* Check for new return length */
592 if ((Message->Request.u1.s1.DataLength -
593 sizeof(LPCP_CONNECTION_MESSAGE)) < ConnectionInfoLength)
594 {
595 /* Set new normalized connection length */
596 ConnectionInfoLength = Message->Request.u1.s1.DataLength -
597 sizeof(LPCP_CONNECTION_MESSAGE);
598 }
599
600 /* Check if the caller had connection information */
601 if (ConnectionInformation)
602 {
603 _SEH2_TRY
604 {
605 /* Return the connection information length if needed */
606 if (ConnectionInformationLength)
607 *ConnectionInformationLength = ConnectionInfoLength;
608
609 /* Return the connection information */
610 RtlCopyMemory(ConnectionInformation,
611 ConnectMessage + 1,
612 ConnectionInfoLength);
613 }
614 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
615 {
616 /* Cleanup and return the exception code */
617 Status = _SEH2_GetExceptionCode();
618 _SEH2_YIELD(goto Failure);
619 }
620 _SEH2_END;
621 }
622
623 /* Make sure we had a connected port */
624 if (ClientPort->ConnectedPort)
625 {
626 /* Get the message length before the port might get killed */
627 PortMessageLength = Port->MaxMessageLength;
628
629 /* Insert the client port */
630 Status = ObInsertObject(ClientPort,
631 NULL,
632 PORT_ALL_ACCESS,
633 0,
634 NULL,
635 &Handle);
636 if (NT_SUCCESS(Status))
637 {
638 LPCTRACE(LPC_CONNECT_DEBUG,
639 "Handle: %p. Length: %lx\n",
640 Handle,
641 PortMessageLength);
642
643 _SEH2_TRY
644 {
645 /* Return the handle */
646 *PortHandle = Handle;
647
648 /* Check if maximum length was requested */
649 if (MaxMessageLength)
650 *MaxMessageLength = PortMessageLength;
651
652 /* Check if we had a client view */
653 if (ClientView)
654 {
655 /* Copy it back */
656 RtlCopyMemory(ClientView,
657 &ConnectMessage->ClientView,
658 sizeof(*ClientView));
659 }
660
661 /* Check if we had a server view */
662 if (ServerView)
663 {
664 /* Copy it back */
665 RtlCopyMemory(ServerView,
666 &ConnectMessage->ServerView,
667 sizeof(*ServerView));
668 }
669 }
670 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
671 {
672 /* An exception happened, close the opened handle */
673 ObCloseHandle(Handle, PreviousMode);
674 Status = _SEH2_GetExceptionCode();
675 }
676 _SEH2_END;
677 }
678 }
679 else
680 {
681 /* No connection port, we failed */
682 if (SectionToMap) ObDereferenceObject(SectionToMap);
683
684 /* Acquire the lock */
685 KeAcquireGuardedMutex(&LpcpLock);
686
687 /* Check if it's because the name got deleted */
688 if (!(ClientPort->ConnectionPort) ||
689 (Port->Flags & LPCP_NAME_DELETED))
690 {
691 /* Set the correct status */
692 Status = STATUS_OBJECT_NAME_NOT_FOUND;
693 }
694 else
695 {
696 /* Otherwise, the caller refused us */
697 Status = STATUS_PORT_CONNECTION_REFUSED;
698 }
699
700 /* Release the lock */
701 KeReleaseGuardedMutex(&LpcpLock);
702
703 /* Kill the port */
704 ObDereferenceObject(ClientPort);
705 }
706
707 /* Free the message */
708 LpcpFreeToPortZone(Message, 0);
709 }
710 else
711 {
712 /* No reply message, fail */
713 Status = STATUS_PORT_CONNECTION_REFUSED;
714 goto Failure;
715 }
716
717 ObDereferenceObject(Port);
718
719 /* Return status */
720 return Status;
721
722 Failure:
723 /* Check if we had a message and free it */
724 if (Message) LpcpFreeToPortZone(Message, 0);
725
726 /* Dereference other objects */
727 if (SectionToMap) ObDereferenceObject(SectionToMap);
728 ObDereferenceObject(ClientPort);
729 ObDereferenceObject(Port);
730
731 /* Return status */
732 return Status;
733 }
734
735 /*
736 * @implemented
737 */
738 NTSTATUS
739 NTAPI
740 NtConnectPort(OUT PHANDLE PortHandle,
741 IN PUNICODE_STRING PortName,
742 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos,
743 IN OUT PPORT_VIEW ClientView OPTIONAL,
744 IN OUT PREMOTE_PORT_VIEW ServerView OPTIONAL,
745 OUT PULONG MaxMessageLength OPTIONAL,
746 IN OUT PVOID ConnectionInformation OPTIONAL,
747 IN OUT PULONG ConnectionInformationLength OPTIONAL)
748 {
749 /* Call the newer API */
750 return NtSecureConnectPort(PortHandle,
751 PortName,
752 SecurityQos,
753 ClientView,
754 NULL,
755 ServerView,
756 MaxMessageLength,
757 ConnectionInformation,
758 ConnectionInformationLength);
759 }
760
761 /* EOF */