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.

583 lines
18 KiB

  1. /*++
  2. Copyright (c) 1999, Microsoft Corporation
  3. Module Name:
  4. tcbview.c
  5. Abstract:
  6. This module contains code for a utility program which monitors
  7. the variables for the active TCP/IP control blocks in the system.
  8. The program optionally maintains a log for a specified TCB
  9. in CSV format in a file specified by the user.
  10. Author:
  11. Abolade Gbadegesin (aboladeg) January-25-1999
  12. Revision History:
  13. --*/
  14. #include <nt.h>
  15. #include <ntrtl.h>
  16. #include <nturtl.h>
  17. #include <windows.h>
  18. #include <commctrl.h>
  19. #include <winsock2.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <io.h>
  23. #include <fcntl.h>
  24. #include <ntddip.h>
  25. #include <ntddtcp.h>
  26. #include <ipinfo.h>
  27. #include <iphlpapi.h>
  28. #include <iphlpstk.h>
  29. //
  30. // GLOBAL DATA
  31. //
  32. ULONG DisplayInterval = 500;
  33. HWND ListHandle;
  34. SOCKADDR_IN LogLocal;
  35. FILE* LogFile = NULL;
  36. PCHAR LogPath;
  37. SOCKADDR_IN LogRemote;
  38. HANDLE TcpipHandle;
  39. UINT_PTR TimerId;
  40. typedef enum {
  41. LocalAddressColumn,
  42. LocalPortColumn,
  43. RemoteAddressColumn,
  44. RemotePortColumn,
  45. SmRttColumn,
  46. DeltaColumn,
  47. RtoColumn,
  48. RexmitColumn,
  49. RexmitCntColumn,
  50. MaximumColumn
  51. } LIST_COLUMNS;
  52. CHAR* ColumnText[] = {
  53. "LocalAddress",
  54. "LocalPort",
  55. "RemoteAddress",
  56. "RemotePort",
  57. "SmRtt",
  58. "Delta",
  59. "Rto",
  60. "Rexmit",
  61. "RexmitCnt",
  62. };
  63. VOID
  64. AllocateConsole(
  65. VOID
  66. )
  67. {
  68. INT OsfHandle;
  69. FILE* FileHandle;
  70. //
  71. // Being a GUI application, we do not have a console for our process.
  72. // Allocate a console now, and make it our standard-output file.
  73. //
  74. AllocConsole();
  75. OsfHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
  76. FileHandle = _fdopen(OsfHandle, "w");
  77. if (!FileHandle) {
  78. perror("_fdopen");
  79. exit(0);
  80. }
  81. *stdout = *FileHandle;
  82. setvbuf(stdout, NULL, _IONBF, 0);
  83. }
  84. LRESULT CALLBACK
  85. DisplayWndProc(
  86. HWND WindowHandle,
  87. UINT Message,
  88. WPARAM Wparam,
  89. LPARAM Lparam
  90. )
  91. {
  92. //
  93. // Handle the few window-messages that we care about.
  94. // Our window will contain a listview as soon as we've initialized,
  95. // and we always resize that listview to fill our client area.
  96. // We also set up a timer which periodically triggers refresh
  97. // of the TCBs displayed.
  98. //
  99. if (Message == WM_CREATE) {
  100. CREATESTRUCT* CreateStruct = (CREATESTRUCT*)Lparam;
  101. LVCOLUMN LvColumn;
  102. RECT rc;
  103. do {
  104. //
  105. // Create the child listview, and insert columns
  106. // for each of the TCB fields that we'll be displaying.
  107. //
  108. GetClientRect(WindowHandle, &rc);
  109. ListHandle =
  110. CreateWindowEx(
  111. 0,
  112. WC_LISTVIEW,
  113. NULL,
  114. WS_CHILD|LVS_REPORT|LVS_NOSORTHEADER,
  115. 0,
  116. 0,
  117. rc.right,
  118. rc.bottom,
  119. WindowHandle,
  120. NULL,
  121. CreateStruct->hInstance,
  122. NULL
  123. );
  124. if (!ListHandle) { break; }
  125. ZeroMemory(&LvColumn, sizeof(LvColumn));
  126. for (; LvColumn.iSubItem < MaximumColumn; LvColumn.iSubItem++) {
  127. LvColumn.mask = LVCF_FMT|LVCF_SUBITEM|LVCF_TEXT|LVCF_WIDTH;
  128. LvColumn.fmt = LVCFMT_LEFT;
  129. LvColumn.pszText = ColumnText[LvColumn.iSubItem];
  130. LvColumn.cx = 50;
  131. ListView_InsertColumn(ListHandle, LvColumn.iSubItem, &LvColumn);
  132. }
  133. //
  134. // Initialize our periodic timer, and display our window.
  135. //
  136. TimerId = SetTimer(WindowHandle, 1, DisplayInterval, NULL);
  137. ShowWindow(WindowHandle, SW_SHOW);
  138. ShowWindow(ListHandle, SW_SHOW);
  139. if (!TimerId) { break; }
  140. return 0;
  141. } while(FALSE);
  142. PostQuitMessage(0);
  143. return (LRESULT)-1;
  144. } else if (Message == WM_DESTROY) {
  145. //
  146. // Stop our periodic timer, close the log-file (if any),
  147. // close the handle on which we communicate with the TCP/IP driver,
  148. // and post a quit message to cause the message-loop of our process
  149. // to end.
  150. //
  151. KillTimer(WindowHandle, TimerId);
  152. if (LogFile) { fclose(LogFile); }
  153. NtClose(TcpipHandle);
  154. PostQuitMessage(0);
  155. return 0;
  156. } else if (Message == WM_SETFOCUS) {
  157. //
  158. // Always pass the focus to our child-control, the listview.
  159. //
  160. SetFocus(ListHandle);
  161. return 0;
  162. } else if (Message == WM_WINDOWPOSCHANGED) {
  163. RECT rc;
  164. //
  165. // Always resize our listview to fill our client-area.
  166. //
  167. GetClientRect(WindowHandle, &rc);
  168. SetWindowPos(
  169. ListHandle,
  170. WindowHandle,
  171. 0,
  172. 0,
  173. rc.right,
  174. rc.bottom,
  175. ((WINDOWPOS*)Lparam)->flags
  176. );
  177. return 0;
  178. } else if (Message == WM_TIMER) {
  179. COORD Coord = {0, 0};
  180. DWORD Error;
  181. ULONG i;
  182. LONG Item;
  183. ULONG Length;
  184. LVITEM LvItem;
  185. CHAR Text[20];
  186. TCP_FINDTCB_REQUEST Request;
  187. TCP_FINDTCB_RESPONSE Response;
  188. PMIB_TCPTABLE Table;
  189. //
  190. // If we're configured to use a log-file and we haven't created one,
  191. // do so now, and print the CSV header to the file.
  192. //
  193. if (LogPath && !LogFile) {
  194. LogFile = fopen(LogPath, "w+");
  195. if (!LogFile) {
  196. return 0;
  197. } else {
  198. fprintf(
  199. LogFile,
  200. "#senduna,sendnext,sendmax,sendwin,unacked,maxwin,cwin,"
  201. "mss,rtt,smrtt,rexmitcnt,rexmittimer,rexmit,retrans,state,"
  202. "flags,rto,delta\n"
  203. );
  204. }
  205. }
  206. //
  207. // Clear our listview and retrieve a new table of TCP connections.
  208. // It would be less visually jarring if, instead of deleting all
  209. // the list-items each time, we used a mark-and-sweep to just update
  210. // the ones which had changed. However, that sounds too much like work.
  211. //
  212. ListView_DeleteAllItems(ListHandle);
  213. Error =
  214. AllocateAndGetTcpTableFromStack(
  215. &Table,
  216. TRUE,
  217. GetProcessHeap(),
  218. 0
  219. );
  220. if (Error) { return 0; }
  221. //
  222. // Display each active TCP control block in the listview.
  223. // For each entry, we retrieve the partial TCB using IOCTL_TCP_FINDTCB,
  224. // and then display it in the list.
  225. // If we are generating a log-file for one of the TCBs,
  226. // we append the current information to that log-file.
  227. //
  228. for (i = 0, Item = 0; i < Table->dwNumEntries; i++) {
  229. if (Table->table[i].dwState < MIB_TCP_STATE_SYN_SENT ||
  230. Table->table[i].dwState > MIB_TCP_STATE_TIME_WAIT) {
  231. continue;
  232. }
  233. Request.Src = Table->table[i].dwLocalAddr;
  234. Request.Dest = Table->table[i].dwRemoteAddr;
  235. Request.SrcPort = (USHORT)Table->table[i].dwLocalPort;
  236. Request.DestPort = (USHORT)Table->table[i].dwRemotePort;
  237. ZeroMemory(&Response, sizeof(Response));
  238. if (!DeviceIoControl(
  239. TcpipHandle,
  240. IOCTL_TCP_FINDTCB,
  241. &Request,
  242. sizeof(Request),
  243. &Response,
  244. sizeof(Response),
  245. &Length,
  246. NULL
  247. )) {
  248. continue;
  249. }
  250. lstrcpy(Text, inet_ntoa(*(PIN_ADDR)&Request.Src));
  251. ZeroMemory(&LvItem, sizeof(LvItem));
  252. LvItem.mask = LVIF_TEXT;
  253. LvItem.iItem = Item;
  254. LvItem.iSubItem = LocalAddressColumn;
  255. LvItem.pszText = Text;
  256. LvItem.iItem = ListView_InsertItem(ListHandle, &LvItem);
  257. if (LvItem.iItem == -1) { continue; }
  258. ListView_SetItemText(
  259. ListHandle, Item, RemoteAddressColumn,
  260. inet_ntoa(*(PIN_ADDR)&Request.Dest)
  261. );
  262. _ltoa(ntohs(Request.SrcPort), Text, 10);
  263. ListView_SetItemText(ListHandle, Item, LocalPortColumn, Text);
  264. _ltoa(ntohs(Request.DestPort), Text, 10);
  265. ListView_SetItemText(ListHandle, Item, RemotePortColumn, Text);
  266. _ltoa(Response.tcb_smrtt, Text, 10);
  267. ListView_SetItemText(ListHandle, Item, SmRttColumn, Text);
  268. _ltoa(0, /* Response.tcb_delta, */ Text, 10);
  269. ListView_SetItemText(ListHandle, Item, DeltaColumn, Text);
  270. wsprintf(
  271. Text, "%d.%d", 0, // Response.tcb_rto / 10,
  272. 0 // (Response.tcb_rto % 10) * 100
  273. );
  274. ListView_SetItemText(ListHandle, Item, RtoColumn, Text);
  275. _ltoa(Response.tcb_rexmit, Text, 10);
  276. ListView_SetItemText(ListHandle, Item, RexmitColumn, Text);
  277. _ltoa(Response.tcb_rexmitcnt, Text, 10);
  278. ListView_SetItemText(ListHandle, Item, RexmitCntColumn, Text);
  279. ++Item;
  280. //
  281. // If we are generating a log-file, update it now.
  282. // We allow the user to specify a wildcard for either or both port
  283. // on the command-line, so if a wildcard was specified
  284. // in 'LogLocal' or 'LogRemote', we now instantiate the wildcard
  285. // for the first matching session.
  286. //
  287. if (Request.Src == LogLocal.sin_addr.s_addr &&
  288. Request.Dest == LogRemote.sin_addr.s_addr &&
  289. (LogLocal.sin_port == 0 ||
  290. Request.SrcPort == LogLocal.sin_port) &&
  291. (LogRemote.sin_port == 0 ||
  292. Request.DestPort == LogRemote.sin_port)) {
  293. //
  294. // This assignment instantiates the user's wildcard, if any,
  295. //
  296. LogLocal.sin_port = Request.SrcPort;
  297. LogRemote.sin_port = Request.DestPort;
  298. fprintf(
  299. LogFile, "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,"
  300. "%x,%u,%u\n",
  301. Response.tcb_senduna,
  302. Response.tcb_sendnext,
  303. Response.tcb_sendmax,
  304. Response.tcb_sendwin,
  305. Response.tcb_unacked,
  306. Response.tcb_maxwin,
  307. Response.tcb_cwin,
  308. Response.tcb_mss,
  309. Response.tcb_rtt,
  310. Response.tcb_smrtt,
  311. Response.tcb_rexmitcnt,
  312. Response.tcb_rexmittimer,
  313. Response.tcb_rexmit,
  314. Response.tcb_retrans,
  315. Response.tcb_state,
  316. 0, // Response.tcb_flags,
  317. 0, // Response.tcb_rto,
  318. 0 // Response.tcb_delta
  319. );
  320. }
  321. }
  322. HeapFree(GetProcessHeap(), 0, Table);
  323. UpdateWindow(ListHandle);
  324. return 0;
  325. }
  326. return DefWindowProc(WindowHandle, Message, Wparam, Lparam);
  327. }
  328. void
  329. DisplayUsage(
  330. void
  331. )
  332. {
  333. AllocateConsole();
  334. printf("tcbview [-?] [-tcbhelp] [-refresh <ms>] [-log <path> <session>\n");
  335. printf("\t<session> = <local endpoint> <remote endpoint>\n");
  336. printf("\t<endpoint> = <address> { <port> | * }\n");
  337. printf("Press <Ctrl-C> to continue...");
  338. Sleep(INFINITE);
  339. }
  340. void
  341. DisplayTcbHelp(
  342. void
  343. )
  344. {
  345. AllocateConsole();
  346. printf("tcbview: TCB Help\n");
  347. printf("tcb fields:\n");
  348. printf("\tsenduna = seq. of first unack'd byte\n");
  349. printf("\tsendnext = seq. of next byte to send\n");
  350. printf("\tsendmax = max. seq. sent so far\n");
  351. printf("\tsendwin = size of send window in bytes\n");
  352. printf("\tunacked = number of unack'd bytes\n");
  353. printf("\tmaxwin = max. send window offered\n");
  354. printf("\tcwin = size of congestion window in bytes\n");
  355. printf("\tmss = max. segment size\n");
  356. printf("\trtt = timestamp of current rtt measurement\n");
  357. printf("\tsmrtt = smoothed rtt measurement\n");
  358. printf("\trexmitcnt = number of rexmit'd segments\n");
  359. printf("\trexmittimer = rexmit timer in ticks\n");
  360. printf("\trexmit = rexmit timeout last computed\n");
  361. printf("\tretrans = total rexmit'd segments (all sessions)\n");
  362. printf("\tstate = connection state\n");
  363. printf("\tflags = connection flags (see below)\n");
  364. printf("\trto = real-time rto (compare rexmit)\n");
  365. printf("\tdelta = rtt variance\n");
  366. printf("\n");
  367. printf("flags:\n");
  368. printf("\t00000001 = window explicitly set\n");
  369. printf("\t00000002 = has client options\n");
  370. printf("\t00000004 = from accept\n");
  371. printf("\t00000008 = from active open\n");
  372. printf("\t00000010 = client notified of disconnect\n");
  373. printf("\t00000020 = in delayed action queue\n");
  374. printf("\t00000040 = completing receives\n");
  375. printf("\t00000080 = in receive-indication handler\n");
  376. printf("\t00000100 = needs receive-completes\n");
  377. printf("\t00000200 = needs to send ack\n");
  378. printf("\t00000400 = needs to output\n");
  379. printf("\t00000800 = delayed sending ack\n");
  380. printf("\t00001000 = probing for path-mtu bh\n");
  381. printf("\t00002000 = using bsd urgent semantics\n");
  382. printf("\t00004000 = in 'DeliverUrgent'\n");
  383. printf("\t00008000 = seen urgent data and urgent data fields valid\n");
  384. printf("\t00010000 = needs to send fin\n");
  385. printf("\t00020000 = using nagle's algorithm\n");
  386. printf("\t00040000 = in 'TCPSend'\n");
  387. printf("\t00080000 = flow-controlled (received zero-window)\n");
  388. printf("\t00100000 = disconnect-notif. pending\n");
  389. printf("\t00200000 = time-wait transition pending\n");
  390. printf("\t00400000 = output being forced\n");
  391. printf("\t00800000 = send pending after receive\n");
  392. printf("\t01000000 = graceful-close pending\n");
  393. printf("\t02000000 = keepalives enabled\n");
  394. printf("\t04000000 = processing urgent data inline\n");
  395. printf("\t08000000 = inform acd about connection\n");
  396. printf("\t10000000 = fin sent since last retransmit\n");
  397. printf("\t20000000 = unack'd fin sent\n");
  398. printf("\t40000000 = need to send rst when closing\n");
  399. printf("\t80000000 = in tcb table\n");
  400. printf("Press <Ctrl-C> to continue...");
  401. Sleep(INFINITE);
  402. }
  403. INT WINAPI
  404. WinMain(
  405. HINSTANCE InstanceHandle,
  406. HINSTANCE Unused,
  407. PCHAR CommandLine,
  408. INT ShowWindowCode
  409. )
  410. {
  411. LONG argc;
  412. PCHAR* argv;
  413. LONG i;
  414. IO_STATUS_BLOCK IoStatus;
  415. MSG Message;
  416. OBJECT_ATTRIBUTES ObjectAttributes;
  417. NTSTATUS Status;
  418. HANDLE ThreadHandle;
  419. ULONG ThreadId;
  420. UNICODE_STRING UnicodeString;
  421. HWND WindowHandle;
  422. WNDCLASS WndClass;
  423. //
  424. // Process command-line arguments. See 'DisplayUsage' above for help.
  425. //
  426. argc = __argc;
  427. argv = __argv;
  428. for (i = 1; i < argc; i++) {
  429. if (lstrcmpi(argv[i], "-?") == 0 || lstrcmpi(argv[i], "/?") == 0) {
  430. DisplayUsage();
  431. return 0;
  432. } else if (lstrcmpi(argv[i], "-tcbhelp") == 0) {
  433. DisplayTcbHelp();
  434. return 0;
  435. } else if (lstrcmpi(argv[i], "-refresh") == 0 && (i + 1) >= argc) {
  436. DisplayInterval = atol(argv[++i]);
  437. if (!DisplayInterval) {
  438. DisplayUsage();
  439. return 0;
  440. }
  441. } else if (lstrcmpi(argv[i], "-log") == 0) {
  442. if ((i + 5) >= argc) {
  443. DisplayUsage();
  444. return 0;
  445. }
  446. LogPath = argv[++i];
  447. LogLocal.sin_addr.s_addr = inet_addr(argv[++i]);
  448. if (lstrcmpi(argv[i+1], "*") == 0) {
  449. LogLocal.sin_port = 0; ++i;
  450. } else {
  451. LogLocal.sin_port = htons((SHORT)atol(argv[++i]));
  452. }
  453. LogRemote.sin_addr.s_addr = inet_addr(argv[++i]);
  454. if (lstrcmpi(argv[i+1], "*") == 0) {
  455. LogRemote.sin_port = 0; ++i;
  456. } else {
  457. LogRemote.sin_port = htons((SHORT)atol(argv[++i]));
  458. }
  459. if (LogLocal.sin_addr.s_addr == INADDR_NONE ||
  460. LogRemote.sin_addr.s_addr == INADDR_NONE) {
  461. DisplayUsage();
  462. return 0;
  463. }
  464. }
  465. }
  466. //
  467. // Open a handle to the TCP/IP driver,
  468. // to be used in issuing IOCTL_TCP_FINDTCB requests.
  469. //
  470. RtlInitUnicodeString(&UnicodeString, DD_TCP_DEVICE_NAME);
  471. InitializeObjectAttributes(
  472. &ObjectAttributes,
  473. &UnicodeString,
  474. OBJ_CASE_INSENSITIVE,
  475. NULL,
  476. NULL
  477. );
  478. Status =
  479. NtCreateFile(
  480. &TcpipHandle,
  481. GENERIC_EXECUTE,
  482. &ObjectAttributes,
  483. &IoStatus,
  484. NULL,
  485. FILE_ATTRIBUTE_NORMAL,
  486. FILE_SHARE_READ|FILE_SHARE_WRITE,
  487. FILE_OPEN_IF,
  488. 0,
  489. NULL,
  490. 0
  491. );
  492. if (!NT_SUCCESS(Status)) {
  493. printf("NtCreateFile: %x\n", Status);
  494. return 0;
  495. }
  496. //
  497. // Register our window class and create the sole instance
  498. // of our main window. Then, enter our application message loop
  499. // until the user dismisses the window.
  500. //
  501. ZeroMemory(&WndClass, sizeof(WndClass));
  502. WndClass.lpfnWndProc = DisplayWndProc;
  503. WndClass.hInstance = InstanceHandle;
  504. WndClass.lpszClassName = "TcbViewClass";
  505. Message.wParam = 0;
  506. if (!RegisterClass(&WndClass)) {
  507. printf("RegisterClass: %d\n", GetLastError());
  508. } else {
  509. WindowHandle =
  510. CreateWindowEx(
  511. 0,
  512. "TcbViewClass",
  513. "TcbView",
  514. WS_TILEDWINDOW,
  515. CW_USEDEFAULT,
  516. CW_USEDEFAULT,
  517. CW_USEDEFAULT,
  518. CW_USEDEFAULT,
  519. NULL,
  520. NULL,
  521. InstanceHandle,
  522. NULL
  523. );
  524. if (!WindowHandle) {
  525. printf("CreateWindowEx: %d\n", GetLastError());
  526. } else {
  527. while(GetMessage(&Message, NULL, 0, 0)) {
  528. TranslateMessage(&Message);
  529. DispatchMessage(&Message);
  530. }
  531. }
  532. }
  533. return (LONG)Message.wParam;
  534. }