09ffb0241dbc5dbc3c6a5d694df5d96fa403134d
[reactos.git] / ntoskrnl / lpc / connect.c
1 /* $Id$
2  * 
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            ntoskrnl/lpc/connect.c
6  * PURPOSE:         Communication mechanism
7  * PROGRAMMER:      David Welch (welch@cwcom.net)
8  * UPDATE HISTORY:
9  *                  Created 22/05/98
10  */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <internal/ob.h>
16 #include <internal/port.h>
17 #include <internal/dbg.h>
18 #include <internal/pool.h>
19 #include <internal/safe.h>
20 #include <internal/mm.h>
21
22 #define NDEBUG
23 #include <internal/debug.h>
24
25 /* GLOBALS *******************************************************************/
26
27 #define TAG_LPC_CONNECT_MESSAGE   TAG('L', 'P', 'C', 'C')
28
29 /* FUNCTIONS *****************************************************************/
30
31 NTSTATUS STDCALL
32 EiConnectPort(IN PEPORT* ConnectedPort,
33               IN PEPORT NamedPort,
34               IN PSECTION_OBJECT Section,
35               IN LARGE_INTEGER SectionOffset,
36               IN ULONG ViewSize,
37               OUT PVOID* ClientSendViewBase,
38               OUT PVOID* ServerSendViewBase,
39               OUT PULONG ReceiveViewSize,
40               OUT PVOID* ReceiveViewBase,
41               OUT PULONG MaximumMessageSize,
42               IN OUT PVOID ConnectData,
43               IN OUT PULONG ConnectDataLength)
44 {
45   PEPORT_CONNECT_REQUEST_MESSAGE RequestMessage;
46   ULONG RequestConnectDataLength;
47   PEPORT OurPort;
48   PQUEUEDMESSAGE Reply;
49   PEPORT_CONNECT_REPLY_MESSAGE CReply;
50   NTSTATUS Status;
51   KIRQL oldIrql;
52
53   if (ConnectDataLength == NULL)
54     {
55       RequestConnectDataLength = 0;
56     }
57   else
58     {
59       RequestConnectDataLength = *ConnectDataLength;
60     }
61
62   /*
63    * Create a port to represent our side of the connection
64    */
65   Status = ObCreateObject (NULL,
66                            PORT_ALL_ACCESS,
67                            NULL,
68                            ExPortType,
69                            (PVOID*)&OurPort);
70   if (!NT_SUCCESS(Status))
71     {
72       return (Status);
73     }
74   NiInitializePort(OurPort);
75
76   /*
77    * Allocate a request message.
78    */
79   RequestMessage = ExAllocatePool(NonPagedPool, 
80                                   sizeof(EPORT_CONNECT_REQUEST_MESSAGE) + 
81                                   RequestConnectDataLength);
82   if (RequestMessage == NULL)
83     {
84       ObDereferenceObject(OurPort);
85       return(STATUS_NO_MEMORY);
86     }
87
88   /*
89    * Initialize the request message.
90    */
91   RequestMessage->MessageHeader.DataSize = 
92     sizeof(EPORT_CONNECT_REQUEST_MESSAGE) + RequestConnectDataLength -
93     sizeof(LPC_MESSAGE);
94   RequestMessage->MessageHeader.MessageSize = 
95     sizeof(EPORT_CONNECT_REQUEST_MESSAGE) + RequestConnectDataLength;
96   DPRINT("RequestMessageSize %d\n",
97          RequestMessage->MessageHeader.MessageSize);
98   RequestMessage->MessageHeader.SharedSectionSize = 0;
99   RequestMessage->ConnectingProcess = PsGetCurrentProcess();
100   ObReferenceObjectByPointer(RequestMessage->ConnectingProcess,
101                              PROCESS_VM_OPERATION,
102                              NULL,
103                              KernelMode);
104   RequestMessage->SendSectionObject = (struct _SECTION_OBJECT*)Section;
105   RequestMessage->SendSectionOffset = SectionOffset;
106   RequestMessage->SendViewSize = ViewSize;
107   RequestMessage->ConnectDataLength = RequestConnectDataLength;
108   if (RequestConnectDataLength > 0)
109     {
110       memcpy(RequestMessage->ConnectData, ConnectData,
111              RequestConnectDataLength);
112     }
113   
114   /*
115    * Queue the message to the named port
116    */
117   EiReplyOrRequestPort(NamedPort,
118                        &RequestMessage->MessageHeader,
119                        LPC_CONNECTION_REQUEST,
120                        OurPort);
121   KeReleaseSemaphore(&NamedPort->Semaphore, IO_NO_INCREMENT, 1, FALSE);
122   ExFreePool(RequestMessage);
123   
124   /*
125    * Wait for them to accept our connection
126    */
127   KeWaitForSingleObject(&OurPort->Semaphore,
128                         UserRequest,
129                         UserMode,
130                         FALSE,
131                         NULL);
132
133   /* 
134    * Dequeue the response
135    */
136   KeAcquireSpinLock (&OurPort->Lock, &oldIrql);
137   Reply = EiDequeueMessagePort (OurPort);
138   KeReleaseSpinLock (&OurPort->Lock, oldIrql);
139   CReply = (PEPORT_CONNECT_REPLY_MESSAGE)&Reply->Message;
140
141   /*
142    * Do some initial cleanup.
143    */
144   ObDereferenceObject(PsGetCurrentProcess());
145
146   /*
147    * Check for connection refusal.
148    */
149   if (CReply->MessageHeader.MessageType == LPC_CONNECTION_REFUSED)
150     {
151       ObDereferenceObject(OurPort);
152       ExFreePool(Reply);
153       /*
154        * FIXME: Check what NT does here. Giving the user data back on
155        * connect failure sounds reasonable; it probably wouldn't break
156        * anything anyway.
157        */
158       if (ConnectDataLength != NULL)
159         {
160           *ConnectDataLength = CReply->ConnectDataLength;
161           memcpy(ConnectData, CReply->ConnectData, CReply->ConnectDataLength);
162         }
163       return(STATUS_PORT_CONNECTION_REFUSED);
164     }
165
166   /*
167    * Otherwise we are connected. Copy data back to the client.
168    */
169   *ServerSendViewBase = CReply->SendServerViewBase;
170   *ReceiveViewSize = CReply->ReceiveClientViewSize;
171   *ReceiveViewBase = CReply->ReceiveClientViewBase;
172   *MaximumMessageSize = CReply->MaximumMessageSize;
173   if (ConnectDataLength != NULL)
174     {
175       *ConnectDataLength = CReply->ConnectDataLength;
176       memcpy(ConnectData, CReply->ConnectData, CReply->ConnectDataLength);
177     }
178
179   /*
180    * Create our view of the send section object.
181    */
182   if (Section != NULL)
183     {
184       *ClientSendViewBase = 0;
185       Status = MmMapViewOfSection(Section,
186                                   PsGetCurrentProcess(),
187                                   ClientSendViewBase,
188                                   0,
189                                   ViewSize,
190                                   &SectionOffset,
191                                   &ViewSize,
192                                   ViewUnmap,
193                                   0 /* MEM_TOP_DOWN? */,
194                                   PAGE_READWRITE);
195       if (!NT_SUCCESS(Status))
196         {
197           /* FIXME: Cleanup here. */
198           return(Status);
199         }
200     }
201
202   /*
203    * Do the final initialization of our port.
204    */
205   OurPort->State = EPORT_CONNECTED_CLIENT;
206
207   /*
208    * Cleanup.
209    */
210   ExFreePool(Reply);
211   *ConnectedPort = OurPort;
212   return(STATUS_SUCCESS);
213 }
214
215 /**********************************************************************
216  * NAME                                                 EXPORTED
217  *      NtConnectPort@32
218  *      
219  * DESCRIPTION
220  *      Connect to a named port and wait for the other side to 
221  *      accept the connection.
222  *
223  * ARGUMENTS
224  *      ConnectedPort
225  *      PortName
226  *      Qos
227  *      WriteMap
228  *      ReadMap
229  *      MaxMessageSize
230  *      ConnectInfo
231  *      UserConnectInfoLength
232  * 
233  * RETURN VALUE
234  * 
235  */
236 NTSTATUS STDCALL
237 NtConnectPort (PHANDLE                          UnsafeConnectedPortHandle,
238                PUNICODE_STRING                  PortName,
239                PSECURITY_QUALITY_OF_SERVICE     Qos,
240                PLPC_SECTION_WRITE               UnsafeWriteMap,
241                PLPC_SECTION_READ                UnsafeReadMap,
242                PULONG                           UnsafeMaximumMessageSize,
243                PVOID                            UnsafeConnectData,
244                PULONG                           UnsafeConnectDataLength)
245 {
246   HANDLE ConnectedPortHandle;
247   LPC_SECTION_WRITE WriteMap;
248   LPC_SECTION_READ ReadMap;
249   ULONG MaximumMessageSize;
250   PVOID ConnectData;
251   ULONG ConnectDataLength;
252   PSECTION_OBJECT SectionObject;
253   LARGE_INTEGER SectionOffset;
254   PEPORT ConnectedPort;
255   NTSTATUS Status;
256   PEPORT NamedPort;
257   
258   /*
259    * Copy in write map and partially validate.
260    */
261   if (UnsafeWriteMap != NULL)
262     {
263       Status = MmCopyFromCaller(&WriteMap, UnsafeWriteMap, 
264                                 sizeof(LPC_SECTION_WRITE));
265       if (!NT_SUCCESS(Status))
266         {
267           return(Status);
268         }
269       if (WriteMap.Length != sizeof(LPC_SECTION_WRITE))
270         {
271           return(STATUS_INVALID_PARAMETER_4);
272         }
273       SectionOffset.QuadPart = WriteMap.SectionOffset;
274     }
275   else
276     {
277       WriteMap.SectionHandle = INVALID_HANDLE_VALUE;
278     }
279
280   /*
281    * Handle connection data.
282    */
283   if (UnsafeConnectData == NULL)
284     {
285       ConnectDataLength = 0;
286       ConnectData = NULL;
287     }
288   else
289     {
290       if (ExGetPreviousMode() == KernelMode)
291         {
292           ConnectDataLength = *UnsafeConnectDataLength;
293           ConnectData = UnsafeConnectData;
294         }
295       else
296         {
297           Status = MmCopyFromCaller(&ConnectDataLength,
298                                     UnsafeConnectDataLength,
299                                     sizeof(ULONG));
300           if (!NT_SUCCESS(Status))
301             {
302               return(Status);
303             }
304           ConnectData = ExAllocatePool(NonPagedPool, ConnectDataLength);
305           if (ConnectData == NULL && ConnectDataLength != 0)
306             {
307               return(STATUS_NO_MEMORY);
308             }
309           Status = MmCopyFromCaller(ConnectData,
310                                     UnsafeConnectData,
311                                     ConnectDataLength); 
312           if (!NT_SUCCESS(Status))
313             {
314               ExFreePool(ConnectData);
315               return(Status);
316             }
317         }
318     }
319
320   /*
321    * Reference the named port.
322    */
323   Status = ObReferenceObjectByName (PortName,
324                                     0,
325                                     NULL,
326                                     PORT_ALL_ACCESS,  /* DesiredAccess */
327                                     ExPortType,
328                                     UserMode,
329                                     NULL,
330                                     (PVOID*)&NamedPort);
331   if (!NT_SUCCESS(Status))
332     {
333       if (KeGetPreviousMode() != KernelMode)
334         {
335           ExFreePool(ConnectData);
336         }
337       return(Status);
338     }
339
340   /*
341    * Reference the send section object.
342    */
343   if (WriteMap.SectionHandle != INVALID_HANDLE_VALUE)
344     {
345       Status = ObReferenceObjectByHandle(WriteMap.SectionHandle,
346                                          SECTION_MAP_READ | SECTION_MAP_WRITE,
347                                          MmSectionObjectType,
348                                          UserMode,
349                                          (PVOID*)&SectionObject,
350                                          NULL);
351       if (!NT_SUCCESS(Status))
352         {
353           ObDereferenceObject(NamedPort);
354           if (KeGetPreviousMode() != KernelMode)
355             {
356               ExFreePool(ConnectData);
357             }
358           return(Status);
359         }
360     }
361   else
362     {
363       SectionObject = NULL;
364     }
365
366   /*
367    * Do the connection establishment.
368    */
369   Status = EiConnectPort(&ConnectedPort,
370                          NamedPort,
371                          SectionObject,
372                          SectionOffset,
373                          WriteMap.ViewSize,
374                          &WriteMap.ViewBase,
375                          &WriteMap.TargetViewBase,
376                          &ReadMap.ViewSize,
377                          &ReadMap.ViewBase,
378                          &MaximumMessageSize,
379                          ConnectData,
380                          &ConnectDataLength);
381   if (!NT_SUCCESS(Status))
382     {
383       /* FIXME: Again, check what NT does here. */
384       if (UnsafeConnectDataLength != NULL)
385         {
386           if (ExGetPreviousMode() != KernelMode)
387             {
388               MmCopyToCaller(UnsafeConnectData, ConnectData,
389                              ConnectDataLength);
390               ExFreePool(ConnectData);
391             }
392           MmCopyToCaller(UnsafeConnectDataLength, &ConnectDataLength,
393                          sizeof(ULONG));
394         }
395       return(Status);
396     }
397
398   /*
399    * Do some initial cleanup.
400    */
401   if (SectionObject != NULL)
402     {
403       ObDereferenceObject(SectionObject);
404       SectionObject = NULL;
405     }
406   ObDereferenceObject(NamedPort);
407   NamedPort = NULL;
408   
409   /*
410    * Copy the data back to the caller.
411    */
412   if (ExGetPreviousMode() != KernelMode)
413     {
414       if (UnsafeConnectDataLength != NULL)
415         {
416           if (ExGetPreviousMode() != KernelMode)
417             {
418               Status = MmCopyToCaller(UnsafeConnectData, ConnectData,
419                                       ConnectDataLength);
420               ExFreePool(ConnectData);
421               if (!NT_SUCCESS(Status))
422                 {
423                   return(Status);
424                 }
425             }
426           Status = MmCopyToCaller(UnsafeConnectDataLength, &ConnectDataLength,
427                                   sizeof(ULONG));
428           if (!NT_SUCCESS(Status))
429             {
430               return(Status);
431             }
432         }
433     }
434   Status = ObInsertObject(ConnectedPort,
435                           NULL,
436                           PORT_ALL_ACCESS,
437                           0,
438                           NULL,
439                           &ConnectedPortHandle);
440   if (!NT_SUCCESS(Status))
441     {
442       return(Status);
443     }
444   Status = MmCopyToCaller(UnsafeConnectedPortHandle, &ConnectedPortHandle,
445                           sizeof(HANDLE));
446   if (!NT_SUCCESS(Status))
447     {
448       return(Status);
449     }
450   if (UnsafeWriteMap != NULL)
451     {
452       Status = MmCopyToCaller(UnsafeWriteMap, &WriteMap, 
453                               sizeof(LPC_SECTION_WRITE));
454       if (!NT_SUCCESS(Status))
455         {
456           return(Status);
457         }
458     }
459   if (UnsafeReadMap != NULL)
460     {
461       Status = MmCopyToCaller(UnsafeReadMap, &ReadMap,
462                               sizeof(LPC_SECTION_READ));
463       if (!NT_SUCCESS(Status))
464         {
465           return(Status);
466         }
467     }
468   if (UnsafeMaximumMessageSize != NULL)
469     {
470       Status = MmCopyToCaller(UnsafeMaximumMessageSize, 
471                               &MaximumMessageSize,
472                               sizeof(LPC_SECTION_WRITE));
473       if (!NT_SUCCESS(Status))
474         {
475           return(Status);
476         }
477     }
478
479   /*
480    * All done.
481    */
482
483   return(STATUS_SUCCESS);
484 }
485
486
487 /**********************************************************************
488  * NAME                                                 EXPORTED
489  *      NtAcceptConnectPort@24
490  *
491  * DESCRIPTION
492  *
493  * ARGUMENTS
494  *      ServerPortHandle
495  *      NamedPortHandle
496  *      LpcMessage
497  *      AcceptIt
498  *      WriteMap
499  *      ReadMap
500  *
501  * RETURN VALUE
502  *
503  */
504 EXPORTED NTSTATUS STDCALL
505 NtAcceptConnectPort (PHANDLE                    ServerPortHandle,
506                      HANDLE                     NamedPortHandle,
507                      PLPC_MESSAGE               LpcMessage,
508                      BOOLEAN                    AcceptIt,
509                      PLPC_SECTION_WRITE WriteMap,
510                      PLPC_SECTION_READ  ReadMap)
511 {
512   NTSTATUS      Status;
513   PEPORT                NamedPort;
514   PEPORT                OurPort = NULL;
515   PQUEUEDMESSAGE        ConnectionRequest;
516   KIRQL         oldIrql;
517   PEPORT_CONNECT_REQUEST_MESSAGE CRequest;
518   PEPORT_CONNECT_REPLY_MESSAGE CReply;
519   ULONG Size;
520
521   Size = sizeof(EPORT_CONNECT_REPLY_MESSAGE);
522   if (LpcMessage)
523   {
524      Size += LpcMessage->DataSize;
525   }
526
527   CReply = ExAllocatePool(NonPagedPool, Size);
528   if (CReply == NULL)
529     {
530       return(STATUS_NO_MEMORY);
531     }
532   
533   Status = ObReferenceObjectByHandle(NamedPortHandle,
534                                      PORT_ALL_ACCESS,
535                                      ExPortType,
536                                      UserMode,
537                                      (PVOID*)&NamedPort,
538                                      NULL);
539   if (!NT_SUCCESS(Status))
540     {
541       ExFreePool(CReply);
542       return (Status);
543     }
544
545   /*
546    * Create a port object for our side of the connection
547    */
548   if (AcceptIt)
549     {
550       Status = ObCreateObject(ServerPortHandle,
551                               PORT_ALL_ACCESS,
552                               NULL,
553                               ExPortType,
554                               (PVOID*)&OurPort);
555       if (!NT_SUCCESS(Status))
556         {
557           ExFreePool(CReply);
558           ObDereferenceObject(NamedPort);
559           return(Status);
560         }
561       NiInitializePort(OurPort);
562     }
563   
564   /*
565    * Dequeue the connection request
566    */
567   KeAcquireSpinLock(&NamedPort->Lock, &oldIrql);
568   ConnectionRequest = EiDequeueConnectMessagePort (NamedPort);
569   KeReleaseSpinLock(&NamedPort->Lock, oldIrql);
570   CRequest = (PEPORT_CONNECT_REQUEST_MESSAGE)(&ConnectionRequest->Message);
571   
572   /*
573    * Prepare the reply.
574    */
575   if (LpcMessage != NULL)
576     {
577       memcpy(&CReply->MessageHeader, LpcMessage, sizeof(LPC_MESSAGE));
578       memcpy(&CReply->ConnectData, (PVOID)(LpcMessage + 1), 
579              LpcMessage->DataSize);
580       CReply->MessageHeader.MessageSize =
581         sizeof(EPORT_CONNECT_REPLY_MESSAGE) + LpcMessage->DataSize;
582       CReply->MessageHeader.DataSize = CReply->MessageHeader.MessageSize -
583         sizeof(LPC_MESSAGE);
584       CReply->ConnectDataLength = LpcMessage->DataSize;
585     }
586   else
587     {
588       CReply->MessageHeader.MessageSize = sizeof(EPORT_CONNECT_REPLY_MESSAGE);
589       CReply->MessageHeader.DataSize = sizeof(EPORT_CONNECT_REPLY_MESSAGE) -
590         sizeof(LPC_MESSAGE);
591       CReply->ConnectDataLength = 0;
592     }
593   if (!AcceptIt)
594     {   
595       EiReplyOrRequestPort(ConnectionRequest->Sender,
596                            &CReply->MessageHeader,
597                            LPC_CONNECTION_REFUSED,
598                            NamedPort);
599       KeReleaseSemaphore(&ConnectionRequest->Sender->Semaphore,
600                          IO_NO_INCREMENT,
601                          1,
602                          FALSE);
603       ObDereferenceObject(ConnectionRequest->Sender);
604       ExFreePool(ConnectionRequest);    
605       ExFreePool(CReply);
606       ObDereferenceObject(NamedPort);
607       return (STATUS_SUCCESS);
608     }
609   
610   /*
611    * Prepare the connection.
612    */
613   if (WriteMap != NULL)
614     {
615       PSECTION_OBJECT SectionObject;
616       LARGE_INTEGER SectionOffset;
617
618       Status = ObReferenceObjectByHandle(WriteMap->SectionHandle,
619                                          SECTION_MAP_READ | SECTION_MAP_WRITE,
620                                          MmSectionObjectType,
621                                          UserMode,
622                                          (PVOID*)&SectionObject,
623                                          NULL);
624       if (!NT_SUCCESS(Status))
625         {
626           return(Status);
627         }
628
629       SectionOffset.QuadPart = WriteMap->SectionOffset;
630       WriteMap->TargetViewBase = 0;
631       CReply->ReceiveClientViewSize = WriteMap->ViewSize;
632       Status = MmMapViewOfSection(SectionObject,
633                                   CRequest->ConnectingProcess,
634                                   &WriteMap->TargetViewBase,
635                                   0,
636                                   CReply->ReceiveClientViewSize,
637                                   &SectionOffset,
638                                   &CReply->ReceiveClientViewSize,
639                                   ViewUnmap,
640                                   0 /* MEM_TOP_DOWN? */,
641                                   PAGE_READWRITE);
642       if (!NT_SUCCESS(Status))
643         {
644           return(Status);
645         }      
646
647       WriteMap->ViewBase = 0;
648       Status = MmMapViewOfSection(SectionObject,
649                                   PsGetCurrentProcess(),
650                                   &WriteMap->ViewBase,
651                                   0,
652                                   WriteMap->ViewSize,
653                                   &SectionOffset,
654                                   &WriteMap->ViewSize,
655                                   ViewUnmap,
656                                   0 /* MEM_TOP_DOWN? */,
657                                   PAGE_READWRITE);
658       if (!NT_SUCCESS(Status))
659         {
660           return(Status);
661         }      
662       
663       ObDereferenceObject(SectionObject);
664     }
665   if (ReadMap != NULL && CRequest->SendSectionObject != NULL)
666     {
667       LARGE_INTEGER SectionOffset;
668
669       SectionOffset = CRequest->SendSectionOffset;
670       ReadMap->ViewSize = CRequest->SendViewSize;
671       ReadMap->ViewBase = 0;
672       Status = MmMapViewOfSection(CRequest->SendSectionObject,
673                                   PsGetCurrentProcess(),
674                                   &ReadMap->ViewBase,
675                                   0,
676                                   CRequest->SendViewSize,
677                                   &SectionOffset,
678                                   &CRequest->SendViewSize,
679                                   ViewUnmap,
680                                   0 /* MEM_TOP_DOWN? */,
681                                   PAGE_READWRITE);
682       if (!NT_SUCCESS(Status))
683         {
684           return(Status);
685         }
686     }
687
688   /*
689    * Finish the reply.
690    */
691   if (ReadMap != NULL)
692     {
693       CReply->SendServerViewBase = ReadMap->ViewBase;
694     }
695   else
696     {
697       CReply->SendServerViewBase = 0;
698     }
699   if (WriteMap != NULL)
700     {
701       CReply->ReceiveClientViewBase = WriteMap->TargetViewBase;
702     }
703   CReply->MaximumMessageSize = 0x148;
704
705
706   /*
707    * Connect the two ports
708    */
709   OurPort->OtherPort = ConnectionRequest->Sender;
710   OurPort->OtherPort->OtherPort = OurPort;
711   EiReplyOrRequestPort(ConnectionRequest->Sender,
712                        (PLPC_MESSAGE)CReply,
713                        LPC_REPLY,
714                        OurPort);
715   ExFreePool(ConnectionRequest);
716   ExFreePool(CReply);
717    
718   ObDereferenceObject(OurPort);
719   ObDereferenceObject(NamedPort);
720   
721   return (STATUS_SUCCESS);
722 }
723
724 /**********************************************************************
725  * NAME                                                 EXPORTED
726  *      NtSecureConnectPort@36
727  *      
728  * DESCRIPTION
729  *      Connect to a named port and wait for the other side to 
730  *      accept the connection. Possibly verify that the server
731  *      matches the ServerSid (trusted server).
732  *      Present in w2k+.
733  *
734  * ARGUMENTS
735  *      ConnectedPort
736  *      PortName
737  *      Qos
738  *      WriteMap
739  *      ServerSid
740  *      ReadMap
741  *      MaxMessageSize
742  *      ConnectInfo
743  *      UserConnectInfoLength
744  * 
745  * RETURN VALUE
746  * 
747  */
748 NTSTATUS STDCALL
749 NtSecureConnectPort (OUT    PHANDLE                             ConnectedPort,
750                      IN     PUNICODE_STRING                     PortName,
751                      IN     PSECURITY_QUALITY_OF_SERVICE        Qos,
752                      IN OUT PLPC_SECTION_WRITE                  WriteMap                OPTIONAL,
753                      IN     PSID                                ServerSid               OPTIONAL,
754                      IN OUT PLPC_SECTION_READ                   ReadMap                 OPTIONAL,
755                      OUT    PULONG                              MaxMessageSize          OPTIONAL,
756                      IN OUT PVOID                               ConnectInfo             OPTIONAL,
757                      IN OUT PULONG                              UserConnectInfoLength   OPTIONAL)
758 {
759         return (STATUS_NOT_IMPLEMENTED);
760 }
761
762 /* EOF */