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
20 #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);
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();
173 return ShowTcpTable();
176 if (bDoShowRouteTable
)
178 if (_wsystem(L
"route print") == -1)
180 ConResPuts(StdErr
, IDS_ERROR_ROUTE
);
188 ShowEthernetStatistics();
192 if (bDoShowProtoCons
)
197 if (bDoShowProtoStats
)
201 if (bDoShowProtoStats
)
202 ShowIcmpStatistics();
205 if (bDoShowProtoStats
)
207 ConResPuts(StdOut
, IDS_ACTIVE_CONNECT
);
208 DisplayTableHeader();
209 return ShowTcpTable();
211 if (bDoShowProtoStats
)
213 ConResPuts(StdOut
, IDS_ACTIVE_CONNECT
);
214 DisplayTableHeader();
215 return (bDoShowAllCons
? ShowUdpTable() : TRUE
);
220 else if (bDoShowProtoStats
)
223 ShowIcmpStatistics();
230 ConResPuts(StdOut
, IDS_ACTIVE_CONNECT
);
231 DisplayTableHeader();
232 if (ShowTcpTable() && bDoShowAllCons
)
239 VOID
ShowIpStatistics(VOID
)
241 PMIB_IPSTATS pIpStats
;
244 pIpStats
= (MIB_IPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPSTATS
));
246 if ((dwRetVal
= GetIpStatistics(pIpStats
)) == NO_ERROR
)
248 ConResPuts(StdOut
, IDS_IP4_STAT_HEADER
);
249 ConResPrintf(StdOut
, IDS_IP_PACK_REC
, pIpStats
->dwInReceives
);
250 ConResPrintf(StdOut
, IDS_IP_HEAD_REC_ERROR
, pIpStats
->dwInHdrErrors
);
251 ConResPrintf(StdOut
, IDS_IP_ADDR_REC_ERROR
, pIpStats
->dwInAddrErrors
);
252 ConResPrintf(StdOut
, IDS_IP_DATAG_FWD
, pIpStats
->dwForwDatagrams
);
253 ConResPrintf(StdOut
, IDS_IP_UNKNOWN_PRO_REC
, pIpStats
->dwInUnknownProtos
);
254 ConResPrintf(StdOut
, IDS_IP_REC_PACK_DISCARD
, pIpStats
->dwInDiscards
);
255 ConResPrintf(StdOut
, IDS_IP_REC_PACK_DELIVER
, pIpStats
->dwInDelivers
);
256 ConResPrintf(StdOut
, IDS_IP_OUT_REQUEST
, pIpStats
->dwOutRequests
);
257 ConResPrintf(StdOut
, IDS_IP_ROUTE_DISCARD
, pIpStats
->dwRoutingDiscards
);
258 ConResPrintf(StdOut
, IDS_IP_DISCARD_OUT_PACK
, pIpStats
->dwOutDiscards
);
259 ConResPrintf(StdOut
, IDS_IP_OUT_PACKET_NO_ROUTE
, pIpStats
->dwOutNoRoutes
);
260 ConResPrintf(StdOut
, IDS_IP_REASSEMBLE_REQUIRED
, pIpStats
->dwReasmReqds
);
261 ConResPrintf(StdOut
, IDS_IP_REASSEMBLE_SUCCESS
, pIpStats
->dwReasmOks
);
262 ConResPrintf(StdOut
, IDS_IP_REASSEMBLE_FAILURE
, pIpStats
->dwReasmFails
);
263 ConResPrintf(StdOut
, IDS_IP_DATAG_FRAG_SUCCESS
, pIpStats
->dwFragOks
);
264 ConResPrintf(StdOut
, IDS_IP_DATAG_FRAG_FAILURE
, pIpStats
->dwFragFails
);
265 ConResPrintf(StdOut
, IDS_IP_DATAG_FRAG_CREATE
, pIpStats
->dwFragCreates
);
269 DoFormatMessage(dwRetVal
);
272 HeapFree(GetProcessHeap(), 0, pIpStats
);
275 VOID
ShowIcmpStatistics(VOID
)
277 PMIB_ICMP pIcmpStats
;
280 pIcmpStats
= (MIB_ICMP
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_ICMP
));
282 if ((dwRetVal
= GetIcmpStatistics(pIcmpStats
)) == NO_ERROR
)
284 ConResPuts(StdOut
, IDS_ICMP4_STAT_HEADER
);
285 ConResPuts(StdOut
, IDS_ICMP_THEADER
);
286 ConResPrintf(StdOut
, IDS_ICMP_MSG
,
287 pIcmpStats
->stats
.icmpInStats
.dwMsgs
, pIcmpStats
->stats
.icmpOutStats
.dwMsgs
);
288 ConResPrintf(StdOut
, IDS_ICMP_ERROR
,
289 pIcmpStats
->stats
.icmpInStats
.dwErrors
, pIcmpStats
->stats
.icmpOutStats
.dwErrors
);
290 ConResPrintf(StdOut
, IDS_ICMP_DEST_UNREACH
,
291 pIcmpStats
->stats
.icmpInStats
.dwDestUnreachs
, pIcmpStats
->stats
.icmpOutStats
.dwDestUnreachs
);
292 ConResPrintf(StdOut
, IDS_ICMP_TIME_EXCEED
,
293 pIcmpStats
->stats
.icmpInStats
.dwTimeExcds
, pIcmpStats
->stats
.icmpOutStats
.dwTimeExcds
);
294 ConResPrintf(StdOut
, IDS_ICMP_PARAM_PROBLEM
,
295 pIcmpStats
->stats
.icmpInStats
.dwParmProbs
, pIcmpStats
->stats
.icmpOutStats
.dwParmProbs
);
296 ConResPrintf(StdOut
, IDS_ICMP_SRC_QUENCHES
,
297 pIcmpStats
->stats
.icmpInStats
.dwSrcQuenchs
, pIcmpStats
->stats
.icmpOutStats
.dwSrcQuenchs
);
298 ConResPrintf(StdOut
, IDS_ICMP_REDIRECT
,
299 pIcmpStats
->stats
.icmpInStats
.dwRedirects
, pIcmpStats
->stats
.icmpOutStats
.dwRedirects
);
300 ConResPrintf(StdOut
, IDS_ICMP_ECHO
,
301 pIcmpStats
->stats
.icmpInStats
.dwEchos
, pIcmpStats
->stats
.icmpOutStats
.dwEchos
);
302 ConResPrintf(StdOut
, IDS_ICMP_ECHO_REPLY
,
303 pIcmpStats
->stats
.icmpInStats
.dwEchoReps
, pIcmpStats
->stats
.icmpOutStats
.dwEchoReps
);
304 ConResPrintf(StdOut
, IDS_ICMP_TIMESTAMP
,
305 pIcmpStats
->stats
.icmpInStats
.dwTimestamps
, pIcmpStats
->stats
.icmpOutStats
.dwTimestamps
);
306 ConResPrintf(StdOut
, IDS_ICMP_TIMESTAMP_REPLY
,
307 pIcmpStats
->stats
.icmpInStats
.dwTimestampReps
, pIcmpStats
->stats
.icmpOutStats
.dwTimestampReps
);
308 ConResPrintf(StdOut
, IDS_ICMP_ADDRESSS_MASK
,
309 pIcmpStats
->stats
.icmpInStats
.dwAddrMasks
, pIcmpStats
->stats
.icmpOutStats
.dwAddrMasks
);
310 ConResPrintf(StdOut
, IDS_ICMP_ADDRESSS_MASK_REPLY
,
311 pIcmpStats
->stats
.icmpInStats
.dwAddrMaskReps
, pIcmpStats
->stats
.icmpOutStats
.dwAddrMaskReps
);
315 DoFormatMessage(dwRetVal
);
318 HeapFree(GetProcessHeap(), 0, pIcmpStats
);
322 VOID
ShowTcpStatistics(VOID
)
324 MIB_TCPSTATS tcpStats
;
327 if ((dwRetVal
= GetTcpStatistics(&tcpStats
)) == NO_ERROR
)
329 ConResPuts(StdOut
, IDS_TCP4_HEADER
);
330 ConResPrintf(StdOut
, IDS_TCP_ACTIVE_OPEN
, tcpStats
.dwActiveOpens
);
331 ConResPrintf(StdOut
, IDS_TCP_PASS_OPEN
, tcpStats
.dwPassiveOpens
);
332 ConResPrintf(StdOut
, IDS_TCP_FAIL_CONNECT
, tcpStats
.dwAttemptFails
);
333 ConResPrintf(StdOut
, IDS_TCP_RESET_CONNECT
, tcpStats
.dwEstabResets
);
334 ConResPrintf(StdOut
, IDS_TCP_CURRENT_CONNECT
, tcpStats
.dwCurrEstab
);
335 ConResPrintf(StdOut
, IDS_TCP_SEG_RECEIVE
, tcpStats
.dwInSegs
);
336 ConResPrintf(StdOut
, IDS_TCP_SEG_SENT
, tcpStats
.dwOutSegs
);
337 ConResPrintf(StdOut
, IDS_TCP_SEG_RETRANSMIT
, tcpStats
.dwRetransSegs
);
341 DoFormatMessage(dwRetVal
);
345 VOID
ShowUdpStatistics(VOID
)
347 MIB_UDPSTATS udpStats
;
350 if ((dwRetVal
= GetUdpStatistics(&udpStats
)) == NO_ERROR
)
352 ConResPuts(StdOut
, IDS_UDP_IP4_HEADER
);
353 ConResPrintf(StdOut
, IDS_UDP_DATAG_RECEIVE
, udpStats
.dwInDatagrams
);
354 ConResPrintf(StdOut
, IDS_UDP_NO_PORT
, udpStats
.dwNoPorts
);
355 ConResPrintf(StdOut
, IDS_UDP_RECEIVE_ERROR
, udpStats
.dwInErrors
);
356 ConResPrintf(StdOut
, IDS_UDP_DATAG_SEND
, udpStats
.dwOutDatagrams
);
360 DoFormatMessage(dwRetVal
);
364 VOID
ShowEthernetStatistics(VOID
)
366 PMIB_IFTABLE pIfTable
;
370 pIfTable
= (MIB_IFTABLE
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IFTABLE
));
372 if (GetIfTable(pIfTable
, &dwSize
, 0) == ERROR_INSUFFICIENT_BUFFER
)
374 HeapFree(GetProcessHeap(), 0, pIfTable
);
375 pIfTable
= (MIB_IFTABLE
*) HeapAlloc(GetProcessHeap(), 0, dwSize
);
377 if ((dwRetVal
= GetIfTable(pIfTable
, &dwSize
, 0)) == NO_ERROR
)
379 ConResPuts(StdOut
, IDS_ETHERNET_INTERFACE_STAT
);
380 ConResPuts(StdOut
, IDS_ETHERNET_THEADER
);
381 ConResPrintf(StdOut
, IDS_ETHERNET_BYTES
,
382 pIfTable
->table
[0].dwInOctets
, pIfTable
->table
[0].dwOutOctets
);
383 ConResPrintf(StdOut
, IDS_ETHERNET_UNICAST_PACKET
,
384 pIfTable
->table
[0].dwInUcastPkts
, pIfTable
->table
[0].dwOutUcastPkts
);
385 ConResPrintf(StdOut
, IDS_ETHERNET_NON_UNICAST_PACKET
,
386 pIfTable
->table
[0].dwInNUcastPkts
, pIfTable
->table
[0].dwOutNUcastPkts
);
387 ConResPrintf(StdOut
, IDS_ETHERNET_DISCARD
,
388 pIfTable
->table
[0].dwInDiscards
, pIfTable
->table
[0].dwOutDiscards
);
389 ConResPrintf(StdOut
, IDS_ETHERNET_ERROR
,
390 pIfTable
->table
[0].dwInErrors
, pIfTable
->table
[0].dwOutErrors
);
391 ConResPrintf(StdOut
, IDS_ETHERNET_UNKNOWN
,
392 pIfTable
->table
[0].dwInUnknownProtos
);
396 DoFormatMessage(dwRetVal
);
399 HeapFree(GetProcessHeap(), 0, pIfTable
);
402 BOOL
ShowTcpTable(VOID
)
404 PMIB_TCPTABLE_OWNER_PID tcpTable
;
407 CHAR HostIp
[HOSTNAMELEN
], HostPort
[PORTNAMELEN
];
408 CHAR RemoteIp
[HOSTNAMELEN
], RemotePort
[PORTNAMELEN
];
409 CHAR Host
[ADDRESSLEN
];
410 CHAR Remote
[ADDRESSLEN
];
413 /* Get the table of TCP endpoints */
414 dwSize
= sizeof (MIB_TCPTABLE_OWNER_PID
);
415 /* Should also work when we get new connections between 2 GetTcpTable()
419 tcpTable
= (PMIB_TCPTABLE_OWNER_PID
) HeapAlloc(GetProcessHeap(), 0, dwSize
);
420 error
= GetExtendedTcpTable(tcpTable
, &dwSize
, TRUE
, AF_INET
, TCP_TABLE_OWNER_PID_ALL
, 0);
421 if ( error
!= NO_ERROR
)
422 HeapFree(GetProcessHeap(), 0, tcpTable
);
424 while ( error
== ERROR_INSUFFICIENT_BUFFER
);
426 if (error
!= NO_ERROR
)
428 ConResPrintf(StdErr
, IDS_ERROR_TCP_SNAPSHOT
);
429 DoFormatMessage(error
);
430 HeapFree(GetProcessHeap(), 0, tcpTable
);
434 /* Dump the TCP table */
435 for (i
= 0; i
< tcpTable
->dwNumEntries
; i
++)
437 /* If we aren't showing all connections, only display established, close wait
438 * and time wait. This is the default output for netstat */
439 if (bDoShowAllCons
|| (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_ESTAB
)
440 || (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_CLOSE_WAIT
)
441 || (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_TIME_WAIT
))
443 /* I've split this up so it's easier to follow */
444 GetIpHostName(TRUE
, tcpTable
->table
[i
].dwLocalAddr
, HostIp
, sizeof(HostIp
));
445 GetPortName(tcpTable
->table
[i
].dwLocalPort
, "tcp", HostPort
, sizeof(HostPort
));
446 sprintf(Host
, "%s:%s", HostIp
, HostPort
);
448 if (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_LISTEN
)
450 sprintf(Remote
, "%s:0", HostIp
);
454 GetIpHostName(FALSE
, tcpTable
->table
[i
].dwRemoteAddr
, RemoteIp
, sizeof(RemoteIp
));
455 GetPortName(tcpTable
->table
[i
].dwRemotePort
, "tcp", RemotePort
, sizeof(RemotePort
));
456 sprintf(Remote
, "%s:%s", RemoteIp
, RemotePort
);
459 if (bDoShowProcessId
)
461 sprintf(PID
, "%ld", tcpTable
->table
[i
].dwOwningPid
);
468 ConPrintf(StdOut
, L
" %-6s %-22S %-22S %-11s %S\n", L
"TCP",
469 Host
, Remote
, TcpState
[tcpTable
->table
[i
].dwState
], PID
);
473 HeapFree(GetProcessHeap(), 0, tcpTable
);
477 BOOL
ShowUdpTable(VOID
)
479 PMIB_UDPTABLE_OWNER_PID udpTable
;
482 CHAR HostIp
[HOSTNAMELEN
], HostPort
[PORTNAMELEN
];
483 CHAR Host
[ADDRESSLEN
];
486 /* Get the table of UDP endpoints */
488 error
= GetExtendedUdpTable(NULL
, &dwSize
, TRUE
, AF_INET
, UDP_TABLE_OWNER_PID
, 0);
489 if (error
!= ERROR_INSUFFICIENT_BUFFER
)
491 ConResPuts(StdErr
, IDS_ERROR_UDP_ENDPOINT
);
492 DoFormatMessage(error
);
495 udpTable
= (PMIB_UDPTABLE_OWNER_PID
) HeapAlloc(GetProcessHeap(), 0, dwSize
);
496 error
= GetExtendedUdpTable(udpTable
, &dwSize
, TRUE
, AF_INET
, UDP_TABLE_OWNER_PID
, 0);
499 ConResPuts(StdErr
, IDS_ERROR_UDP_ENDPOINT_TABLE
);
500 DoFormatMessage(error
);
501 HeapFree(GetProcessHeap(), 0, udpTable
);
505 /* Dump the UDP table */
506 for (i
= 0; i
< udpTable
->dwNumEntries
; i
++)
509 /* I've split this up so it's easier to follow */
510 GetIpHostName(TRUE
, udpTable
->table
[i
].dwLocalAddr
, HostIp
, sizeof(HostIp
));
511 GetPortName(udpTable
->table
[i
].dwLocalPort
, "udp", HostPort
, sizeof(HostPort
));
513 sprintf(Host
, "%s:%s", HostIp
, HostPort
);
515 if (bDoShowProcessId
)
517 sprintf(PID
, "%ld", udpTable
->table
[i
].dwOwningPid
);
524 ConPrintf(StdOut
, L
" %-6s %-22S %-34s %S\n", L
"UDP", Host
, L
"*:*", PID
);
527 HeapFree(GetProcessHeap(), 0, udpTable
);
532 * Translate port numbers into their text equivalent if there is one
535 GetPortName(UINT Port
, PCSTR Proto
, CHAR Name
[], INT NameLen
)
537 struct servent
*pServent
;
541 sprintf(Name
, "%d", htons((WORD
)Port
));
544 /* Try to translate to a name */
545 if ((pServent
= getservbyport(Port
, Proto
)))
546 strcpy(Name
, pServent
->s_name
);
548 sprintf(Name
, "%d", htons((WORD
)Port
));
553 * convert addresses into dotted decimal or hostname
556 GetIpHostName(BOOL Local
, UINT IpAddr
, CHAR Name
[], INT NameLen
)
558 // struct hostent *phostent;
561 /* display dotted decimal */
562 nIpAddr
= htonl(IpAddr
);
563 if (bDoShowNumbers
) {
564 sprintf(Name
, "%d.%d.%d.%d",
565 (nIpAddr
>> 24) & 0xFF,
566 (nIpAddr
>> 16) & 0xFF,
567 (nIpAddr
>> 8) & 0xFF,
574 /* try to resolve the name */
577 sprintf(Name
, "%d.%d.%d.%d",
578 (nIpAddr
>> 24) & 0xFF,
579 (nIpAddr
>> 16) & 0xFF,
580 (nIpAddr
>> 8) & 0xFF,
583 if (gethostname(Name
, NameLen
) != 0)
584 DoFormatMessage(WSAGetLastError());
586 } else if (IpAddr
== 0x0100007f) {
588 if (gethostname(Name
, NameLen
) != 0)
589 DoFormatMessage(WSAGetLastError());
591 strncpy(Name
, "localhost", 10);
593 // } else if (phostent = gethostbyaddr((char*)&ipaddr, sizeof(nipaddr), PF_INET)) {
594 // strcpy(name, phostent->h_name);
596 sprintf(Name
, "%d.%d.%d.%d",
597 ((nIpAddr
>> 24) & 0x000000FF),
598 ((nIpAddr
>> 16) & 0x000000FF),
599 ((nIpAddr
>> 8) & 0x000000FF),
600 ((nIpAddr
) & 0x000000FF));
607 * Parse command line parameters and set any options
608 * Run display output, looping over set intervals if a number is given
611 int wmain(int argc
, wchar_t *argv
[])
616 /* Initialize the Console Standard Streams */
619 if (!ParseCmdline(argc
, argv
))
622 if (WSAStartup(MAKEWORD(2, 2), &wsaData
) != 0)
624 ConResPrintf(StdErr
, IDS_ERROR_WSA_START
, WSAGetLastError());
628 Success
= DisplayOutput();
629 while (bLoopOutput
&& Success
)
631 Sleep(Interval
*1000);
632 Success
= DisplayOutput();
636 return (Success
? EXIT_SUCCESS
: EXIT_FAILURE
);