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.

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