Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1081 lines
34 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. lpccompl.c
  5. Abstract:
  6. Local Inter-Process Communication (LPC) connection system services.
  7. Author:
  8. Steve Wood (stevewo) 15-May-1989
  9. Revision History:
  10. --*/
  11. #include "lpcp.h"
  12. //
  13. // Local procedure prototypes
  14. //
  15. VOID
  16. LpcpPrepareToWakeClient (
  17. IN PETHREAD ClientThread
  18. );
  19. #ifdef ALLOC_PRAGMA
  20. #pragma alloc_text(PAGE,NtAcceptConnectPort)
  21. #pragma alloc_text(PAGE,NtCompleteConnectPort)
  22. #pragma alloc_text(PAGE,LpcpPrepareToWakeClient)
  23. #endif
  24. NTSTATUS
  25. NtAcceptConnectPort (
  26. OUT PHANDLE PortHandle,
  27. IN PVOID PortContext OPTIONAL,
  28. IN PPORT_MESSAGE ConnectionRequest,
  29. IN BOOLEAN AcceptConnection,
  30. IN OUT PPORT_VIEW ServerView OPTIONAL,
  31. OUT PREMOTE_PORT_VIEW ClientView OPTIONAL
  32. )
  33. /*++
  34. Routine Description:
  35. A server process can accept or reject a client connection request
  36. using the NtAcceptConnectPort service.
  37. The ConnectionRequest parameter must specify a connection request
  38. returned by a previous call to the NtListenPort service. This
  39. service will either complete the connection if the AcceptConnection
  40. parameter is TRUE, or reject the connection request if the
  41. AcceptConnection parameter is FALSE.
  42. In either case, the contents of the data portion of the connection
  43. request is the data to return to the caller of NtConnectPort.
  44. If the connection request is accepted, then two communication port
  45. objects will be created and connected together. One will be
  46. inserted in the client process' handle table and returned to the
  47. client via the PortHandle parameter it specified on the
  48. NtConnectPort service. The other will be inserted in the server
  49. process' handle table and returned via the PortHandle parameter
  50. specified on the NtCompleteConnectPort service. In addition the
  51. two communication ports (client and server) will be linked together.
  52. If the connection request is accepted, and the ServerView parameter
  53. was specified, then the section handle is examined. If it is valid,
  54. then the portion of the section described by the SectionOffset and
  55. ViewSize fields will be mapped into both the client and server
  56. process address spaces. The address in server's address space will
  57. be returned in the ViewBase field. The address in the client's
  58. address space will be returned in the ViewRemoteBase field. The
  59. actual offset and size used to map the section will be returned in
  60. the SectionOffset and ViewSize fields.
  61. Communication port objects are temporary objects that have no names
  62. and cannot be inherited. When either the client or server process
  63. calls the !f NtClose service for a communication port, the port will
  64. be deleted since there can never be more than one outstanding handle
  65. for each communication port. The port object type specific delete
  66. procedure will then be invoked. This delete procedure will examine
  67. the communication port, and if it is connected to another
  68. communication port, it will queue an LPC_PORT_CLOSED datagram to
  69. that port's message queue. This will allow both the client and
  70. server processes to notice when a port becomes disconnected, either
  71. because of an explicit call to NtClose or an implicit call due to
  72. process termination. In addition, the delete procedure will scan
  73. the message queue of the port being closed and for each message
  74. still in the queue, it will return an ERROR_PORT_CLOSED status to
  75. any thread that is waiting for a reply to the message.
  76. Arguments:
  77. PortHandle - A pointer to a variable that will receive the server
  78. communication port object handle value.
  79. PortContext - An uninterpreted pointer that is stored in the
  80. server communication port. This pointer is returned whenever
  81. a message is received for this port.
  82. ConnectionRequest - A pointer to a structure that describes the
  83. connection request being accepted or rejected:
  84. The ConnectionRequest structure
  85. ULONG Length - Specifies the size of this data structure in
  86. bytes.
  87. CLIENT_ID ClientId - Specifies a structure that contains the
  88. client identifier (CLIENT_ID) of the thread that sent the
  89. request.
  90. The ClientId Structure
  91. ULONG UniqueProcessId - A unique value for each process
  92. in the system.
  93. ULONG UniqueThreadId - A unique value for each thread in the
  94. system.
  95. ULONG MessageId - A unique value that identifies the connection
  96. request being completed.
  97. ULONG PortAttributes - This field has no meaning for this service.
  98. ULONG ClientViewSize - This field has no meaning for this service.
  99. AcceptConnection - Specifies a boolean value which indicates where
  100. the connection request is being accepted or rejected. A value
  101. of TRUE means that the connection request is accepted and a
  102. server communication port handle will be created and connected
  103. to the client's communication port handle. A value of FALSE
  104. means that the connection request is not accepted.
  105. ServerView - A pointer to a structure that specifies the section that
  106. the server process will use to send messages back to the client
  107. process connected to this port.
  108. The ServerView Structure
  109. ULONG Length - Specifies the size of this data structure in
  110. bytes.
  111. HANDLE SectionHandle - Specifies an open handle to a section
  112. object.
  113. ULONG SectionOffset - Specifies a field that will receive the
  114. actual offset, in bytes, from the start of the section. The
  115. initial value of this parameter specifies the byte offset
  116. within the section that the client's view is based. The
  117. value is rounded down to the next host page size boundary.
  118. ULONG ViewSize - Specifies the size of the view, in bytes.
  119. PVOID ViewBase - Specifies a field that will receive the base
  120. address of the port memory in the server's address space.
  121. PVOID ViewRemoteBase - Specifies a field that will receive
  122. the base address of the server port's memory in the client's
  123. address space. Used to generate pointers that are
  124. meaningful to the client.
  125. ClientView - An optional pointer to a structure that will receive
  126. information about the client process' view in the server's
  127. address space. The server process can use this information
  128. to validate pointers it receives from the client process.
  129. The ClientView Structure
  130. ULONG Length - Specifies the size of this data structure in
  131. bytes.
  132. PVOID ViewBase - Specifies a field that will receive the base
  133. address of the client port's memory in the server's address
  134. space.
  135. ULONG ViewSize - Specifies a field that will receive the
  136. size, in bytes, of the client's view in the server's address
  137. space. If this field is zero, then client has no view in
  138. the server's address space.
  139. Return Value:
  140. NTSTATUS - An appropriate status value.
  141. --*/
  142. {
  143. PLPCP_PORT_OBJECT ConnectionPort;
  144. PLPCP_PORT_OBJECT ServerPort;
  145. PLPCP_PORT_OBJECT ClientPort;
  146. PVOID ClientSectionToMap;
  147. HANDLE Handle;
  148. KPROCESSOR_MODE PreviousMode;
  149. NTSTATUS Status;
  150. ULONG ConnectionInfoLength;
  151. PLPCP_MESSAGE Msg;
  152. PLPCP_CONNECTION_MESSAGE ConnectMsg;
  153. PORT_MESSAGE CapturedReplyMessage;
  154. PVOID SectionToMap;
  155. LARGE_INTEGER SectionOffset;
  156. SIZE_T ViewSize;
  157. PEPROCESS ClientProcess;
  158. PETHREAD ClientThread;
  159. PORT_VIEW CapturedServerView;
  160. PAGED_CODE();
  161. //
  162. // Get previous processor mode and probe output arguments if necessary.
  163. //
  164. PreviousMode = KeGetPreviousMode();
  165. if (PreviousMode != KernelMode) {
  166. try {
  167. ProbeForWriteHandle( PortHandle );
  168. ProbeForReadSmallStructure( ConnectionRequest,
  169. sizeof( *ConnectionRequest ),
  170. sizeof( ULONG ));
  171. CapturedReplyMessage = *ConnectionRequest;
  172. if (ARGUMENT_PRESENT( ServerView )) {
  173. CapturedServerView = ProbeAndReadStructure( ServerView, PORT_VIEW );
  174. if (CapturedServerView.Length != sizeof( *ServerView )) {
  175. return STATUS_INVALID_PARAMETER;
  176. }
  177. ProbeForWriteSmallStructure( ServerView,
  178. sizeof( *ServerView ),
  179. sizeof( ULONG ));
  180. }
  181. if (ARGUMENT_PRESENT( ClientView )) {
  182. if (ProbeAndReadUlong( &ClientView->Length ) != sizeof( *ClientView )) {
  183. return STATUS_INVALID_PARAMETER;
  184. }
  185. ProbeForWriteSmallStructure( ClientView,
  186. sizeof( *ClientView ),
  187. sizeof( ULONG ));
  188. }
  189. } except( EXCEPTION_EXECUTE_HANDLER ) {
  190. return GetExceptionCode();
  191. }
  192. } else {
  193. //
  194. // Otherwise the previous mode is kernel mode
  195. //
  196. CapturedReplyMessage = *ConnectionRequest;
  197. if (ARGUMENT_PRESENT( ServerView )) {
  198. if (ServerView->Length != sizeof( *ServerView )) {
  199. return STATUS_INVALID_PARAMETER;
  200. }
  201. CapturedServerView = *ServerView;
  202. }
  203. if (ARGUMENT_PRESENT( ClientView )) {
  204. if (ClientView->Length != sizeof( *ClientView )) {
  205. return STATUS_INVALID_PARAMETER;
  206. }
  207. }
  208. }
  209. //
  210. // Translate the ClientId from the connection request into a
  211. // thread pointer. This is a referenced pointer to keep the thread
  212. // from evaporating out from under us.
  213. //
  214. Status = PsLookupProcessThreadByCid( &CapturedReplyMessage.ClientId,
  215. &ClientProcess,
  216. &ClientThread );
  217. if (!NT_SUCCESS( Status )) {
  218. return Status;
  219. }
  220. //
  221. // Acquire the mutex that guards the LpcReplyMessage field of
  222. // the thread and get the pointer to the message that the thread
  223. // is waiting for a reply to.
  224. //
  225. LpcpAcquireLpcpLock();
  226. //
  227. // See if the thread is waiting for a reply to the connection request
  228. // specified on this call. If not then a bogus connection request
  229. // has been specified, so release the mutex, dereference the thread
  230. // and return failure.
  231. //
  232. // The check is that the client is waiting for a reply to a connection
  233. // request and that the message id is both valid and lines up correctly
  234. //
  235. if (( LpcpGetThreadMessage( ClientThread ) == NULL ) ||
  236. (CapturedReplyMessage.MessageId == 0) ||
  237. (ClientThread->LpcReplyMessageId != CapturedReplyMessage.MessageId) ||
  238. ((LpcpGetThreadMessage(ClientThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_CONNECTION_REQUEST)) {
  239. Msg = NULL;
  240. } else {
  241. //
  242. // Remember the LPCP message from the thread
  243. //
  244. Msg = LpcpGetThreadMessage(ClientThread);
  245. //
  246. // Get connection message immediately following the LPCP message
  247. //
  248. ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(Msg + 1);
  249. //
  250. // Remember the client port from the connection message
  251. //
  252. ClientPort = ConnectMsg->ClientPort;
  253. //
  254. // Get a pointer to the connection port from the client port.
  255. //
  256. ConnectionPort = ClientPort->ConnectionPort;
  257. //
  258. // Check if the server process accept the connection
  259. //
  260. if ( ConnectionPort->ServerProcess != PsGetCurrentProcess() ) {
  261. //
  262. // Release the LPC mutex
  263. //
  264. LpcpReleaseLpcpLock();
  265. ObDereferenceObject( ClientProcess );
  266. ObDereferenceObject( ClientThread );
  267. return (STATUS_REPLY_MESSAGE_MISMATCH);
  268. }
  269. //
  270. // Remove the LPC message from the thread
  271. //
  272. ClientThread->LpcReplyMessage = NULL;
  273. //
  274. // Remove the client port from the connection message
  275. //
  276. ConnectMsg->ClientPort = NULL;
  277. //
  278. // Clean up the rest of the client thread. This cleanup use to be
  279. // done unconditionally right before releasing the mutex however
  280. // this causes trouble if our caller supplied a bad reply message
  281. // and we clobber an arbitrary threads state
  282. //
  283. ClientThread->LpcReplyMessageId = 0;
  284. }
  285. //
  286. // Release the mutex that guards the field.
  287. //
  288. LpcpReleaseLpcpLock();
  289. //
  290. // Now if we did not get an LPCP message from a client thread then this
  291. // isn't a good call and we'll dereference what we thought was the
  292. // client thread/process and tell our caller their mistake
  293. //
  294. if ( !Msg ) {
  295. LpcpPrint(( "%s Attempted AcceptConnectPort to Thread %lx (%s)\n",
  296. PsGetCurrentProcess()->ImageFileName,
  297. ClientThread,
  298. THREAD_TO_PROCESS( ClientThread )->ImageFileName ));
  299. LpcpPrint(( "failed. MessageId == %u\n", CapturedReplyMessage.MessageId ));
  300. LpcpPrint(( " Thread MessageId == %u\n", ClientThread->LpcReplyMessageId ));
  301. LpcpPrint(( " Thread Msg == %x\n", ClientThread->LpcReplyMessage ));
  302. ObDereferenceObject( ClientProcess );
  303. ObDereferenceObject( ClientThread );
  304. return (STATUS_REPLY_MESSAGE_MISMATCH);
  305. }
  306. //
  307. // At this point we have a good matching client for this accept connect
  308. // call.
  309. //
  310. LpcpTrace(("Replying to Connect Msg %lx to Port %lx\n",
  311. Msg, ClientPort->ConnectionPort ));
  312. //
  313. // Regardless of whether we are accepting or rejecting the connection,
  314. // return the connection information to the waiting thread.
  315. //
  316. ConnectionInfoLength = CapturedReplyMessage.u1.s1.DataLength;
  317. if (ConnectionInfoLength > ConnectionPort->MaxConnectionInfoLength) {
  318. ConnectionInfoLength = ConnectionPort->MaxConnectionInfoLength;
  319. }
  320. Msg->Request.u1.s1.DataLength = (CSHORT)(sizeof( *ConnectMsg ) +
  321. ConnectionInfoLength);
  322. Msg->Request.u1.s1.TotalLength = (CSHORT)(sizeof( *Msg ) +
  323. Msg->Request.u1.s1.DataLength);
  324. Msg->Request.u2.s2.Type = LPC_REPLY;
  325. Msg->Request.u2.s2.DataInfoOffset = 0;
  326. Msg->Request.ClientId = CapturedReplyMessage.ClientId;
  327. Msg->Request.MessageId = CapturedReplyMessage.MessageId;
  328. Msg->Request.ClientViewSize = 0;
  329. try {
  330. RtlCopyMemory( ConnectMsg + 1,
  331. (PCHAR)(ConnectionRequest + 1),
  332. ConnectionInfoLength );
  333. } except( EXCEPTION_EXECUTE_HANDLER ) {
  334. Status = GetExceptionCode();
  335. }
  336. //
  337. // Now it is time to process a positive accept request
  338. //
  339. ClientSectionToMap = NULL;
  340. if (AcceptConnection) {
  341. //
  342. // Allocate and initialize a server communication port object.
  343. // Communication ports have no names, can not be inherited and
  344. // are process private handles.
  345. //
  346. Status = ObCreateObject( PreviousMode,
  347. LpcPortObjectType,
  348. NULL,
  349. PreviousMode,
  350. NULL,
  351. FIELD_OFFSET( LPCP_PORT_OBJECT, WaitEvent ),
  352. 0,
  353. 0,
  354. (PVOID *)&ServerPort );
  355. if (!NT_SUCCESS( Status )) {
  356. goto bailout;
  357. }
  358. RtlZeroMemory( ServerPort, FIELD_OFFSET( LPCP_PORT_OBJECT, WaitEvent ));
  359. ServerPort->PortContext = PortContext;
  360. ServerPort->Flags = SERVER_COMMUNICATION_PORT;
  361. InitializeListHead( &ServerPort->LpcReplyChainHead );
  362. InitializeListHead( &ServerPort->LpcDataInfoChainHead );
  363. //
  364. // Connect the newly created server communication port to the
  365. // connection port with a referenced pointer. Prevents the
  366. // connection port from going away until all of the communication
  367. // ports have been closed.
  368. //
  369. ObReferenceObject( ConnectionPort );
  370. ServerPort->ConnectionPort = ConnectionPort;
  371. ServerPort->MaxMessageLength = ConnectionPort->MaxMessageLength;
  372. //
  373. // Connect the client and server communication ports together
  374. // with unreferenced pointers. They are unreferenced so that
  375. // the PortObjectType delete procedure will get called when a
  376. // communication port is closed. If this were not the case then
  377. // we would need a special NtClosePort system service in order
  378. // to tear down a pair of connected communication ports.
  379. //
  380. ServerPort->ConnectedPort = ClientPort;
  381. ClientPort->ConnectedPort = ServerPort;
  382. ServerPort->Creator = PsGetCurrentThread()->Cid;
  383. ClientPort->Creator = Msg->Request.ClientId;
  384. //
  385. // If the client has allocated a port memory section that is mapped
  386. // into the client's address space, then map a view of the same
  387. // section for the server process to see.
  388. //
  389. LpcpAcquireLpcpLock();
  390. ClientSectionToMap = ConnectMsg->SectionToMap;
  391. ConnectMsg->SectionToMap = NULL;
  392. LpcpReleaseLpcpLock();
  393. if (ClientSectionToMap) {
  394. LARGE_INTEGER LargeSectionOffset;
  395. LargeSectionOffset.LowPart = ConnectMsg->ClientView.SectionOffset;
  396. LargeSectionOffset.HighPart = 0;
  397. Status = MmMapViewOfSection( ClientSectionToMap,
  398. PsGetCurrentProcess(),
  399. &ServerPort->ClientSectionBase,
  400. 0,
  401. 0,
  402. &LargeSectionOffset,
  403. &ConnectMsg->ClientView.ViewSize,
  404. ViewUnmap,
  405. 0,
  406. PAGE_READWRITE );
  407. ConnectMsg->ClientView.SectionOffset = LargeSectionOffset.LowPart;
  408. if (NT_SUCCESS( Status )) {
  409. ConnectMsg->ClientView.ViewRemoteBase = ServerPort->ClientSectionBase;
  410. //
  411. // The client section was mapped. We'll add an extra reference to
  412. // server process. This reference will be removed on port cleanup.
  413. //
  414. ServerPort->MappingProcess = PsGetCurrentProcess();
  415. ObReferenceObject( ServerPort->MappingProcess );
  416. } else {
  417. //
  418. // At this point we're really going to drop all the way
  419. // out to the label bailout: because everything else is
  420. // protected with a test against Status. But first we have
  421. // to release the server port that we've just created
  422. //
  423. ObDereferenceObject( ServerPort );
  424. }
  425. }
  426. //
  427. // If the server process has allocated a port memory section for
  428. // send data to the client on call back requests, map two views
  429. // of that section, the first for the server process and the
  430. // second view for the client process. Return the location of the
  431. // server's view to the caller of this function. Return the
  432. // client's view to the client process via the reply to the
  433. // connection request.
  434. //
  435. if (NT_SUCCESS( Status ) && ARGUMENT_PRESENT( ServerView )) {
  436. LARGE_INTEGER LargeSectionOffset;
  437. LargeSectionOffset.LowPart = CapturedServerView.SectionOffset;
  438. LargeSectionOffset.HighPart = 0;
  439. //
  440. // Map in the section into the servers address space
  441. //
  442. //
  443. // Does this call need to verify that the section handle
  444. // is still valid.
  445. //
  446. Status = ObReferenceObjectByHandle( CapturedServerView.SectionHandle,
  447. SECTION_MAP_READ |
  448. SECTION_MAP_WRITE,
  449. MmSectionObjectType,
  450. PreviousMode,
  451. (PVOID *)&SectionToMap,
  452. NULL );
  453. if (NT_SUCCESS( Status )) {
  454. Status = MmMapViewOfSection( SectionToMap,
  455. PsGetCurrentProcess(),
  456. &ServerPort->ServerSectionBase,
  457. 0,
  458. 0,
  459. &LargeSectionOffset,
  460. &CapturedServerView.ViewSize,
  461. ViewUnmap,
  462. 0,
  463. PAGE_READWRITE );
  464. if (NT_SUCCESS( Status )) {
  465. //
  466. // The section was mapped into the server process. We'll add a
  467. // reference to the server process only if we didn't before.
  468. //
  469. if ( ServerPort->MappingProcess == NULL ) {
  470. ServerPort->MappingProcess = PsGetCurrentProcess();
  471. ObReferenceObject( ServerPort->MappingProcess );
  472. }
  473. CapturedServerView.SectionOffset = LargeSectionOffset.LowPart;
  474. CapturedServerView.ViewBase = ServerPort->ServerSectionBase;
  475. SectionOffset.LowPart = CapturedServerView.SectionOffset;
  476. SectionOffset.HighPart = 0;
  477. ViewSize = CapturedServerView.ViewSize;
  478. Status = MmMapViewOfSection( SectionToMap,
  479. ClientProcess,
  480. &ClientPort->ServerSectionBase,
  481. 0,
  482. 0,
  483. &SectionOffset,
  484. &ViewSize,
  485. ViewUnmap,
  486. 0,
  487. PAGE_READWRITE );
  488. if (NT_SUCCESS( Status )) {
  489. //
  490. // The section was mapped into the client process. We'll add a
  491. // reference to the client process only we didn't before.
  492. // (we don't have also a client section)
  493. //
  494. if ( ClientPort->MappingProcess == NULL ) {
  495. ClientPort->MappingProcess = ClientProcess;
  496. ObReferenceObject( ClientProcess );
  497. }
  498. //
  499. // Let the server know where the client's view of the
  500. // section got mapped
  501. //
  502. CapturedServerView.ViewRemoteBase = ClientPort->ServerSectionBase;
  503. //
  504. // Let the client know where the server's view of the
  505. // section got mapped
  506. //
  507. ConnectMsg->ServerView.ViewBase = ClientPort->ServerSectionBase;
  508. ConnectMsg->ServerView.ViewSize = ViewSize;
  509. } else {
  510. ObDereferenceObject( ServerPort );
  511. }
  512. } else {
  513. ObDereferenceObject( ServerPort );
  514. }
  515. ObDereferenceObject( SectionToMap );
  516. } else {
  517. ObDereferenceObject( ServerPort );
  518. }
  519. }
  520. //
  521. // Insert the server communication port object in specified object
  522. // table. Set port handle value if successful. If not
  523. // successful, then the port will have been dereferenced, which
  524. // will cause it to be freed, after our delete procedure is
  525. // called. The delete procedure will undo the work done to
  526. // initialize the port.
  527. //
  528. if (NT_SUCCESS( Status )) {
  529. //
  530. // Add an extra reference to the object otherwise right when we
  531. // create the handle a rouge caller might close and destroy the
  532. // port.
  533. //
  534. ObReferenceObject( ServerPort );
  535. //
  536. // Now add the handle
  537. //
  538. Status = ObInsertObject( ServerPort,
  539. NULL,
  540. PORT_ALL_ACCESS,
  541. 0,
  542. (PVOID *)NULL,
  543. &Handle );
  544. if (NT_SUCCESS( Status )) {
  545. try {
  546. if (ARGUMENT_PRESENT( ServerView )) {
  547. *ServerView = CapturedServerView;
  548. }
  549. if (ARGUMENT_PRESENT( ClientView )) {
  550. ClientView->ViewBase = ConnectMsg->ClientView.ViewRemoteBase;
  551. ClientView->ViewSize = ConnectMsg->ClientView.ViewSize;
  552. }
  553. *PortHandle = Handle;
  554. if (!ARGUMENT_PRESENT( PortContext )) {
  555. ServerPort->PortContext = Handle;
  556. }
  557. ServerPort->ClientThread = ClientThread;
  558. LpcpAcquireLpcpLock();
  559. ClientThread->LpcReplyMessage = Msg;
  560. LpcpReleaseLpcpLock();
  561. ClientThread = NULL;
  562. } except( EXCEPTION_EXECUTE_HANDLER ) {
  563. NtClose( Handle );
  564. Status = GetExceptionCode();
  565. }
  566. }
  567. //
  568. // Now we can remove the extra object reference
  569. //
  570. ObDereferenceObject( ServerPort );
  571. }
  572. } else {
  573. //
  574. // Otherwise the server has not accepted the connection request
  575. //
  576. LpcpPrint(( "Refusing connection from %x.%x\n",
  577. Msg->Request.ClientId.UniqueProcess,
  578. Msg->Request.ClientId.UniqueThread ));
  579. }
  580. bailout:
  581. if ( ClientSectionToMap ) {
  582. ObDereferenceObject( ClientSectionToMap );
  583. }
  584. //
  585. // If the client is not null then this is an error condition and we need
  586. // to cleanup and wake the client thread. In success cases the client
  587. // thread is woken up with a call to Complete Connect Request
  588. //
  589. if (ClientThread != NULL) {
  590. LpcpAcquireLpcpLock();
  591. ClientThread->LpcReplyMessage = Msg;
  592. if (AcceptConnection) {
  593. LpcpPrint(( "LPC: Failing AcceptConnection with Status == %x\n", Status ));
  594. }
  595. LpcpPrepareToWakeClient( ClientThread );
  596. LpcpReleaseLpcpLock();
  597. //
  598. // Wake up the thread that is waiting for an answer to its connection
  599. // request inside of NtConnectPort.
  600. //
  601. KeReleaseSemaphore( &ClientThread->LpcReplySemaphore,
  602. 0,
  603. 1L,
  604. FALSE );
  605. //
  606. // Dereference client thread and return the system service status.
  607. //
  608. ObDereferenceObject( ClientThread );
  609. }
  610. if (ClientPort) {
  611. ObDereferenceObject( ClientPort );
  612. }
  613. ObDereferenceObject( ClientProcess );
  614. //
  615. // And return to our caller
  616. //
  617. return Status;
  618. }
  619. NTSTATUS
  620. NtCompleteConnectPort (
  621. IN HANDLE PortHandle
  622. )
  623. /*++
  624. Routine Description:
  625. This routine is called by the server after it calls NtAcceptConnectPort to
  626. wake up the client thread. Between calling NtAcceptConnectPort and
  627. NtCompleteConnectPort the server can do whatever work is necessary before
  628. waking up the client
  629. Arguments:
  630. PortHandle - Supplies a handle to the server communication port
  631. Return Value:
  632. NTSTATUS - An appropriate status value.
  633. --*/
  634. {
  635. PLPCP_PORT_OBJECT PortObject;
  636. KPROCESSOR_MODE PreviousMode;
  637. NTSTATUS Status;
  638. PETHREAD ClientThread;
  639. PAGED_CODE();
  640. //
  641. // Get previous processor mode
  642. //
  643. PreviousMode = KeGetPreviousMode();
  644. //
  645. // Reference the port object by handle
  646. //
  647. Status = LpcpReferencePortObject( PortHandle,
  648. 0,
  649. PreviousMode,
  650. &PortObject );
  651. if (!NT_SUCCESS( Status )) {
  652. return Status;
  653. }
  654. //
  655. // Error if a port type is invalid.
  656. //
  657. if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
  658. ObDereferenceObject( PortObject );
  659. return STATUS_INVALID_PORT_HANDLE;
  660. }
  661. //
  662. // Under the LPC lock we need to check for a client thread and if there
  663. // is one we'll remember and remove the client thread, and then prepare
  664. // to wake the client
  665. //
  666. LpcpAcquireLpcpLock();
  667. if (PortObject->ClientThread == NULL) {
  668. LpcpReleaseLpcpLock();
  669. ObDereferenceObject( PortObject );
  670. return STATUS_INVALID_PARAMETER;
  671. }
  672. ClientThread = PortObject->ClientThread;
  673. //
  674. // Double check that the thread is still waiting for a reply message
  675. //
  676. if (LpcpGetThreadMessage(ClientThread) == NULL) {
  677. LpcpReleaseLpcpLock();
  678. ObDereferenceObject( PortObject );
  679. //
  680. // At this point the client has already been woken. We will get a client died message
  681. //
  682. return STATUS_SUCCESS;
  683. }
  684. //
  685. // The check needs to ensure that the client thread is really on the
  686. // reply chain for the sever's connection port. This is a quick and
  687. // dirty fix for NT 5.0. We zoom down the connection port lpc reply
  688. // chain looking for an entry that contains the client threads. If
  689. // we find a match it's okay if we don't it's bad.
  690. //
  691. if (PortObject->ConnectionPort) {
  692. PLIST_ENTRY Entry;
  693. for (Entry = PortObject->ConnectionPort->LpcReplyChainHead.Flink;
  694. Entry != (PLIST_ENTRY)(&PortObject->ConnectionPort->LpcReplyChainHead.Flink);
  695. Entry = Entry->Flink) {
  696. if (Entry == ((PLIST_ENTRY)(&ClientThread->LpcReplyChain.Flink))) {
  697. break;
  698. }
  699. }
  700. if (Entry != ((PLIST_ENTRY)(&ClientThread->LpcReplyChain.Flink))) {
  701. LpcpReleaseLpcpLock();
  702. ObDereferenceObject( PortObject );
  703. //
  704. // At this point the client has already been woken. We will get a client died message
  705. //
  706. return STATUS_SUCCESS;
  707. }
  708. }
  709. //
  710. // Now do the wakeup
  711. //
  712. PortObject->ClientThread = NULL;
  713. LpcpPrepareToWakeClient( ClientThread );
  714. LpcpReleaseLpcpLock();
  715. //
  716. // Wake up the thread that is waiting for an answer to its connection
  717. // request inside of NtConnectPort.
  718. //
  719. KeReleaseSemaphore( &ClientThread->LpcReplySemaphore,
  720. 0,
  721. 1L,
  722. FALSE );
  723. //
  724. // Dereference client thread
  725. //
  726. ObDereferenceObject( ClientThread );
  727. ObDereferenceObject( PortObject );
  728. //
  729. // And return to our caller
  730. //
  731. return Status;
  732. }
  733. //
  734. // Local support routine
  735. //
  736. VOID
  737. LpcpPrepareToWakeClient (
  738. IN PETHREAD ClientThread
  739. )
  740. /*++
  741. Routine Description:
  742. This routine is used to prepare the client thread to receive a reply to
  743. its connection request
  744. Arguments:
  745. ClientThread - Specifies the thread we are preparing to wake up
  746. Return Value:
  747. None.
  748. --*/
  749. {
  750. PAGED_CODE();
  751. //
  752. // Remove the thread from the rundown list the connection port as we are
  753. // sending a reply. The operation only needs to take place if the
  754. // thread isn't exiting and it's in the lpc reply chain for a connection
  755. // port
  756. //
  757. if ((!ClientThread->LpcExitThreadCalled) &&
  758. (!IsListEmpty( &ClientThread->LpcReplyChain ))) {
  759. RemoveEntryList( &ClientThread->LpcReplyChain );
  760. InitializeListHead( &ClientThread->LpcReplyChain );
  761. }
  762. //
  763. // And return to our caller
  764. //
  765. return;
  766. }