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.

798 lines
17 KiB

  1. /******************************************************************************\
  2. * This is a part of the Microsoft Source Code Samples.
  3. * Copyright 1992 - 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 1992 - 1997 Microsoft Corporation
  12. Module Name:
  13. Client.c
  14. Abstract:
  15. The Client component of Remote. Connects to the remote
  16. server using named pipes. It sends its stdin to
  17. the server and output everything from server to
  18. its stdout.
  19. Author:
  20. Rajivendra Nath 2-Jan-1992
  21. Dave Hart Summer 1997 single-pipe operation
  22. Environment:
  23. Console App. User mode.
  24. Revision History:
  25. --*/
  26. #include <windows.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <process.h>
  30. #include <io.h>
  31. #include <string.h>
  32. #include "Remote.h"
  33. BOOL fAsyncPipe = TRUE; // need this so server has it TRUE
  34. HANDLE*
  35. EstablishSession(
  36. char *server,
  37. char *pipe
  38. );
  39. DWORD
  40. WINAPI
  41. SendServerInp(
  42. LPVOID pvParam
  43. );
  44. BOOL
  45. FilterClientInp(
  46. char *buff,
  47. int count
  48. );
  49. BOOL
  50. Mych(
  51. DWORD ctrlT
  52. );
  53. VOID
  54. SendMyInfo(
  55. PHANDLE Pipes
  56. );
  57. #define ZERO_LENGTH_READ_LIMIT 200
  58. HANDLE MyStdInp;
  59. HANDLE MyStdOut;
  60. //
  61. // ReadPipe and WritePipe are referenced by multiple
  62. // threads so need to be volatile.
  63. //
  64. volatile HANDLE ReadPipe;
  65. volatile HANDLE WritePipe;
  66. CONSOLE_SCREEN_BUFFER_INFO csbi;
  67. char MyEchoStr[30];
  68. BOOL CmdSent;
  69. DWORD LinesToSend=LINESTOSEND;
  70. VOID
  71. Client(
  72. char* Server,
  73. char* Pipe
  74. )
  75. {
  76. HANDLE *Connection;
  77. DWORD dwThreadID;
  78. HANDLE hThread;
  79. DWORD cb;
  80. OVERLAPPED ol;
  81. char rgchBuf[1024];
  82. DWORD dwZeroCount = 0;
  83. CWCDATA cwcData = {NULL};
  84. MyStdInp=GetStdHandle(STD_INPUT_HANDLE);
  85. MyStdOut=GetStdHandle(STD_OUTPUT_HANDLE);
  86. printf("**************************************\n");
  87. printf("*********** REMOTE ************\n");
  88. printf("*********** CLIENT ************\n");
  89. printf("**************************************\n");
  90. if ((Connection=EstablishSession(Server,Pipe))==NULL)
  91. return;
  92. ReadPipe=Connection[0];
  93. WritePipe=Connection[1];
  94. SetConsoleCtrlHandler((PHANDLER_ROUTINE)Mych,TRUE);
  95. // Start Thread For Client --> Server Flow
  96. hThread = (HANDLE)
  97. _beginthreadex(
  98. NULL, // security
  99. 0, // default stack size
  100. SendServerInp, // thread proc
  101. NULL, // parm
  102. 0, // not suspended
  103. &dwThreadID
  104. );
  105. if ( ! hThread)
  106. {
  107. Errormsg("REMOTE /C Could Not Create Thread.");
  108. return;
  109. }
  110. ZeroMemory(&ol, sizeof(ol));
  111. ol.hEvent =
  112. CreateEvent(
  113. NULL, // security
  114. TRUE, // auto-reset
  115. FALSE, // initially nonsignaled
  116. NULL // unnamed
  117. );
  118. while (ReadFileSynch(ReadPipe, rgchBuf, sizeof rgchBuf, &cb, 0, &ol)) {
  119. if (cb) {
  120. // If we are interested in colors, do special output
  121. if ( pWantColorLines() )
  122. {
  123. if ( !WriteConsoleWithColor( MyStdOut,
  124. rgchBuf,
  125. cb,
  126. &cwcData ) )
  127. {
  128. break;
  129. }
  130. }
  131. else
  132. {
  133. if ( ! WriteFile(MyStdOut, rgchBuf, cb, &cb, NULL)) {
  134. break;
  135. }
  136. }
  137. dwZeroCount = 0;
  138. } else {
  139. if (++dwZeroCount > ZERO_LENGTH_READ_LIMIT) {
  140. //
  141. // If we get a bunch of zero length reads in a row,
  142. // something's broken, don't loop forever.
  143. // (bug #115866).
  144. //
  145. printf("\nREMOTE: bailing out, server must have gone away.\n");
  146. break;
  147. }
  148. }
  149. }
  150. CloseHandle(ol.hEvent);
  151. printf("*** SESSION OVER ***");
  152. fflush(stdout);
  153. //
  154. // Terminate the keyboard reading thread.
  155. //
  156. TerminateThread(hThread, 0);
  157. CloseHandle(hThread);
  158. CloseClientPipes();
  159. printf("\n");
  160. fflush(stdout);
  161. }
  162. DWORD
  163. WINAPI
  164. SendServerInp(
  165. LPVOID pvParam
  166. )
  167. {
  168. DWORD dread,dwrote;
  169. OVERLAPPED ol;
  170. char buff[512];
  171. UNREFERENCED_PARAMETER(pvParam);
  172. ZeroMemory(&ol, sizeof(ol));
  173. ol.hEvent =
  174. CreateEvent(
  175. NULL, // security
  176. TRUE, // auto-reset
  177. FALSE, // initially nonsignaled
  178. NULL // unnamed
  179. );
  180. while(ReadFile(MyStdInp,buff,sizeof buff,&dread,NULL))
  181. {
  182. if (FilterClientInp(buff,dread))
  183. continue;
  184. if (!WriteFileSynch(WritePipe,buff,dread,&dwrote,0,&ol))
  185. break;
  186. }
  187. CloseClientPipes();
  188. return 0;
  189. }
  190. BOOL
  191. FilterClientInp(
  192. char *buff,
  193. int count
  194. )
  195. {
  196. if (count==0)
  197. return(TRUE);
  198. if (buff[0]==2) // Adhoc screening of ^B so that i386kd/mipskd
  199. return(TRUE); // do not terminate.
  200. if (buff[0]==COMMANDCHAR)
  201. {
  202. switch (buff[1])
  203. {
  204. case 'k':
  205. case 'K':
  206. case 'q':
  207. case 'Q':
  208. CloseClientPipes();
  209. return(FALSE);
  210. case 'h':
  211. case 'H':
  212. printf("%cM : Send Message\n",COMMANDCHAR);
  213. printf("%cP : Show Popup on Server\n",COMMANDCHAR);
  214. printf("%cS : Status of Server\n",COMMANDCHAR);
  215. printf("%cQ : Quit client\n",COMMANDCHAR);
  216. printf("%cH : This Help\n",COMMANDCHAR);
  217. return(TRUE);
  218. default:
  219. return(FALSE);
  220. }
  221. }
  222. return(FALSE);
  223. }
  224. BOOL
  225. Mych(
  226. DWORD ctrlT
  227. )
  228. {
  229. char c[2];
  230. DWORD tmp;
  231. OVERLAPPED ol;
  232. c[0]=CTRLC;
  233. if (ctrlT==CTRL_C_EVENT)
  234. {
  235. ZeroMemory(&ol, sizeof(ol));
  236. ol.hEvent =
  237. CreateEvent(
  238. NULL, // security
  239. TRUE, // auto-reset
  240. FALSE, // initially nonsignaled
  241. NULL // unnamed
  242. );
  243. if (INVALID_HANDLE_VALUE != WritePipe &&
  244. !WriteFileSynch(WritePipe,c,1,&tmp,0,&ol))
  245. {
  246. CloseHandle(ol.hEvent);
  247. Errormsg("Error Sending ^c");
  248. return(FALSE);
  249. }
  250. CloseHandle(ol.hEvent);
  251. return(TRUE);
  252. }
  253. if ((ctrlT==CTRL_BREAK_EVENT)||
  254. (ctrlT==CTRL_CLOSE_EVENT)||
  255. (ctrlT==CTRL_LOGOFF_EVENT)||
  256. (ctrlT==CTRL_SHUTDOWN_EVENT)
  257. ) {
  258. CloseClientPipes();
  259. }
  260. return(FALSE);
  261. }
  262. VOID
  263. CloseClientPipes(
  264. VOID
  265. )
  266. {
  267. HANDLE WriteHandle, ReadHandle;
  268. WriteHandle = (HANDLE) InterlockedExchangePointer(
  269. (PVOID *) &WritePipe,
  270. INVALID_HANDLE_VALUE
  271. );
  272. if (INVALID_HANDLE_VALUE != WriteHandle) {
  273. CloseHandle(WriteHandle);
  274. ReadHandle = (HANDLE) InterlockedExchangePointer(
  275. (PVOID *) &ReadPipe,
  276. INVALID_HANDLE_VALUE
  277. );
  278. if (INVALID_HANDLE_VALUE != ReadHandle &&
  279. WriteHandle != ReadHandle) {
  280. CloseHandle(ReadHandle);
  281. }
  282. }
  283. }
  284. VOID
  285. HandleConnectError(
  286. char *server,
  287. char *srvpipename
  288. )
  289. {
  290. DWORD Err = GetLastError();
  291. char msg[128];
  292. Errormsg("*** Unable to Connect ***");
  293. //
  294. // Print a helpful message
  295. //
  296. switch(Err)
  297. {
  298. case ERROR_FILE_NOT_FOUND:
  299. sprintf(msg,"invalid pipe name \"%s\"", srvpipename);
  300. break;
  301. case ERROR_BAD_NETPATH:
  302. sprintf(msg,"\\\\%s not found", server);
  303. break;
  304. default:
  305. FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM|
  306. FORMAT_MESSAGE_IGNORE_INSERTS,
  307. NULL, Err, 0, msg, sizeof(msg), NULL);
  308. break;
  309. }
  310. printf("Diagnosis: %s\n",msg);
  311. //
  312. // If the machine exists but the pipe doesn't do an
  313. // automatic remote /q to list pipes available on
  314. // that machine.
  315. //
  316. if (ERROR_FILE_NOT_FOUND == Err) {
  317. printf("\nREMOTE /Q %s\n", server);
  318. fflush(stdout);
  319. QueryRemotePipes(server);
  320. }
  321. }
  322. HANDLE*
  323. EstablishSession(
  324. char *server,
  325. char *srvpipename
  326. )
  327. {
  328. extern BOOL bForceTwoPipes;
  329. static HANDLE PipeH[2];
  330. char pipenameSrvIn[200];
  331. char pipenameSrvOut[200];
  332. BOOL fOldServer;
  333. DWORD dwError;
  334. DWORD RetryCount = 0;
  335. //
  336. // Since in single-pipe operation we'll be using the same
  337. // pipe in two threads, we have to open the handles for
  338. // overlapped operation, even though we always want
  339. // synchronous operation.
  340. //
  341. sprintf(pipenameSrvIn ,SERVER_READ_PIPE ,server,srvpipename);
  342. sprintf(pipenameSrvOut,SERVER_WRITE_PIPE,server,srvpipename);
  343. if (bForceTwoPipes) {
  344. dwError = ERROR_NOT_SUPPORTED;
  345. } else {
  346. RetrySrvBidi:
  347. if (INVALID_HANDLE_VALUE ==
  348. (PipeH[1] =
  349. CreateFile(
  350. pipenameSrvIn,
  351. GENERIC_READ | GENERIC_WRITE,
  352. 0,
  353. NULL,
  354. OPEN_EXISTING,
  355. FILE_FLAG_OVERLAPPED,
  356. NULL
  357. ))) {
  358. dwError = GetLastError();
  359. if (ERROR_PIPE_BUSY == dwError) {
  360. printf( "All pipe instances busy, waiting for another...\n");
  361. WaitNamedPipe(
  362. pipenameSrvIn,
  363. 15000
  364. );
  365. if (RetryCount++ < 6) {
  366. goto RetrySrvBidi;
  367. }
  368. }
  369. if (ERROR_ACCESS_DENIED != dwError &&
  370. ERROR_NOT_SUPPORTED != dwError) {
  371. HandleConnectError(server, srvpipename);
  372. return NULL;
  373. }
  374. } else {
  375. PipeH[0] = PipeH[1];
  376. fAsyncPipe = TRUE;
  377. printf("Connected...\n\n");
  378. SendMyInfo(PipeH);
  379. return PipeH;
  380. }
  381. }
  382. //
  383. // Old remote servers don't allow you to open the
  384. // server IN pipe for READ access, so go down the
  385. // old path, notably opening OUT first so the
  386. // server knows we'll be using both pipes. We'll
  387. // also come down this path on Win95 because
  388. // it doesn't allow you to open an overlapped
  389. // pipe handle. Or if remote /c mach pipe /2 is used.
  390. //
  391. fOldServer = (ERROR_ACCESS_DENIED == dwError);
  392. RetrySrvOut:
  393. if (INVALID_HANDLE_VALUE ==
  394. (PipeH[0] =
  395. CreateFile(
  396. pipenameSrvOut,
  397. GENERIC_READ,
  398. 0,
  399. NULL,
  400. OPEN_EXISTING,
  401. 0,
  402. NULL
  403. ))) {
  404. if (ERROR_PIPE_BUSY == GetLastError()) {
  405. printf( "All OUT pipe instances busy, waiting for another...\n");
  406. WaitNamedPipe(
  407. pipenameSrvOut,
  408. 32000 // server recycles abandoned
  409. ); // OUT pipe after two minutes
  410. if (RetryCount++ < 6) {
  411. goto RetrySrvOut;
  412. }
  413. }
  414. HandleConnectError(server, srvpipename);
  415. return NULL;
  416. }
  417. RetrySrvIn:
  418. if (INVALID_HANDLE_VALUE ==
  419. (PipeH[1] =
  420. CreateFile(
  421. pipenameSrvIn,
  422. GENERIC_WRITE,
  423. 0,
  424. NULL,
  425. OPEN_EXISTING,
  426. 0,
  427. NULL
  428. ))) {
  429. dwError = GetLastError();
  430. if (ERROR_PIPE_BUSY == dwError) {
  431. printf( "All IN pipe instances busy, waiting for another...\n");
  432. WaitNamedPipe(
  433. pipenameSrvIn,
  434. 15000
  435. );
  436. if (RetryCount++ < 6) {
  437. goto RetrySrvIn;
  438. }
  439. }
  440. HandleConnectError(server, srvpipename);
  441. return NULL;
  442. }
  443. fAsyncPipe = FALSE;
  444. printf("Connected... %s\n\n",
  445. fOldServer
  446. ? "to two-pipe remote server."
  447. : "using two pipes."
  448. );
  449. SendMyInfo(PipeH);
  450. return PipeH;
  451. }
  452. VOID
  453. SendMyInfo(
  454. PHANDLE pipeH
  455. )
  456. {
  457. HANDLE rPipe=pipeH[0];
  458. HANDLE wPipe=pipeH[1];
  459. DWORD hostlen;
  460. WORD BytesToSend=sizeof(SESSION_STARTUPINFO);
  461. DWORD tmp;
  462. OVERLAPPED ol;
  463. SESSION_STARTUPINFO ssi;
  464. SESSION_STARTREPLY ssr;
  465. ol.hEvent =
  466. CreateEvent(
  467. NULL, // security
  468. TRUE, // auto-reset
  469. FALSE, // initially nonsignaled
  470. NULL // unnamed
  471. );
  472. ssi.Size=BytesToSend;
  473. ssi.Version=VERSION;
  474. hostlen = sizeof(ssi.ClientName) / sizeof(ssi.ClientName[0]);
  475. GetComputerName(ssi.ClientName, &hostlen);
  476. ssi.LinesToSend=LinesToSend;
  477. ssi.Flag=ClientToServerFlag;
  478. {
  479. DWORD NewCode=MAGICNUMBER;
  480. char Name[MAX_COMPUTERNAME_LENGTH+1];
  481. strcpy(Name,(char *)ssi.ClientName);
  482. memcpy(&Name[11],(char *)&NewCode,sizeof(NewCode));
  483. //
  484. // The server needs to know if we're doing single-pipe
  485. // operation so it can complete the connection properly.
  486. // So if we are, change the first byte of the first
  487. // send (the computername, which is later superceded
  488. // by the one in the SESSION_STARTUPINFO structure)
  489. // to an illegal computername character, question mark.
  490. //
  491. if (wPipe == rPipe) {
  492. Name[0] = '?';
  493. }
  494. WriteFileSynch(wPipe,(char *)Name,HOSTNAMELEN-1,&tmp,0,&ol);
  495. ReadFileSynch(rPipe ,(char *)&ssr.MagicNumber,sizeof(ssr.MagicNumber),&tmp,0,&ol);
  496. if (ssr.MagicNumber!=MAGICNUMBER)
  497. {
  498. SetLastError(ERROR_INVALID_PARAMETER);
  499. ErrorExit("Pipe connected but server not recognized.\n");
  500. }
  501. //Get Rest of the info-its not the old server
  502. ReadFileSynch(
  503. rPipe,
  504. (char *)&ssr + sizeof(ssr.MagicNumber),
  505. sizeof(ssr)-sizeof(ssr.MagicNumber),
  506. &tmp,
  507. 0,
  508. &ol
  509. );
  510. }
  511. if (!WriteFileSynch(wPipe,(char *)&ssi,BytesToSend,&tmp,0,&ol))
  512. {
  513. Errormsg("INFO Send Error");
  514. }
  515. CloseHandle(ol.hEvent);
  516. }
  517. VOID
  518. QueryRemotePipes(
  519. char* pszServer
  520. )
  521. {
  522. HANDLE hQPipe;
  523. DWORD dwRead;
  524. DWORD dwError;
  525. char fullname[400];
  526. char* msg;
  527. int msgLen;
  528. if (pszServer[0] == '\\' && pszServer[1] == '\\') {
  529. pszServer += 2;
  530. }
  531. printf("Querying server \\\\%s\n", pszServer);
  532. sprintf(fullname, QUERY_DEBUGGERS_PIPE, pszServer);
  533. //
  534. // Send request and display the query result
  535. //
  536. hQPipe = CreateFile(fullname,
  537. GENERIC_READ | GENERIC_WRITE,
  538. 0,
  539. NULL,
  540. OPEN_EXISTING,
  541. FILE_ATTRIBUTE_NORMAL,
  542. NULL);
  543. if(hQPipe == INVALID_HANDLE_VALUE) {
  544. dwError = GetLastError();
  545. if (ERROR_FILE_NOT_FOUND == dwError) {
  546. printf("No Remote servers running on \\\\%s\n", pszServer);
  547. } else if (ERROR_BAD_NETPATH == dwError) {
  548. printf("\\\\%s not found on the network\n", pszServer);
  549. } else {
  550. FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
  551. FORMAT_MESSAGE_IGNORE_INSERTS,
  552. NULL, dwError, 0,
  553. fullname, sizeof(fullname), NULL);
  554. printf("Can't query server %s: %s\n", pszServer, fullname);
  555. }
  556. return;
  557. }
  558. // Send Query Command
  559. if(!WriteFile(hQPipe, "q", 1, &dwRead, NULL)
  560. || (dwRead != 1))
  561. {
  562. printf("\nError: Can't send command\n");
  563. goto failure;
  564. }
  565. if(!ReadFile(hQPipe,
  566. &msgLen,
  567. sizeof(int), // read msg dimension
  568. &dwRead,
  569. NULL)
  570. || (dwRead != sizeof(int)))
  571. {
  572. printf("\nError: Can't read message\n");
  573. goto failure;
  574. }
  575. if(!msgLen)
  576. {
  577. printf("\nNo visible sessions on server %s", pszServer);
  578. goto failure;
  579. }
  580. if(msgLen > 65535) // error
  581. {
  582. printf("Error querying server %s, got %d for msg length, 65535 max.\n",
  583. pszServer,
  584. msgLen
  585. );
  586. goto failure;
  587. }
  588. // +1 for null terminator
  589. if((msg = (char*)malloc( (msgLen +1) *sizeof(char))) == NULL)
  590. {
  591. printf("\nOut of memory\n");
  592. goto failure;
  593. }
  594. ReadFile(hQPipe,
  595. msg,
  596. msgLen * sizeof(char), // read msg
  597. &dwRead,
  598. NULL);
  599. // Make sure the string is terminated
  600. msg[dwRead] = 0;
  601. printf("\nVisible sessions on server %s:\n\n", pszServer);
  602. printf("%s\n", msg);
  603. free(msg);
  604. failure:
  605. CloseHandle(hQPipe);
  606. }