Source code of Windows XP (NT5)
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.

1201 lines
31 KiB

  1. /******************************************************************************\
  2. * This is a part of the Microsoft Source Code Samples.
  3. * Copyright 1995 - 1997 Microsoft Corporation.
  4. * All rights reserved.
  5. * This source code is only intended as a supplement to
  6. * Microsoft Development Tools and/or WinHelp documentation.
  7. * See these sources for detailed information regarding the
  8. * Microsoft samples programs.
  9. \******************************************************************************/
  10. /*++
  11. Copyright (c) 1997 Microsoft Corporation
  12. Module Name:
  13. SrvMain.c
  14. Abstract:
  15. The server component of Remote. It spawns a child process
  16. and redirects the stdin/stdout/stderr of child to itself.
  17. Waits for connections from clients - passing the
  18. output of child process to client and the input from clients
  19. to child process.
  20. This version uses overlapped I/O to do in one thread what
  21. the original uses 9 for. Almost. Because there is no way to
  22. get overlapped stdin/stdout handles, two threads sit around
  23. doing blocking I/O on stdin and stdout. 3 is better than 9.
  24. Unfortunately there's no CreatePipe()
  25. or equivalent option to open an overlapped handle to an anonymous
  26. pipe, so I stole the source for NT CreatePipe and hacked it to
  27. accept flags indicating overlapped for one or both ends of the
  28. anonymous pipe. In our usage the child end handles are not
  29. overlapped but the server end handles are.
  30. Author:
  31. Dave Hart 30 May 1997 after Server.c by
  32. Rajivendra Nath 2-Jan-1992
  33. Environment:
  34. Console App. User mode.
  35. Revision History:
  36. --*/
  37. #include <windows.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <process.h>
  41. #include <io.h>
  42. #include <string.h>
  43. #if DBG
  44. #undef NDEBUG // so asserts work on chk builds
  45. #endif
  46. #include "Remote.h"
  47. #define SERVER_H_NOEXTERN
  48. #include "Server.h"
  49. DWORD cbRemoteClient = sizeof(REMOTE_CLIENT); // for debugging
  50. /*************************************************************/
  51. int
  52. OverlappedServer( //Main routine for server.
  53. char* pszChildCmd,
  54. char* pszPipeNameArg
  55. )
  56. {
  57. int i;
  58. BOOL b;
  59. DWORD cWait;
  60. DWORD dwWait;
  61. PREMOTE_CLIENT pClientRemove;
  62. #if DBG
  63. // Trace = -1; // all TR_ bits on (and then some)
  64. #endif
  65. //
  66. // Initialize globals
  67. //
  68. pszPipeName = pszPipeNameArg;
  69. dwNextClientID = 1; // local client will be 1
  70. cConnectIns = CONNECT_COUNT;
  71. cWait = MAX_WAIT_HANDLES;
  72. hHeap = HeapCreate(
  73. 0,
  74. 3 * sizeof(REMOTE_CLIENT), // initial size
  75. 3000 * sizeof(REMOTE_CLIENT) // max
  76. );
  77. OsVersionInfo.dwOSVersionInfoSize = sizeof OsVersionInfo;
  78. b = GetVersionEx(&OsVersionInfo);
  79. ASSERT( b );
  80. printf("**************************************\n");
  81. printf("*********** REMOTE ************\n");
  82. printf("*********** SERVER ************\n");
  83. printf("**************************************\n");
  84. fflush(stdout);
  85. //
  86. // Setup the ACLs we need, taking into account any /u switches
  87. //
  88. SetupSecurityDescriptors();
  89. printf("To Connect: Remote /C %s \"%s\"\n\n", HostName, pszPipeName);
  90. fflush(stdout);
  91. //
  92. // runtime link to NT-only kernel32 APIs so we can
  93. // load on Win95 for client use.
  94. //
  95. RuntimeLinkAPIs();
  96. //
  97. // Setup our three lists of clients: handshaking,
  98. // connected, and closing/closed.
  99. //
  100. InitializeClientLists();
  101. //
  102. // set _REMOTE environment variable to the pipe name (why?)
  103. //
  104. SetEnvironmentVariable("_REMOTE", pszPipeName);
  105. //
  106. // Create a tempfile for storing Child process output.
  107. //
  108. {
  109. char szTempDirectory[MAX_PATH + 1];
  110. GetTempPath(sizeof(szTempDirectory), szTempDirectory);
  111. //
  112. // Before we litter the temp directory with more REMnnn.TMP
  113. // files, let's delete all the orphaned ones we can. This
  114. // will fail for temp files open by other remote servers.
  115. //
  116. CleanupTempFiles(szTempDirectory);
  117. GetTempFileName(szTempDirectory, "REM", 0, SaveFileName);
  118. }
  119. if ( ! (hWriteTempFile =
  120. CreateFile(
  121. SaveFileName, /* name of the file */
  122. GENERIC_READ | GENERIC_WRITE, /* access (read/write) mode */
  123. FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
  124. NULL, /* security descriptor */
  125. CREATE_ALWAYS, /* how to create */
  126. FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL,
  127. NULL
  128. ))) {
  129. ErrorExit("Could not Create Temp File");
  130. }
  131. //
  132. // We don't want to have multiple IN pipes created and
  133. // awaiting connection simultaneously if there are
  134. // multiple remote server processes sharing different
  135. // sessions under the same pipe name. This would be
  136. // hairy for several reasons including breaking the
  137. // current round-robin behavior of connections, since
  138. // the oldest server pipe is connected first. So
  139. // we create/open a named event based on the pipe name and
  140. // set the event so that any other remote servers on the
  141. // same pipe will fall back to a single IN pipe listening.
  142. //
  143. {
  144. char szPerPipeEventName[1024];
  145. sprintf(
  146. szPerPipeEventName,
  147. "MSRemoteSrv%s",
  148. pszPipeName
  149. );
  150. rghWait[WAITIDX_PER_PIPE_EVENT] =
  151. CreateEvent(
  152. &saPublic, // security
  153. TRUE, // manual reset (synchronization)
  154. FALSE, // initially nonsignaled
  155. szPerPipeEventName
  156. );
  157. if (! rghWait[WAITIDX_PER_PIPE_EVENT]) {
  158. ErrorExit("Unable to create per-pipe event.");
  159. }
  160. if (ERROR_ALREADY_EXISTS == GetLastError()) {
  161. TRACE(CONNECT, ("Found previous server on '%s', using 1 listening pipe.\n", pszPipeName));
  162. SetEvent(rghWait[WAITIDX_PER_PIPE_EVENT]);
  163. for (i = 1; i < (int) cConnectIns; i++) {
  164. rghPipeIn[i] = INVALID_HANDLE_VALUE;
  165. }
  166. cWait = MAX_WAIT_HANDLES - cConnectIns + 1;
  167. cConnectIns = 1;
  168. //
  169. // We don't want to wait on the event handle, but it's easier
  170. // to have a handle in its slot, so dupe a handle to our own
  171. // process. Note we toss the value of the created event handle
  172. // without closing it -- we want it to stay around but we're
  173. // done with it.
  174. //
  175. DuplicateHandle(
  176. GetCurrentProcess(),
  177. GetCurrentProcess(),
  178. GetCurrentProcess(),
  179. &rghWait[WAITIDX_PER_PIPE_EVENT],
  180. 0,
  181. FALSE,
  182. DUPLICATE_SAME_ACCESS
  183. );
  184. }
  185. }
  186. //
  187. // Create the event for the OVERLAPPED structure
  188. // used by the main server thread for WriteFileSynch calls.
  189. //
  190. olMainThread.hEvent =
  191. CreateEvent(
  192. NULL, // security
  193. TRUE, // auto-reset
  194. FALSE, // initially nonsignaled
  195. NULL // unnamed
  196. );
  197. //
  198. // Create the events for the OVERLAPPED structures
  199. // used for ConnectNamedPipe operations.
  200. //
  201. olConnectOut.hEvent =
  202. rghWait[WAITIDX_CONNECT_OUT] =
  203. CreateEvent(
  204. NULL, // security
  205. TRUE, // manual reset as ConnectNamedPipe demands
  206. FALSE, // initially nonsignaled
  207. NULL
  208. );
  209. for (i = 0;
  210. i < (int) cConnectIns;
  211. i++) {
  212. rgolConnectIn[i].hEvent =
  213. rghWait[WAITIDX_CONNECT_IN_BASE + i] =
  214. CreateEvent(
  215. NULL, // security
  216. TRUE, // manual reset as ConnectNamedPipe demands
  217. FALSE, // initially nonsignaled
  218. NULL
  219. );
  220. }
  221. //
  222. // Create a timer we'll use to detect 2-pipe clients connected to
  223. // OUT without ever connecting to IN so we can recycle our single
  224. // OUT instance and allow other two-pipe clients in again.
  225. // NT 3.51 doesn't have waitable timers, so we don't do that
  226. // error handling on that OS. Same as old remote.exe.
  227. //
  228. if (pfnCreateWaitableTimer) {
  229. hConnectOutTimer =
  230. pfnCreateWaitableTimer(
  231. NULL, // security
  232. FALSE, // bManualReset, we want auto-reset
  233. NULL // unnamed
  234. );
  235. } else {
  236. hConnectOutTimer = INVALID_HANDLE_VALUE;
  237. }
  238. //
  239. // Start the command as a child process
  240. //
  241. if (hAttachedProcess != INVALID_HANDLE_VALUE) {
  242. ChldProc = hAttachedProcess;
  243. hWriteChildStdIn = hAttachedWriteChildStdIn;
  244. hReadChildOutput = hAttachedReadChildStdOut;
  245. } else {
  246. ChldProc =
  247. ForkChildProcess(
  248. ChildCmd,
  249. &hWriteChildStdIn,
  250. &hReadChildOutput
  251. );
  252. }
  253. rghWait[WAITIDX_CHILD_PROCESS] = ChldProc;
  254. //
  255. // Set ^c/^break handler. It will kill the child process on
  256. // ^break and pass ^c through to it.
  257. //
  258. SetConsoleCtrlHandler(SrvCtrlHand, TRUE);
  259. //
  260. // Setup local session and start first read against its input.
  261. // This starts a chain of completion routines that continues
  262. // until this server exits.
  263. //
  264. StartLocalSession();
  265. //
  266. // Start a read operation on the child output pipe.
  267. // This starts a chain of completion routines that continues
  268. // until the child terminates.
  269. //
  270. StartChildOutPipeRead();
  271. //
  272. // Start several async ConnectNamedPipe operations, to reduce the chance
  273. // of a client getting pipe busy errors. Since there is no
  274. // completion port version of ConnectNamedPipe, we'll wait on the
  275. // events in the main loop below that indicate completion.
  276. //
  277. CreatePipeAndIssueConnect(OUT_PIPE);
  278. for (i = 0;
  279. i < (int) cConnectIns;
  280. i++) {
  281. CreatePipeAndIssueConnect(i);
  282. }
  283. InitAd(IsAdvertise);
  284. //
  285. // We may need to service the query pipe for remote /q clients.
  286. //
  287. InitializeQueryServer();
  288. //
  289. // main loop of thread, waits for ConnectNamedPipe completions
  290. // and handles them while remaining alertable for completion
  291. // routines to get called.
  292. //
  293. while (1) {
  294. dwWait =
  295. WaitForMultipleObjectsEx(
  296. cWait,
  297. rghWait,
  298. FALSE, // wait on any handle, not all
  299. 30 * 1000, // ms
  300. TRUE // alertable (completion routines)
  301. );
  302. if (WAIT_IO_COMPLETION == dwWait) {
  303. //
  304. // A completion routine was called.
  305. //
  306. continue;
  307. }
  308. if (WAIT_TIMEOUT == dwWait) {
  309. //
  310. // Presumably since we've timed out for 30 seconds
  311. // with no IO completion, closing clients have
  312. // finished any pending IOs and the memory can be
  313. // released.
  314. //
  315. while (pClientRemove = RemoveFirstClientFromClosingList()) {
  316. HeapFree(hHeap, 0, pClientRemove);
  317. }
  318. continue;
  319. }
  320. if (WAITIDX_CONNECT_OUT == dwWait) {
  321. HandleOutPipeConnected();
  322. continue;
  323. }
  324. if (WAITIDX_CONNECT_IN_BASE <= dwWait &&
  325. (WAITIDX_CONNECT_IN_BASE + CONNECT_COUNT) > dwWait) {
  326. HandleInPipeConnected( dwWait - WAITIDX_CONNECT_IN_BASE );
  327. continue;
  328. }
  329. if (WAITIDX_QUERYSRV_WAIT == dwWait ||
  330. WAITIDX_QUERYSRV_WAIT + WAIT_ABANDONED_0 == dwWait ) {
  331. //
  332. // The remote server which was handling the query pipe
  333. // has gone away. We'll try to take over.
  334. //
  335. QueryWaitCompleted();
  336. continue;
  337. }
  338. if (WAITIDX_PER_PIPE_EVENT == dwWait) {
  339. //
  340. // Another server is starting on this same
  341. // pipename. To be most compatible we need
  342. // to fall back to listening on only one
  343. // IN pipe instance.
  344. //
  345. if (1 != cConnectIns) {
  346. TRACE(CONNECT,
  347. ("Another server starting on '%s', falling back to 1 IN listening pipe.\n",
  348. pszPipeName
  349. ));
  350. for (i = 1; i < (int) cConnectIns; i++) {
  351. CANCELIO( rghPipeIn[i] );
  352. DisconnectNamedPipe( rghPipeIn[i] );
  353. CloseHandle( rghPipeIn[i] );
  354. rghPipeIn[i] = INVALID_HANDLE_VALUE;
  355. }
  356. cWait = MAX_WAIT_HANDLES - cConnectIns + 1;
  357. cConnectIns = 1;
  358. //
  359. // We don't want to wait on the event handle, but it's easier
  360. // to have a handle in its slot, so dupe a handle to our own
  361. // process. We toss the event handle without closing it so
  362. // it will stay around for future remote servers on the same
  363. // pipe name.
  364. //
  365. DuplicateHandle(
  366. GetCurrentProcess(),
  367. GetCurrentProcess(),
  368. GetCurrentProcess(),
  369. &rghWait[WAITIDX_PER_PIPE_EVENT],
  370. 0,
  371. FALSE,
  372. DUPLICATE_SAME_ACCESS
  373. );
  374. }
  375. continue;
  376. }
  377. if (WAITIDX_CHILD_PROCESS == dwWait ||
  378. WAITIDX_READ_STDIN_DONE == dwWait) {
  379. if (INVALID_HANDLE_VALUE != hConnectOutTimer) {
  380. CloseHandle(hConnectOutTimer);
  381. hConnectOutTimer = INVALID_HANDLE_VALUE;
  382. }
  383. //
  384. // Cancel ConnectNamedPipe operations and close
  385. // the pipes
  386. //
  387. if (INVALID_HANDLE_VALUE != hPipeOut) {
  388. DisconnectNamedPipe( hPipeOut );
  389. CANCELIO( hPipeOut );
  390. CloseHandle( rghWait[WAITIDX_CONNECT_OUT] );
  391. rghWait[WAITIDX_CONNECT_OUT] = INVALID_HANDLE_VALUE;
  392. }
  393. for (i = 0;
  394. i < (int) cConnectIns;
  395. i++) {
  396. if (INVALID_HANDLE_VALUE != rghPipeIn[i]) {
  397. TRACE(CONNECT, ("Tearing down listening IN pipe #%d.\n", i + 1));
  398. DisconnectNamedPipe( rghPipeIn[i] );
  399. CANCELIO( rghPipeIn[i] );
  400. CloseHandle( rghPipeIn[i] );
  401. rghPipeIn[i] = INVALID_HANDLE_VALUE;
  402. }
  403. }
  404. //
  405. // Cancel read against child process in/out pipes
  406. //
  407. if (INVALID_HANDLE_VALUE != hWriteChildStdIn) {
  408. CANCELIO( hWriteChildStdIn );
  409. CloseHandle( hWriteChildStdIn );
  410. hWriteChildStdIn = INVALID_HANDLE_VALUE;
  411. }
  412. if (INVALID_HANDLE_VALUE != hReadChildOutput) {
  413. CANCELIO( hReadChildOutput );
  414. CloseHandle( hReadChildOutput );
  415. hReadChildOutput = INVALID_HANDLE_VALUE;
  416. }
  417. //
  418. // Cancel client I/Os
  419. //
  420. bShuttingDownServer = TRUE;
  421. //
  422. // Note that CloseClient will remove entries from this list,
  423. // so we walk it starting at the head at each step.
  424. //
  425. for (pClientRemove = (PREMOTE_CLIENT) ClientListHead.Flink;
  426. pClientRemove != (PREMOTE_CLIENT) &ClientListHead;
  427. pClientRemove = (PREMOTE_CLIENT) ClientListHead.Flink ) {
  428. CloseClient(pClientRemove);
  429. }
  430. //
  431. // on our way out...
  432. //
  433. break;
  434. }
  435. //
  436. // Unexpected WaitForMulipleObjectsEx return
  437. //
  438. printf("Remote: unknown wait return %d\n", dwWait);
  439. ErrorExit("fix srvmain.c");
  440. } // endless loop
  441. ShutAd(IsAdvertise);
  442. while (i = 0, GetExitCodeProcess(ChldProc, &i) &&
  443. STILL_ACTIVE == i) {
  444. printf("\nRemote: Waiting for child to exit.\n");
  445. WaitForSingleObjectEx(ChldProc, 10 * 1000, TRUE);
  446. }
  447. //
  448. // For some interesting reason when we're attached to
  449. // a debugger like ntsd and it exits, our printf
  450. // below comes out *after* the cmd.exe prompt, making
  451. // it look like we hung on exit even though cmd.exe is
  452. // patiently awaiting a command. So suppress it.
  453. //
  454. if (hAttachedProcess == INVALID_HANDLE_VALUE) {
  455. printf("\nRemote exiting. Child (%s) exit code was %d.\n", ChildCmd, i);
  456. }
  457. CANCELIO(hWriteTempFile);
  458. CloseHandle(hWriteTempFile);
  459. hWriteTempFile = INVALID_HANDLE_VALUE;
  460. //
  461. // Flush any pending completion routines.
  462. //
  463. while (WAIT_IO_COMPLETION == SleepEx(50, TRUE)) {
  464. ;
  465. }
  466. if (!DeleteFile(SaveFileName)) {
  467. printf("Remote: Temp File %s not deleted..\n",SaveFileName);
  468. }
  469. return i;
  470. }
  471. VOID
  472. FASTCALL
  473. StartLocalSession(
  474. VOID
  475. )
  476. {
  477. DWORD dwThreadId;
  478. char szHexAsciiId[9];
  479. pLocalClient = HeapAlloc(
  480. hHeap,
  481. HEAP_ZERO_MEMORY,
  482. sizeof(*pLocalClient)
  483. );
  484. if (!pLocalClient) {
  485. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  486. ErrorExit("Unable to allocate local client.");
  487. }
  488. pLocalClient->dwID = dwNextClientID++;
  489. sprintf(szHexAsciiId, "%08x", pLocalClient->dwID);
  490. CopyMemory(pLocalClient->HexAsciiId, szHexAsciiId, sizeof(pLocalClient->HexAsciiId));
  491. strcpy(pLocalClient->Name, "Local");
  492. pLocalClient->ServerFlags = SFLG_LOCAL;
  493. //
  494. // we need overlapped handles to stdin/stdout,
  495. // and woefully DuplicateHandle can't do it.
  496. // So we'll create two anonymous pipes and two
  497. // threads to shuffle data between stdin/stdout
  498. // and the pipes. The server end of the pipes
  499. // is opened overlapped, the "client" end (used
  500. // by the threads) is not overlapped.
  501. //
  502. rgCopyPipe[0].hRead = GetStdHandle(STD_INPUT_HANDLE);
  503. if ( ! MyCreatePipeEx(&pLocalClient->PipeReadH, &rgCopyPipe[0].hWrite, NULL, 0, FILE_FLAG_OVERLAPPED, 0)) {
  504. ErrorExit("Cannot create local input pipe");
  505. }
  506. rgCopyPipe[1].hWrite = GetStdHandle(STD_OUTPUT_HANDLE);
  507. if ( ! MyCreatePipeEx(&rgCopyPipe[1].hRead, &pLocalClient->PipeWriteH, NULL, 0, 0, FILE_FLAG_OVERLAPPED)) {
  508. ErrorExit("Cannot create local output pipe");
  509. }
  510. rghWait[WAITIDX_READ_STDIN_DONE] = (HANDLE)
  511. _beginthreadex(
  512. NULL, // security
  513. 0, // default stack size
  514. CopyPipeToPipe, // proc
  515. (LPVOID) &rgCopyPipe[0], // parm
  516. 0, // flags
  517. &dwThreadId
  518. );
  519. CloseHandle( (HANDLE)
  520. _beginthreadex(
  521. NULL, // security
  522. 0, // default stack size
  523. CopyPipeToPipe, // proc
  524. (LPVOID) &rgCopyPipe[1], // parm
  525. 0, // flags
  526. &dwThreadId
  527. )
  528. );
  529. StartSession( pLocalClient );
  530. }
  531. //
  532. // Two of these threads to deal with non-overlapped stdin/stdout.
  533. // CRT is OK.
  534. //
  535. DWORD
  536. WINAPI
  537. CopyPipeToPipe(
  538. LPVOID lpCopyPipeData
  539. )
  540. {
  541. PCOPYPIPE psd = (PCOPYPIPE) lpCopyPipeData;
  542. DWORD cb;
  543. char achBuffer[BUFFSIZE];
  544. while (1) {
  545. if ( ! ReadFile(
  546. psd->hRead,
  547. achBuffer,
  548. sizeof(achBuffer),
  549. &cb,
  550. NULL
  551. )) {
  552. TRACE(COPYPIPE, ("CopyPipeToPipe ReadFile %s failed, exiting thread.\n",
  553. (psd == &rgCopyPipe[0])
  554. ? "stdin"
  555. : "local client output pipe"));
  556. break;
  557. }
  558. if ( ! WriteFile(
  559. psd->hWrite,
  560. achBuffer,
  561. cb,
  562. &cb,
  563. NULL
  564. )) {
  565. TRACE(COPYPIPE, ("CopyPipeToPipe WriteFile %s failed, exiting thread.\n",
  566. (psd == &rgCopyPipe[0])
  567. ? "local client input pipe"
  568. : "stdout"));
  569. break;
  570. }
  571. }
  572. return 0;
  573. }
  574. VOID
  575. FASTCALL
  576. StartSession(
  577. PREMOTE_CLIENT pClient
  578. )
  579. {
  580. pClient->rSaveFile =
  581. CreateFile(
  582. SaveFileName,
  583. GENERIC_READ,
  584. FILE_SHARE_READ | FILE_SHARE_WRITE,
  585. NULL,
  586. OPEN_EXISTING,
  587. FILE_FLAG_OVERLAPPED,
  588. NULL
  589. );
  590. if ( ! pClient->rSaveFile) {
  591. printf("Remote:Cannot open ReadHandle to temp file:%d\n",GetLastError());
  592. } else {
  593. pClient->UserName[0] = 0;
  594. GetNamedPipeHandleState(
  595. pClient->PipeReadH,
  596. NULL,
  597. NULL,
  598. NULL,
  599. NULL,
  600. pClient->UserName,
  601. sizeof(pClient->UserName)
  602. );
  603. //
  604. // For every client except the local
  605. // stdin/stdout client, there's a copy of remote.exe
  606. // running in client mode on the other side. Do
  607. // handshaking with it to setup options and check
  608. // versions. HandshakeWithRemoteClient will start
  609. // the "normal" I/O cycle once the handshake cycle is
  610. // done. Note it returns as soon as the first handshake
  611. // I/O is submitted.
  612. //
  613. if (pClient->ServerFlags & SFLG_LOCAL) {
  614. AddClientToHandshakingList(pClient);
  615. MoveClientToNormalList(pClient);
  616. //
  617. // Start read operation against this client's input.
  618. //
  619. StartReadClientInput(pClient);
  620. //
  621. // Start write cycle for client output from the temp
  622. // file.
  623. //
  624. StartReadTempFile(pClient);
  625. } else {
  626. HandshakeWithRemoteClient(pClient);
  627. }
  628. }
  629. }
  630. VOID
  631. FASTCALL
  632. CreatePipeAndIssueConnect(
  633. int nIndex // IN pipe index or OUT_PIPE
  634. )
  635. {
  636. BOOL b;
  637. DWORD dwError;
  638. char szPipeName[BUFFSIZE];
  639. if (OUT_PIPE == nIndex) {
  640. TRACE(CONNECT, ("Creating listening OUT pipe.\n"));
  641. } else {
  642. TRACE(CONNECT, ("Creating listening IN pipe #%d.\n", nIndex + 1));
  643. }
  644. if (OUT_PIPE == nIndex) {
  645. sprintf(szPipeName, SERVER_WRITE_PIPE, ".", pszPipeName);
  646. hPipeOut =
  647. CreateNamedPipe(
  648. szPipeName,
  649. PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
  650. PIPE_TYPE_BYTE,
  651. PIPE_UNLIMITED_INSTANCES,
  652. 0,
  653. 0,
  654. 0,
  655. &saPipe
  656. );
  657. if (INVALID_HANDLE_VALUE == hPipeOut) {
  658. ErrorExit("Unable to CreateNamedPipe OUT");
  659. }
  660. b = ConnectNamedPipe(hPipeOut, &olConnectOut);
  661. if ( ! b ) {
  662. dwError = GetLastError();
  663. if (ERROR_PIPE_CONNECTED == dwError) {
  664. b = TRUE;
  665. }
  666. }
  667. if ( b ) {
  668. TRACE(CONNECT, ("Quick connect on OUT pipe.\n"));
  669. HandleOutPipeConnected();
  670. } else {
  671. if (ERROR_IO_PENDING != dwError) {
  672. ErrorExit("ConnectNamedPipe out failed");
  673. }
  674. }
  675. } else {
  676. sprintf(szPipeName, SERVER_READ_PIPE, ".", pszPipeName);
  677. rghPipeIn[nIndex] =
  678. CreateNamedPipe(
  679. szPipeName,
  680. PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
  681. PIPE_TYPE_BYTE,
  682. PIPE_UNLIMITED_INSTANCES,
  683. 0,
  684. 0,
  685. 0,
  686. &saPipe
  687. );
  688. if (INVALID_HANDLE_VALUE == rghPipeIn[nIndex]) {
  689. if (ERROR_ACCESS_DENIED == GetLastError()) {
  690. if (DaclNameCount) {
  691. ErrorExit("Unable to CreateNamedPipe, are YOU in the list of permitted users?");
  692. } else {
  693. ErrorExit("Unable to CreateNamedPipe, maybe old remote server on same pipe name?");
  694. }
  695. } else {
  696. ErrorExit("Unable to CreateNamedPipe IN");
  697. }
  698. }
  699. b = ConnectNamedPipe(rghPipeIn[nIndex], &rgolConnectIn[nIndex]);
  700. if ( ! b ) {
  701. dwError = GetLastError();
  702. if (ERROR_PIPE_CONNECTED == dwError) {
  703. b = TRUE;
  704. }
  705. }
  706. if ( b ) {
  707. TRACE(CONNECT, ("Quick connect on IN pipe #%d.\n", nIndex));
  708. HandleInPipeConnected(nIndex);
  709. } else {
  710. if (ERROR_IO_PENDING != dwError) {
  711. ErrorExit("ConnectNamedPipe in failed");
  712. }
  713. }
  714. }
  715. if (OUT_PIPE == nIndex) {
  716. TRACE(CONNECT, ("Listening OUT pipe handle %p.\n", hPipeOut));
  717. } else {
  718. TRACE(CONNECT, ("Listening IN pipe #%d handle %p.\n", nIndex + 1, rghPipeIn[nIndex]));
  719. }
  720. }
  721. VOID
  722. FASTCALL
  723. HandleOutPipeConnected(
  724. VOID
  725. )
  726. {
  727. LARGE_INTEGER DueTime;
  728. ResetEvent(rghWait[WAITIDX_CONNECT_OUT]);
  729. bOutPipeConnected = TRUE;
  730. TRACE(CONNECT, ("Two-pipe caller connected to OUT pipe %p.\n",
  731. hPipeOut));
  732. //
  733. // Start a 1 minute timer in case we don't get a connection
  734. // on an IN pipe from this client, we'll recycle the OUT
  735. // pipe.
  736. //
  737. if (INVALID_HANDLE_VALUE != hConnectOutTimer) {
  738. DueTime.QuadPart = Int32x32To64(60 * 1000, -10000);
  739. pfnSetWaitableTimer(
  740. hConnectOutTimer,
  741. &DueTime,
  742. 0, // not periodic, single-fire
  743. ConnectOutTimerFired,
  744. 0, // arg to compl. rtn
  745. TRUE
  746. );
  747. }
  748. }
  749. VOID
  750. APIENTRY
  751. ConnectOutTimerFired(
  752. LPVOID pArg,
  753. DWORD dwTimerLo,
  754. DWORD dwTimerHi
  755. )
  756. {
  757. UNREFERENCED_PARAMETER( pArg );
  758. UNREFERENCED_PARAMETER( dwTimerLo );
  759. UNREFERENCED_PARAMETER( dwTimerHi );
  760. //
  761. // We've had a connected OUT pipe for a minute now,
  762. // only two-pipe clients connect to that and they
  763. // immediately connect to IN afterwards. Presumably
  764. // the client died between these two operations. Until
  765. // we recycle the OUT pipe all two-pipe clients are
  766. // unable to connect getting pipe busy errors.
  767. //
  768. if ( ! bOutPipeConnected ) {
  769. TRACE(CONNECT, ("ConnectOut timer fired but Out pipe not connected.\n"));
  770. return;
  771. }
  772. TRACE(CONNECT, ("Two-pipe caller hung for 1 minute, recycling OUT pipe %p.\n",
  773. hPipeOut));
  774. bOutPipeConnected = FALSE;
  775. CANCELIO(hPipeOut);
  776. DisconnectNamedPipe(hPipeOut);
  777. CloseHandle(hPipeOut);
  778. hPipeOut = INVALID_HANDLE_VALUE;
  779. CreatePipeAndIssueConnect(OUT_PIPE);
  780. //
  781. // In order for things to work reliably for 2-pipe clients
  782. // when there are multiple remote servers on the same pipename,
  783. // we need to tear down the listening IN pipe and recreate it so
  784. // that the oldest listening OUT pipe will be from the same process
  785. // as the oldest listening IN pipe.
  786. //
  787. if (1 == cConnectIns) {
  788. TRACE(CONNECT, ("Recycling IN pipe %p as well for round-robin behavior.\n",
  789. rghPipeIn[0]));
  790. CANCELIO(rghPipeIn[0]);
  791. DisconnectNamedPipe(rghPipeIn[0]);
  792. CloseHandle(rghPipeIn[0]);
  793. rghPipeIn[0] = INVALID_HANDLE_VALUE;
  794. CreatePipeAndIssueConnect(0);
  795. }
  796. }
  797. VOID
  798. FASTCALL
  799. HandleInPipeConnected(
  800. int nIndex
  801. )
  802. {
  803. PREMOTE_CLIENT pClient;
  804. char szHexAsciiId[9];
  805. ResetEvent(rghWait[WAITIDX_CONNECT_IN_BASE + nIndex]);
  806. if (nIndex >= (int) cConnectIns) {
  807. //
  808. // The I/O was cancelled on the excess
  809. // listening pipes, causing the event to
  810. // fire.
  811. //
  812. ASSERT(INVALID_HANDLE_VALUE == rghPipeIn[nIndex]);
  813. TRACE(CONNECT, ("IN pipe #%d, handle %p listen cancelled.\n",
  814. nIndex + 1, rghPipeIn[nIndex]));
  815. return;
  816. }
  817. TRACE(CONNECT, ("Caller connected to IN pipe #%d, handle %p.\n",
  818. nIndex + 1, rghPipeIn[nIndex]));
  819. //
  820. // A client is fully connected, but we don't know if
  821. // it's a single-pipe or two-pipe client. Until
  822. // we do its PipeWriteH will be invalid. We'll figure
  823. // it out in ReadClientNameCompleted.
  824. //
  825. pClient = HeapAlloc(
  826. hHeap,
  827. HEAP_ZERO_MEMORY,
  828. sizeof(*pClient)
  829. );
  830. if ( ! pClient) {
  831. printf("Out of memory connecting client, hanging up.\n");
  832. CloseHandle( rghPipeIn[nIndex] );
  833. rghPipeIn[nIndex] = INVALID_HANDLE_VALUE;
  834. CreatePipeAndIssueConnect( nIndex );
  835. if (bOutPipeConnected) {
  836. //
  837. // Hang up on the two-pipe caller connected to the
  838. // OUT pipe as well -- it may be this client or it
  839. // may be another, no way to tell, and really no
  840. // great need to because if it's another caller
  841. // we probably wouldn't be able to allocate memory
  842. // for it either.
  843. //
  844. // Also if we're using a single IN pipe for
  845. // multiple-server round-robin behavior we
  846. // want to recycle both pipes at the same time.
  847. //
  848. TRACE(CONNECT, ("Also hanging up on connected two-pipe caller on OUT pipe %p.\n",
  849. hPipeOut));
  850. bOutPipeConnected = FALSE;
  851. if (INVALID_HANDLE_VALUE != hConnectOutTimer) {
  852. pfnCancelWaitableTimer(hConnectOutTimer);
  853. }
  854. DisconnectNamedPipe(hPipeOut);
  855. CloseHandle(hPipeOut);
  856. hPipeOut = INVALID_HANDLE_VALUE;
  857. CreatePipeAndIssueConnect( OUT_PIPE );
  858. }
  859. } else {
  860. //
  861. // Initialize the Client
  862. //
  863. pClient->dwID = dwNextClientID++;
  864. sprintf(szHexAsciiId, "%08x", pClient->dwID);
  865. CopyMemory(pClient->HexAsciiId, szHexAsciiId, sizeof(pClient->HexAsciiId));
  866. pClient->PipeReadH = rghPipeIn[nIndex];
  867. rghPipeIn[nIndex] = INVALID_HANDLE_VALUE;
  868. pClient->PipeWriteH = INVALID_HANDLE_VALUE;
  869. TRACE(CONNECT, ("Handshaking new client %d (%p) on IN pipe handle %p.\n",
  870. pClient->dwID, pClient, pClient->PipeReadH));
  871. //
  872. // Start another connect operation to replace this completed one.
  873. //
  874. CreatePipeAndIssueConnect( nIndex );
  875. //
  876. // Start session I/Os with the new client. This will link it
  877. // into the handshaking list.
  878. //
  879. StartSession( pClient );
  880. }
  881. }