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
32 enum ProtoType
{IP
, TCP
, UDP
, ICMP
} Protocol
;
33 DWORD Interval
; /* time to pause between printing output */
35 /* TCP endpoint states */
53 * format message string and display output
55 VOID
DoFormatMessage(DWORD ErrorCode
)
57 if (ErrorCode
== ERROR_SUCCESS
)
60 ConMsgPuts(StdErr
, FORMAT_MESSAGE_FROM_SYSTEM
,
61 NULL
, ErrorCode
, LANG_USER_DEFAULT
);
66 * Parse command line parameters and set any options
69 BOOL
ParseCmdline(int argc
, wchar_t* argv
[])
75 if ((argc
== 1) || (iswdigit(*argv
[1])))
78 /* Parse command line for options we have been given. */
79 for (i
= 1; i
< argc
; i
++)
81 if ((argc
> 1) && (argv
[i
][0] == L
'-' || argv
[i
][0] == L
'/'))
83 while ((c
= *++argv
[i
]) != L
'\0')
88 bDoShowAllCons
= TRUE
;
91 bDoShowProcName
= TRUE
;
94 bDoShowEthStats
= TRUE
;
97 bDoShowNumbers
= TRUE
;
100 bDoShowProtoCons
= TRUE
;
102 if (!_wcsicmp(L
"IP", Proto
))
104 else if (!_wcsicmp(L
"ICMP", Proto
))
106 else if (!_wcsicmp(L
"TCP", Proto
))
108 else if (!_wcsicmp(L
"UDP", Proto
))
112 ConResPuts(StdOut
, IDS_USAGE
);
117 bDoShowRouteTable
= TRUE
;
120 bDoShowProtoStats
= TRUE
;
123 bDoShowProcessId
= TRUE
;
127 ConPuts(StdOut
, L
"got v\n");
128 bDoDispSeqComp
= TRUE
;
131 ConResPuts(StdOut
, IDS_USAGE
);
136 else if (iswdigit(*argv
[i
]) != 0)
138 if (swscanf(argv
[i
], L
"%lu", &Interval
) != EOF
)
145 // ConResPrintf(StdOut, IDS_USAGE);
146 // return EXIT_FAILURE;
154 * Display table header
156 VOID
DisplayTableHeader(VOID
)
158 ConResPuts(StdOut
, IDS_DISPLAY_THEADER
);
159 if (bDoShowProcessId
)
160 ConResPuts(StdOut
, IDS_DISPLAY_PROCESS
);
162 ConPuts(StdOut
, L
"\n");
166 * Simulate Microsofts netstat utility output
168 BOOL
DisplayOutput(VOID
)
172 DisplayTableHeader();
177 if (bDoShowRouteTable
)
179 /* mingw doesn't have lib for _tsystem */
180 if (system("route print") == -1)
182 ConResPuts(StdErr
, IDS_ERROR_ROUTE
);
190 ShowEthernetStatistics();
194 if (bDoShowProtoCons
)
199 if (bDoShowProtoStats
)
206 if (bDoShowProtoStats
)
208 ShowIcmpStatistics();
213 if (bDoShowProtoStats
)
215 ConResPuts(StdOut
, IDS_ACTIVE_CONNECT
);
216 DisplayTableHeader();
220 if (bDoShowProtoStats
)
222 ConResPuts(StdOut
, IDS_ACTIVE_CONNECT
);
223 DisplayTableHeader();
230 else if (bDoShowProtoStats
)
233 ShowIcmpStatistics();
240 ConResPuts(StdOut
, IDS_ACTIVE_CONNECT
);
241 DisplayTableHeader();
249 VOID
ShowIpStatistics(VOID
)
251 PMIB_IPSTATS pIpStats
;
254 pIpStats
= (MIB_IPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPSTATS
));
256 if ((dwRetVal
= GetIpStatistics(pIpStats
)) == NO_ERROR
)
258 ConResPuts(StdOut
, IDS_IP4_STAT_HEADER
);
259 ConResPrintf(StdOut
, IDS_IP_PACK_REC
, pIpStats
->dwInReceives
);
260 ConResPrintf(StdOut
, IDS_IP_HEAD_REC_ERROR
, pIpStats
->dwInHdrErrors
);
261 ConResPrintf(StdOut
, IDS_IP_ADDR_REC_ERROR
, pIpStats
->dwInAddrErrors
);
262 ConResPrintf(StdOut
, IDS_IP_DATAG_FWD
, pIpStats
->dwForwDatagrams
);
263 ConResPrintf(StdOut
, IDS_IP_UNKNOWN_PRO_REC
, pIpStats
->dwInUnknownProtos
);
264 ConResPrintf(StdOut
, IDS_IP_REC_PACK_DISCARD
, pIpStats
->dwInDiscards
);
265 ConResPrintf(StdOut
, IDS_IP_REC_PACK_DELIVER
, pIpStats
->dwInDelivers
);
266 ConResPrintf(StdOut
, IDS_IP_OUT_REQUEST
, pIpStats
->dwOutRequests
);
267 ConResPrintf(StdOut
, IDS_IP_ROUTE_DISCARD
, pIpStats
->dwRoutingDiscards
);
268 ConResPrintf(StdOut
, IDS_IP_DISCARD_OUT_PACK
, pIpStats
->dwOutDiscards
);
269 ConResPrintf(StdOut
, IDS_IP_OUT_PACKET_NO_ROUTE
, pIpStats
->dwOutNoRoutes
);
270 ConResPrintf(StdOut
, IDS_IP_REASSEMBLE_REQUIRED
, pIpStats
->dwReasmReqds
);
271 ConResPrintf(StdOut
, IDS_IP_REASSEMBLE_SUCCESS
, pIpStats
->dwReasmOks
);
272 ConResPrintf(StdOut
, IDS_IP_REASSEMBLE_FAILURE
, pIpStats
->dwReasmFails
);
273 ConResPrintf(StdOut
, IDS_IP_DATAG_FRAG_SUCCESS
, pIpStats
->dwFragOks
);
274 ConResPrintf(StdOut
, IDS_IP_DATAG_FRAG_FAILURE
, pIpStats
->dwFragFails
);
275 ConResPrintf(StdOut
, IDS_IP_DATAG_FRAG_CREATE
, pIpStats
->dwFragCreates
);
279 DoFormatMessage(dwRetVal
);
282 HeapFree(GetProcessHeap(), 0, pIpStats
);
285 VOID
ShowIcmpStatistics(VOID
)
287 PMIB_ICMP pIcmpStats
;
290 pIcmpStats
= (MIB_ICMP
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_ICMP
));
292 if ((dwRetVal
= GetIcmpStatistics(pIcmpStats
)) == NO_ERROR
)
294 ConResPuts(StdOut
, IDS_ICMP4_STAT_HEADER
);
295 ConResPuts(StdOut
, IDS_ICMP_THEADER
);
296 ConResPrintf(StdOut
, IDS_ICMP_MSG
,
297 pIcmpStats
->stats
.icmpInStats
.dwMsgs
, pIcmpStats
->stats
.icmpOutStats
.dwMsgs
);
298 ConResPrintf(StdOut
, IDS_ICMP_ERROR
,
299 pIcmpStats
->stats
.icmpInStats
.dwErrors
, pIcmpStats
->stats
.icmpOutStats
.dwErrors
);
300 ConResPrintf(StdOut
, IDS_ICMP_DEST_UNREACH
,
301 pIcmpStats
->stats
.icmpInStats
.dwDestUnreachs
, pIcmpStats
->stats
.icmpOutStats
.dwDestUnreachs
);
302 ConResPrintf(StdOut
, IDS_ICMP_TIME_EXCEED
,
303 pIcmpStats
->stats
.icmpInStats
.dwTimeExcds
, pIcmpStats
->stats
.icmpOutStats
.dwTimeExcds
);
304 ConResPrintf(StdOut
, IDS_ICMP_PARAM_PROBLEM
,
305 pIcmpStats
->stats
.icmpInStats
.dwParmProbs
, pIcmpStats
->stats
.icmpOutStats
.dwParmProbs
);
306 ConResPrintf(StdOut
, IDS_ICMP_SRC_QUENCHES
,
307 pIcmpStats
->stats
.icmpInStats
.dwSrcQuenchs
, pIcmpStats
->stats
.icmpOutStats
.dwSrcQuenchs
);
308 ConResPrintf(StdOut
, IDS_ICMP_REDIRECT
,
309 pIcmpStats
->stats
.icmpInStats
.dwRedirects
, pIcmpStats
->stats
.icmpOutStats
.dwRedirects
);
310 ConResPrintf(StdOut
, IDS_ICMP_ECHO
,
311 pIcmpStats
->stats
.icmpInStats
.dwEchos
, pIcmpStats
->stats
.icmpOutStats
.dwEchos
);
312 ConResPrintf(StdOut
, IDS_ICMP_ECHO_REPLY
,
313 pIcmpStats
->stats
.icmpInStats
.dwEchoReps
, pIcmpStats
->stats
.icmpOutStats
.dwEchoReps
);
314 ConResPrintf(StdOut
, IDS_ICMP_TIMESTAMP
,
315 pIcmpStats
->stats
.icmpInStats
.dwTimestamps
, pIcmpStats
->stats
.icmpOutStats
.dwTimestamps
);
316 ConResPrintf(StdOut
, IDS_ICMP_TIMESTAMP_REPLY
,
317 pIcmpStats
->stats
.icmpInStats
.dwTimestampReps
, pIcmpStats
->stats
.icmpOutStats
.dwTimestampReps
);
318 ConResPrintf(StdOut
, IDS_ICMP_ADDRESSS_MASK
,
319 pIcmpStats
->stats
.icmpInStats
.dwAddrMasks
, pIcmpStats
->stats
.icmpOutStats
.dwAddrMasks
);
320 ConResPrintf(StdOut
, IDS_ICMP_ADDRESSS_MASK_REPLY
,
321 pIcmpStats
->stats
.icmpInStats
.dwAddrMaskReps
, pIcmpStats
->stats
.icmpOutStats
.dwAddrMaskReps
);
325 DoFormatMessage(dwRetVal
);
328 HeapFree(GetProcessHeap(), 0, pIcmpStats
);
332 VOID
ShowTcpStatistics(VOID
)
334 PMIB_TCPSTATS pTcpStats
;
337 pTcpStats
= (MIB_TCPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_TCPSTATS
));
339 if ((dwRetVal
= GetTcpStatistics(pTcpStats
)) == NO_ERROR
)
341 ConResPuts(StdOut
, IDS_TCP4_HEADER
);
342 ConResPrintf(StdOut
, IDS_TCP_ACTIVE_OPEN
, pTcpStats
->dwActiveOpens
);
343 ConResPrintf(StdOut
, IDS_TCP_PASS_OPEN
, pTcpStats
->dwPassiveOpens
);
344 ConResPrintf(StdOut
, IDS_TCP_FAIL_CONNECT
, pTcpStats
->dwAttemptFails
);
345 ConResPrintf(StdOut
, IDS_TCP_RESET_CONNECT
, pTcpStats
->dwEstabResets
);
346 ConResPrintf(StdOut
, IDS_TCP_CURRENT_CONNECT
, pTcpStats
->dwCurrEstab
);
347 ConResPrintf(StdOut
, IDS_TCP_SEG_RECEIVE
, pTcpStats
->dwInSegs
);
348 ConResPrintf(StdOut
, IDS_TCP_SEG_SENT
, pTcpStats
->dwOutSegs
);
349 ConResPrintf(StdOut
, IDS_TCP_SEG_RETRANSMIT
, pTcpStats
->dwRetransSegs
);
353 DoFormatMessage(dwRetVal
);
356 HeapFree(GetProcessHeap(), 0, pTcpStats
);
359 VOID
ShowUdpStatistics(VOID
)
361 PMIB_UDPSTATS pUdpStats
;
364 pUdpStats
= (MIB_UDPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_UDPSTATS
));
366 if ((dwRetVal
= GetUdpStatistics(pUdpStats
)) == NO_ERROR
)
368 ConResPuts(StdOut
, IDS_UDP_IP4_HEADER
);
369 ConResPrintf(StdOut
, IDS_UDP_DATAG_RECEIVE
, pUdpStats
->dwInDatagrams
);
370 ConResPrintf(StdOut
, IDS_UDP_NO_PORT
, pUdpStats
->dwNoPorts
);
371 ConResPrintf(StdOut
, IDS_UDP_RECEIVE_ERROR
, pUdpStats
->dwInErrors
);
372 ConResPrintf(StdOut
, IDS_UDP_DATAG_SEND
, pUdpStats
->dwOutDatagrams
);
376 DoFormatMessage(dwRetVal
);
379 HeapFree(GetProcessHeap(), 0, pUdpStats
);
382 VOID
ShowEthernetStatistics(VOID
)
384 PMIB_IFTABLE pIfTable
;
388 pIfTable
= (MIB_IFTABLE
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IFTABLE
));
390 if (GetIfTable(pIfTable
, &dwSize
, 0) == ERROR_INSUFFICIENT_BUFFER
)
392 HeapFree(GetProcessHeap(), 0, pIfTable
);
393 pIfTable
= (MIB_IFTABLE
*) HeapAlloc(GetProcessHeap(), 0, dwSize
);
395 if ((dwRetVal
= GetIfTable(pIfTable
, &dwSize
, 0)) == NO_ERROR
)
397 ConResPuts(StdOut
, IDS_ETHERNET_INTERFACE_STAT
);
398 ConResPuts(StdOut
, IDS_ETHERNET_THEADER
);
399 ConResPrintf(StdOut
, IDS_ETHERNET_BYTES
,
400 pIfTable
->table
[0].dwInOctets
, pIfTable
->table
[0].dwOutOctets
);
401 ConResPrintf(StdOut
, IDS_ETHERNET_UNICAST_PACKET
,
402 pIfTable
->table
[0].dwInUcastPkts
, pIfTable
->table
[0].dwOutUcastPkts
);
403 ConResPrintf(StdOut
, IDS_ETHERNET_NON_UNICAST_PACKET
,
404 pIfTable
->table
[0].dwInNUcastPkts
, pIfTable
->table
[0].dwOutNUcastPkts
);
405 ConResPrintf(StdOut
, IDS_ETHERNET_DISCARD
,
406 pIfTable
->table
[0].dwInDiscards
, pIfTable
->table
[0].dwOutDiscards
);
407 ConResPrintf(StdOut
, IDS_ETHERNET_ERROR
,
408 pIfTable
->table
[0].dwInErrors
, pIfTable
->table
[0].dwOutErrors
);
409 ConResPrintf(StdOut
, IDS_ETHERNET_UNKNOWN
,
410 pIfTable
->table
[0].dwInUnknownProtos
);
414 DoFormatMessage(dwRetVal
);
417 HeapFree(GetProcessHeap(), 0, pIfTable
);
420 VOID
ShowTcpTable(VOID
)
422 PMIB_TCPTABLE_OWNER_PID tcpTable
;
425 CHAR HostIp
[HOSTNAMELEN
], HostPort
[PORTNAMELEN
];
426 CHAR RemoteIp
[HOSTNAMELEN
], RemotePort
[PORTNAMELEN
];
427 CHAR Host
[ADDRESSLEN
];
428 CHAR Remote
[ADDRESSLEN
];
431 /* Get the table of TCP endpoints */
432 dwSize
= sizeof (MIB_TCPTABLE_OWNER_PID
);
433 /* Should also work when we get new connections between 2 GetTcpTable()
437 tcpTable
= (PMIB_TCPTABLE_OWNER_PID
) HeapAlloc(GetProcessHeap(), 0, dwSize
);
438 error
= GetExtendedTcpTable(tcpTable
, &dwSize
, TRUE
, AF_INET
, TCP_TABLE_OWNER_PID_ALL
, 0);
439 if ( error
!= NO_ERROR
)
440 HeapFree(GetProcessHeap(), 0, tcpTable
);
442 while ( error
== ERROR_INSUFFICIENT_BUFFER
);
444 if (error
!= NO_ERROR
)
446 ConResPrintf(StdErr
, IDS_ERROR_TCP_SNAPSHOT
);
447 DoFormatMessage(error
);
451 /* Dump the TCP table */
452 for (i
= 0; i
< tcpTable
->dwNumEntries
; i
++)
454 /* If we aren't showing all connections, only display established, close wait
455 * and time wait. This is the default output for netstat */
456 if (bDoShowAllCons
|| (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_ESTAB
)
457 || (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_CLOSE_WAIT
)
458 || (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_TIME_WAIT
))
460 /* I've split this up so it's easier to follow */
461 GetIpHostName(TRUE
, tcpTable
->table
[i
].dwLocalAddr
, HostIp
, HOSTNAMELEN
);
462 GetPortName(tcpTable
->table
[i
].dwLocalPort
, "tcp", HostPort
, PORTNAMELEN
);
463 sprintf(Host
, "%s:%s", HostIp
, HostPort
);
465 if (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_LISTEN
)
467 sprintf(Remote
, "%s:0", HostIp
);
471 GetIpHostName(FALSE
, tcpTable
->table
[i
].dwRemoteAddr
, RemoteIp
, HOSTNAMELEN
);
472 GetPortName(tcpTable
->table
[i
].dwRemotePort
, "tcp", RemotePort
, PORTNAMELEN
);
473 sprintf(Remote
, "%s:%s", RemoteIp
, RemotePort
);
476 if (bDoShowProcessId
)
478 sprintf(PID
, "%ld", tcpTable
->table
[i
].dwOwningPid
);
485 ConPrintf(StdOut
, L
" %-6s %-22S %-22S %-11s %S\n", L
"TCP",
486 Host
, Remote
, TcpState
[tcpTable
->table
[i
].dwState
], PID
);
489 HeapFree(GetProcessHeap(), 0, tcpTable
);
492 VOID
ShowUdpTable(VOID
)
494 PMIB_UDPTABLE_OWNER_PID udpTable
;
497 CHAR HostIp
[HOSTNAMELEN
], HostPort
[PORTNAMELEN
];
498 CHAR Host
[ADDRESSLEN
];
501 /* Get the table of UDP endpoints */
503 error
= GetExtendedUdpTable(NULL
, &dwSize
, TRUE
, AF_INET
, UDP_TABLE_OWNER_PID
, 0);
504 if (error
!= ERROR_INSUFFICIENT_BUFFER
)
506 ConResPuts(StdErr
, IDS_ERROR_UDP_ENDPOINT
);
507 DoFormatMessage(error
);
510 udpTable
= (PMIB_UDPTABLE_OWNER_PID
) HeapAlloc(GetProcessHeap(), 0, dwSize
);
511 error
= GetExtendedUdpTable(udpTable
, &dwSize
, TRUE
, AF_INET
, UDP_TABLE_OWNER_PID
, 0);
514 ConResPuts(StdErr
, IDS_ERROR_UDP_ENDPOINT_TABLE
);
515 DoFormatMessage(error
);
516 HeapFree(GetProcessHeap(), 0, udpTable
);
520 /* Dump the UDP table */
521 for (i
= 0; i
< udpTable
->dwNumEntries
; i
++)
524 /* I've split this up so it's easier to follow */
525 GetIpHostName(TRUE
, udpTable
->table
[i
].dwLocalAddr
, HostIp
, HOSTNAMELEN
);
526 GetPortName(udpTable
->table
[i
].dwLocalPort
, "tcp", HostPort
, PORTNAMELEN
);
528 sprintf(Host
, "%s:%s", HostIp
, HostPort
);
530 if (bDoShowProcessId
)
532 sprintf(PID
, "%ld", udpTable
->table
[i
].dwOwningPid
);
539 ConPrintf(StdOut
, L
" %-6s %-22S %-34s %S\n", L
"UDP", Host
, L
"*:*", PID
);
542 HeapFree(GetProcessHeap(), 0, udpTable
);
546 * Translate port numbers into their text equivalent if there is one
549 GetPortName(UINT Port
, PCSTR Proto
, CHAR Name
[], INT NameLen
)
551 struct servent
*pServent
;
555 sprintf(Name
, "%d", htons((WORD
)Port
));
558 /* Try to translate to a name */
559 if ((pServent
= getservbyport(Port
, Proto
)))
560 strcpy(Name
, pServent
->s_name
);
562 sprintf(Name
, "%d", htons((WORD
)Port
));
567 * convert addresses into dotted decimal or hostname
570 GetIpHostName(BOOL Local
, UINT IpAddr
, CHAR Name
[], int NameLen
)
572 // struct hostent *phostent;
575 /* display dotted decimal */
576 nIpAddr
= htonl(IpAddr
);
577 if (bDoShowNumbers
) {
578 sprintf(Name
, "%d.%d.%d.%d",
579 (nIpAddr
>> 24) & 0xFF,
580 (nIpAddr
>> 16) & 0xFF,
581 (nIpAddr
>> 8) & 0xFF,
588 /* try to resolve the name */
591 sprintf(Name
, "%d.%d.%d.%d",
592 (nIpAddr
>> 24) & 0xFF,
593 (nIpAddr
>> 16) & 0xFF,
594 (nIpAddr
>> 8) & 0xFF,
597 if (gethostname(Name
, NameLen
) != 0)
598 DoFormatMessage(WSAGetLastError());
600 } else if (IpAddr
== 0x0100007f) {
602 if (gethostname(Name
, NameLen
) != 0)
603 DoFormatMessage(WSAGetLastError());
605 strncpy(Name
, "localhost", 10);
607 // } else if (phostent = gethostbyaddr((char*)&ipaddr, sizeof(nipaddr), PF_INET)) {
608 // strcpy(name, phostent->h_name);
610 sprintf(Name
, "%d.%d.%d.%d",
611 ((nIpAddr
>> 24) & 0x000000FF),
612 ((nIpAddr
>> 16) & 0x000000FF),
613 ((nIpAddr
>> 8) & 0x000000FF),
614 ((nIpAddr
) & 0x000000FF));
621 * Parse command line parameters and set any options
622 * Run display output, looping over set intervals if a number is given
625 int wmain(int argc
, wchar_t *argv
[])
629 /* Initialize the Console Standard Streams */
632 if (WSAStartup(MAKEWORD(2, 2), &wsaData
) != 0)
634 ConResPrintf(StdErr
, IDS_ERROR_WSA_START
, WSAGetLastError());
638 if (ParseCmdline(argc
, argv
))
647 Sleep(Interval
*1000);