2 * PROJECT: ReactOS netstat utility
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/network/netstat/netstat.c
5 * PURPOSE: display IP stack statistics
6 * COPYRIGHT: Copyright 2005 Ged Murphy <gedmurphy@gmail.com>
10 * sort function return values.
11 * implement -b, -o and -v
12 * clean up GetIpHostName
13 * command line parser needs more work
16 #define WIN32_NO_STATUS
29 enum ProtoType
{IP
, TCP
, UDP
, ICMP
} Protocol
;
30 DWORD Interval
; /* time to pause between printing output */
32 /* TCP endpoint states */
33 TCHAR TcpState
[][32] = {
50 * format message string and display output
52 DWORD
DoFormatMessage(DWORD ErrorCode
)
57 if ((RetVal
= FormatMessage(
58 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
59 FORMAT_MESSAGE_FROM_SYSTEM
|
60 FORMAT_MESSAGE_IGNORE_INSERTS
,
63 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), /* Default language */
68 _tprintf(_T("%s"), (LPTSTR
)lpMsgBuf
);
71 /* return number of TCHAR's stored in output buffer
72 * excluding '\0' - as FormatMessage does*/
81 * Parse command line parameters and set any options
84 BOOL
ParseCmdline(int argc
, char* argv
[])
90 if ((argc
== 1) || (_istdigit(*argv
[1])))
93 /* Parse command line for options we have been given. */
94 for (i
= 1; i
< argc
; i
++)
96 if ((argc
> 1) && (argv
[i
][0] == '-' || argv
[i
][0] == '/'))
98 while ((c
= *++argv
[i
]) != '\0')
103 bDoShowAllCons
= TRUE
;
106 bDoShowProcName
= TRUE
;
109 bDoShowEthStats
= TRUE
;
112 bDoShowNumbers
= TRUE
;
115 bDoShowProtoCons
= TRUE
;
117 if (!_stricmp("IP", Proto
))
119 else if (!_stricmp("ICMP", Proto
))
121 else if (!_stricmp("TCP", Proto
))
123 else if (!_stricmp("UDP", Proto
))
132 bDoShowRouteTable
= TRUE
;
135 bDoShowProtoStats
= TRUE
;
138 bDoShowProcessId
= TRUE
;
141 _tprintf(_T("got v\n"));
142 bDoDispSeqComp
= TRUE
;
150 else if (_istdigit(*argv
[i
]))
152 if (_stscanf(argv
[i
], "%lu", &Interval
) != EOF
)
168 * Display table header
170 VOID
DisplayTableHeader()
172 _tprintf(_T("\n Proto Local Address Foreign Address State"));
173 if (bDoShowProcessId
)
174 _tprintf(_T(" Process\n"));
181 * Simulate Microsofts netstat utility output
187 DisplayTableHeader();
192 if (bDoShowRouteTable
)
194 /* mingw doesn't have lib for _tsystem */
195 if (system("route print") == -1)
197 _tprintf(_T("cannot find 'route.exe'\n"));
205 ShowEthernetStatistics();
209 if (bDoShowProtoCons
)
214 if (bDoShowProtoStats
)
221 if (bDoShowProtoStats
)
223 ShowIcmpStatistics();
228 if (bDoShowProtoStats
)
230 _tprintf(_T("\nActive Connections\n"));
231 DisplayTableHeader();
235 if (bDoShowProtoStats
)
237 _tprintf(_T("\nActive Connections\n"));
238 DisplayTableHeader();
245 else if (bDoShowProtoStats
)
248 ShowIcmpStatistics();
255 _tprintf(_T("\nActive Connections\n"));
256 DisplayTableHeader();
264 VOID
ShowIpStatistics()
266 PMIB_IPSTATS pIpStats
;
269 pIpStats
= (MIB_IPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPSTATS
));
271 if ((dwRetVal
= GetIpStatistics(pIpStats
)) == NO_ERROR
)
273 _tprintf(_T("\nIPv4 Statistics\n\n"));
274 _tprintf(_T(" %-34s = %lu\n"), _T("Packets Received"), pIpStats
->dwInReceives
);
275 _tprintf(_T(" %-34s = %lu\n"), _T("Received Header Errors"), pIpStats
->dwInHdrErrors
);
276 _tprintf(_T(" %-34s = %lu\n"), _T("Received Address Errors"), pIpStats
->dwInAddrErrors
);
277 _tprintf(_T(" %-34s = %lu\n"), _T("Datagrams Forwarded"), pIpStats
->dwForwDatagrams
);
278 _tprintf(_T(" %-34s = %lu\n"), _T("Unknown Protocols Received"), pIpStats
->dwInUnknownProtos
);
279 _tprintf(_T(" %-34s = %lu\n"), _T("Received Packets Discarded"), pIpStats
->dwInDiscards
);
280 _tprintf(_T(" %-34s = %lu\n"), _T("Received Packets Delivered"), pIpStats
->dwInDelivers
);
281 _tprintf(_T(" %-34s = %lu\n"), _T("Output Requests"), pIpStats
->dwOutRequests
);
282 _tprintf(_T(" %-34s = %lu\n"), _T("Routing Discards"), pIpStats
->dwRoutingDiscards
);
283 _tprintf(_T(" %-34s = %lu\n"), _T("Discarded Output Packets"), pIpStats
->dwOutDiscards
);
284 _tprintf(_T(" %-34s = %lu\n"), _T("Output Packets No Route"), pIpStats
->dwOutNoRoutes
);
285 _tprintf(_T(" %-34s = %lu\n"), _T("Reassembly Required"), pIpStats
->dwReasmReqds
);
286 _tprintf(_T(" %-34s = %lu\n"), _T("Reassembly Succesful"), pIpStats
->dwReasmOks
);
287 _tprintf(_T(" %-34s = %lu\n"), _T("Reassembly Failures"), pIpStats
->dwReasmFails
);
288 // _tprintf(_T(" %-34s = %lu\n"), _T("Datagrams successfully fragmented"), NULL); /* FIXME: what is this one? */
289 _tprintf(_T(" %-34s = %lu\n"), _T("Datagrams Failing Fragmentation"), pIpStats
->dwFragFails
);
290 _tprintf(_T(" %-34s = %lu\n"), _T("Fragments Created"), pIpStats
->dwFragCreates
);
293 DoFormatMessage(dwRetVal
);
295 HeapFree(GetProcessHeap(), 0, pIpStats
);
298 VOID
ShowIcmpStatistics()
300 PMIB_ICMP pIcmpStats
;
303 pIcmpStats
= (MIB_ICMP
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_ICMP
));
305 if ((dwRetVal
= GetIcmpStatistics(pIcmpStats
)) == NO_ERROR
)
307 _tprintf(_T("\nICMPv4 Statistics\n\n"));
308 _tprintf(_T(" Received Sent\n"));
309 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Messages"),
310 pIcmpStats
->stats
.icmpInStats
.dwMsgs
, pIcmpStats
->stats
.icmpOutStats
.dwMsgs
);
311 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Errors"),
312 pIcmpStats
->stats
.icmpInStats
.dwErrors
, pIcmpStats
->stats
.icmpOutStats
.dwErrors
);
313 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Destination Unreachable"),
314 pIcmpStats
->stats
.icmpInStats
.dwDestUnreachs
, pIcmpStats
->stats
.icmpOutStats
.dwDestUnreachs
);
315 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Time Exceeded"),
316 pIcmpStats
->stats
.icmpInStats
.dwTimeExcds
, pIcmpStats
->stats
.icmpOutStats
.dwTimeExcds
);
317 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Parameter Problems"),
318 pIcmpStats
->stats
.icmpInStats
.dwParmProbs
, pIcmpStats
->stats
.icmpOutStats
.dwParmProbs
);
319 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Source Quenches"),
320 pIcmpStats
->stats
.icmpInStats
.dwSrcQuenchs
, pIcmpStats
->stats
.icmpOutStats
.dwSrcQuenchs
);
321 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Redirects"),
322 pIcmpStats
->stats
.icmpInStats
.dwRedirects
, pIcmpStats
->stats
.icmpOutStats
.dwRedirects
);
323 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Echos"),
324 pIcmpStats
->stats
.icmpInStats
.dwEchos
, pIcmpStats
->stats
.icmpOutStats
.dwEchos
);
325 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Echo Replies"),
326 pIcmpStats
->stats
.icmpInStats
.dwEchoReps
, pIcmpStats
->stats
.icmpOutStats
.dwEchoReps
);
327 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Timestamps"),
328 pIcmpStats
->stats
.icmpInStats
.dwTimestamps
, pIcmpStats
->stats
.icmpOutStats
.dwTimestamps
);
329 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Timestamp Replies"),
330 pIcmpStats
->stats
.icmpInStats
.dwTimestampReps
, pIcmpStats
->stats
.icmpOutStats
.dwTimestampReps
);
331 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Address Masks"),
332 pIcmpStats
->stats
.icmpInStats
.dwAddrMasks
, pIcmpStats
->stats
.icmpOutStats
.dwAddrMasks
);
333 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Address Mask Replies"),
334 pIcmpStats
->stats
.icmpInStats
.dwAddrMaskReps
, pIcmpStats
->stats
.icmpOutStats
.dwAddrMaskReps
);
337 DoFormatMessage(dwRetVal
);
339 HeapFree(GetProcessHeap(), 0, pIcmpStats
);
343 VOID
ShowTcpStatistics()
345 PMIB_TCPSTATS pTcpStats
;
348 pTcpStats
= (MIB_TCPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_TCPSTATS
));
350 if ((dwRetVal
= GetTcpStatistics(pTcpStats
)) == NO_ERROR
)
352 _tprintf(_T("\nTCP Statistics for IPv4\n\n"));
353 _tprintf(_T(" %-35s = %lu\n"), _T("Active Opens"), pTcpStats
->dwActiveOpens
);
354 _tprintf(_T(" %-35s = %lu\n"), _T("Passive Opens"), pTcpStats
->dwPassiveOpens
);
355 _tprintf(_T(" %-35s = %lu\n"), _T("Failed Connection Attempts"), pTcpStats
->dwAttemptFails
);
356 _tprintf(_T(" %-35s = %lu\n"), _T("Reset Connections"), pTcpStats
->dwEstabResets
);
357 _tprintf(_T(" %-35s = %lu\n"), _T("Current Connections"), pTcpStats
->dwCurrEstab
);
358 _tprintf(_T(" %-35s = %lu\n"), _T("Segments Received"), pTcpStats
->dwInSegs
);
359 _tprintf(_T(" %-35s = %lu\n"), _T("Segments Sent"), pTcpStats
->dwOutSegs
);
360 _tprintf(_T(" %-35s = %lu\n"), _T("Segments Retransmitted"), pTcpStats
->dwRetransSegs
);
363 DoFormatMessage(dwRetVal
);
365 HeapFree(GetProcessHeap(), 0, pTcpStats
);
368 VOID
ShowUdpStatistics()
370 PMIB_UDPSTATS pUdpStats
;
373 pUdpStats
= (MIB_UDPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_UDPSTATS
));
375 if ((dwRetVal
= GetUdpStatistics(pUdpStats
)) == NO_ERROR
)
377 _tprintf(_T("\nUDP Statistics for IPv4\n\n"));
378 _tprintf(_T(" %-21s = %lu\n"), _T("Datagrams Received"), pUdpStats
->dwInDatagrams
);
379 _tprintf(_T(" %-21s = %lu\n"), _T("No Ports"), pUdpStats
->dwNoPorts
);
380 _tprintf(_T(" %-21s = %lu\n"), _T("Receive Errors"), pUdpStats
->dwInErrors
);
381 _tprintf(_T(" %-21s = %lu\n"), _T("Datagrams Sent"), pUdpStats
->dwOutDatagrams
);
384 DoFormatMessage(dwRetVal
);
386 HeapFree(GetProcessHeap(), 0, pUdpStats
);
389 VOID
ShowEthernetStatistics()
391 PMIB_IFTABLE pIfTable
;
395 pIfTable
= (MIB_IFTABLE
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IFTABLE
));
397 if (GetIfTable(pIfTable
, &dwSize
, 0) == ERROR_INSUFFICIENT_BUFFER
)
399 HeapFree(GetProcessHeap(), 0, pIfTable
);
400 pIfTable
= (MIB_IFTABLE
*) HeapAlloc(GetProcessHeap(), 0, dwSize
);
402 if ((dwRetVal
= GetIfTable(pIfTable
, &dwSize
, 0)) == NO_ERROR
)
404 _tprintf(_T("Interface Statistics\n\n"));
405 _tprintf(_T(" Received Sent\n\n"));
406 _tprintf(_T("%-20s %14lu %15lu\n"), _T("Bytes"),
407 pIfTable
->table
[0].dwInOctets
, pIfTable
->table
[0].dwOutOctets
);
408 _tprintf(_T("%-20s %14lu %15lu\n"), _T("Unicast packets"),
409 pIfTable
->table
[0].dwInUcastPkts
, pIfTable
->table
[0].dwOutUcastPkts
);
410 _tprintf(_T("%-20s %14lu %15lu\n"), _T("Non-unicast packets"),
411 pIfTable
->table
[0].dwInNUcastPkts
, pIfTable
->table
[0].dwOutNUcastPkts
);
412 _tprintf(_T("%-20s %14lu %15lu\n"), _T("Discards"),
413 pIfTable
->table
[0].dwInDiscards
, pIfTable
->table
[0].dwOutDiscards
);
414 _tprintf(_T("%-20s %14lu %15lu\n"), _T("Errors"),
415 pIfTable
->table
[0].dwInErrors
, pIfTable
->table
[0].dwOutErrors
);
416 _tprintf(_T("%-20s %14lu\n"), _T("Unknown Protocols"),
417 pIfTable
->table
[0].dwInUnknownProtos
);
420 DoFormatMessage(dwRetVal
);
422 HeapFree(GetProcessHeap(), 0, pIfTable
);
427 PMIB_TCPTABLE_OWNER_PID tcpTable
;
430 CHAR HostIp
[HOSTNAMELEN
], HostPort
[PORTNAMELEN
];
431 CHAR RemoteIp
[HOSTNAMELEN
], RemotePort
[PORTNAMELEN
];
432 CHAR Host
[ADDRESSLEN
];
433 CHAR Remote
[ADDRESSLEN
];
436 /* Get the table of TCP endpoints */
437 dwSize
= sizeof (MIB_TCPTABLE_OWNER_PID
);
438 /* Should also work when we get new connections between 2 GetTcpTable()
442 tcpTable
= (PMIB_TCPTABLE_OWNER_PID
) HeapAlloc(GetProcessHeap(), 0, dwSize
);
443 error
= GetExtendedTcpTable(tcpTable
, &dwSize
, TRUE
, AF_INET
, TCP_TABLE_OWNER_PID_ALL
, 0);
444 if ( error
!= NO_ERROR
)
445 HeapFree(GetProcessHeap(), 0, tcpTable
);
447 while ( error
== ERROR_INSUFFICIENT_BUFFER
);
449 if (error
!= NO_ERROR
)
451 printf("Failed to snapshot TCP endpoints.\n");
452 DoFormatMessage(error
);
456 /* Dump the TCP table */
457 for (i
= 0; i
< tcpTable
->dwNumEntries
; i
++)
459 /* If we aren't showing all connections, only display established, close wait
460 * and time wait. This is the default output for netstat */
461 if (bDoShowAllCons
|| (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_ESTAB
)
462 || (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_CLOSE_WAIT
)
463 || (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_TIME_WAIT
))
465 /* I've split this up so it's easier to follow */
466 GetIpHostName(TRUE
, tcpTable
->table
[i
].dwLocalAddr
, HostIp
, HOSTNAMELEN
);
467 GetPortName(tcpTable
->table
[i
].dwLocalPort
, "tcp", HostPort
, PORTNAMELEN
);
468 sprintf(Host
, "%s:%s", HostIp
, HostPort
);
470 if (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_LISTEN
)
472 sprintf(Remote
, "%s:0", HostIp
);
476 GetIpHostName(FALSE
, tcpTable
->table
[i
].dwRemoteAddr
, RemoteIp
, HOSTNAMELEN
);
477 GetPortName(tcpTable
->table
[i
].dwRemotePort
, "tcp", RemotePort
, PORTNAMELEN
);
478 sprintf(Remote
, "%s:%s", RemoteIp
, RemotePort
);
481 if (bDoShowProcessId
)
483 sprintf(PID
, "%ld", tcpTable
->table
[i
].dwOwningPid
);
490 _tprintf(_T(" %-6s %-22s %-22s %-11s %s\n"), _T("TCP"),
491 Host
, Remote
, TcpState
[tcpTable
->table
[i
].dwState
], PID
);
494 HeapFree(GetProcessHeap(), 0, tcpTable
);
499 PMIB_UDPTABLE udpTable
;
502 CHAR HostIp
[HOSTNAMELEN
], HostPort
[PORTNAMELEN
];
503 CHAR Host
[ADDRESSLEN
];
505 /* Get the table of UDP endpoints */
507 error
= GetUdpTable(NULL
, &dwSize
, TRUE
);
508 if (error
!= ERROR_INSUFFICIENT_BUFFER
)
510 printf("Failed to snapshot UDP endpoints.\n");
511 DoFormatMessage(error
);
514 udpTable
= (PMIB_UDPTABLE
) HeapAlloc(GetProcessHeap(), 0, dwSize
);
515 error
= GetUdpTable(udpTable
, &dwSize
, TRUE
);
518 printf("Failed to snapshot UDP endpoints table.\n");
519 DoFormatMessage(error
);
520 HeapFree(GetProcessHeap(), 0, udpTable
);
524 /* Dump the UDP table */
525 for (i
= 0; i
< udpTable
->dwNumEntries
; i
++)
528 /* I've split this up so it's easier to follow */
529 GetIpHostName(TRUE
, udpTable
->table
[i
].dwLocalAddr
, HostIp
, HOSTNAMELEN
);
530 GetPortName(udpTable
->table
[i
].dwLocalPort
, "tcp", HostPort
, PORTNAMELEN
);
532 sprintf(Host
, "%s:%s", HostIp
, HostPort
);
534 _tprintf(_T(" %-6s %-22s %-22s\n"), _T("UDP"), Host
, _T("*:*"));
537 HeapFree(GetProcessHeap(), 0, udpTable
);
541 * Translate port numbers into their text equivalent if there is one
544 GetPortName(UINT Port
, PCSTR Proto
, CHAR Name
[], INT NameLen
)
546 struct servent
*pServent
;
550 sprintf(Name
, "%d", htons((WORD
)Port
));
553 /* Try to translate to a name */
554 if ((pServent
= getservbyport(Port
, Proto
)))
555 strcpy(Name
, pServent
->s_name
);
557 sprintf(Name
, "%d", htons((WORD
)Port
));
562 * convert addresses into dotted decimal or hostname
565 GetIpHostName(BOOL Local
, UINT IpAddr
, CHAR Name
[], int NameLen
)
567 // struct hostent *phostent;
570 /* display dotted decimal */
571 nIpAddr
= htonl(IpAddr
);
572 if (bDoShowNumbers
) {
573 sprintf(Name
, "%d.%d.%d.%d",
574 (nIpAddr
>> 24) & 0xFF,
575 (nIpAddr
>> 16) & 0xFF,
576 (nIpAddr
>> 8) & 0xFF,
583 /* try to resolve the name */
586 sprintf(Name
, "%d.%d.%d.%d",
587 (nIpAddr
>> 24) & 0xFF,
588 (nIpAddr
>> 16) & 0xFF,
589 (nIpAddr
>> 8) & 0xFF,
592 if (gethostname(Name
, NameLen
) != 0)
593 DoFormatMessage(WSAGetLastError());
595 } else if (IpAddr
== 0x0100007f) {
597 if (gethostname(Name
, NameLen
) != 0)
598 DoFormatMessage(WSAGetLastError());
600 _tcsncpy(Name
, _T("localhost"), 10);
602 // } else if (phostent = gethostbyaddr((char*)&ipaddr, sizeof(nipaddr), PF_INET)) {
603 // strcpy(name, phostent->h_name);
605 sprintf(Name
, "%d.%d.%d.%d",
606 ((nIpAddr
>> 24) & 0x000000FF),
607 ((nIpAddr
>> 16) & 0x000000FF),
608 ((nIpAddr
>> 8) & 0x000000FF),
609 ((nIpAddr
) & 0x000000FF));
616 _tprintf(_T("\nDisplays current TCP/IP protocol statistics and network connections.\n\n"
617 "NETSTAT [-a] [-e] [-n] [-p proto] [-r] [-s] [interval]\n\n"
618 " -a Displays all connections and listening ports.\n"
619 " -e Displays Ethernet statistics. May be combined with -s\n"
621 " -n Displays address and port numbers in numeric form.\n"
622 " -p proto Shows connections for protocol 'proto' TCP or UDP.\n"
623 " If used with the -s option to display\n"
624 " per-protocol statistics, 'proto' may be TCP, UDP, or IP.\n"
625 " -r Displays the current routing table.\n"
626 " -s Displays per-protocol statistics. By default, Statistics are\n"
627 " shown for IP, ICMP, TCP and UDP;\n"
628 " the -p option may be used to specify a subset of the default.\n"
629 " -o Displays the process ID for each connection.\n"
630 " interval Redisplays selected statistics every 'interval' seconds.\n"
631 " Press CTRL+C to stop redisplaying. By default netstat will\n"
632 " print the current information only once.\n"));
637 * Parse command line parameters and set any options
638 * Run display output, looping over set intervals if a number is given
641 int main(int argc
, char *argv
[])
645 if (WSAStartup(MAKEWORD(2, 2), &wsaData
) != 0)
647 _tprintf(_T("WSAStartup() failed : %d\n"), WSAGetLastError());
651 if (ParseCmdline(argc
, argv
))
660 Sleep(Interval
*1000);