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 (dwError
== ERROR_SUCCESS
)
60 ConMsgPuts(StdErr
, FORMAT_MESSAGE_FROM_SYSTEM
,
61 NULL
, dwError
, 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()
158 ConResPuts(StdOut
, IDS_DISPLAY_THEADER
);
159 if (bDoShowProcessId
)
160 ConResPuts(StdOut
, IDS_DISPLAY_PROCESS
);
162 ConPuts(StdOut
, L
"\n");
167 * Simulate Microsofts netstat utility output
173 DisplayTableHeader();
178 if (bDoShowRouteTable
)
180 /* mingw doesn't have lib for _tsystem */
181 if (system("route print") == -1)
183 ConResPuts(StdErr
, IDS_ERROR_ROUTE
);
191 ShowEthernetStatistics();
195 if (bDoShowProtoCons
)
200 if (bDoShowProtoStats
)
207 if (bDoShowProtoStats
)
209 ShowIcmpStatistics();
214 if (bDoShowProtoStats
)
216 ConResPuts(StdOut
, IDS_ACTIVE_CONNECT
);
217 DisplayTableHeader();
221 if (bDoShowProtoStats
)
223 ConResPuts(StdOut
, IDS_ACTIVE_CONNECT
);
224 DisplayTableHeader();
231 else if (bDoShowProtoStats
)
234 ShowIcmpStatistics();
241 ConResPuts(StdOut
, IDS_ACTIVE_CONNECT
);
242 DisplayTableHeader();
250 VOID
ShowIpStatistics()
252 PMIB_IPSTATS pIpStats
;
255 pIpStats
= (MIB_IPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPSTATS
));
257 if ((dwRetVal
= GetIpStatistics(pIpStats
)) == NO_ERROR
)
259 ConResPuts(StdOut
, IDS_IP4_STAT_HEADER
);
260 ConResPrintf(StdOut
, IDS_IP_PACK_REC
, pIpStats
->dwInReceives
);
261 ConResPrintf(StdOut
, IDS_IP_HEAD_REC_ERROR
, pIpStats
->dwInHdrErrors
);
262 ConResPrintf(StdOut
, IDS_IP_ADDR_REC_ERROR
, pIpStats
->dwInAddrErrors
);
263 ConResPrintf(StdOut
, IDS_IP_DATAG_FWD
, pIpStats
->dwForwDatagrams
);
264 ConResPrintf(StdOut
, IDS_IP_UNKNOWN_PRO_REC
, pIpStats
->dwInUnknownProtos
);
265 ConResPrintf(StdOut
, IDS_IP_REC_PACK_DISCARD
, pIpStats
->dwInDiscards
);
266 ConResPrintf(StdOut
, IDS_IP_REC_PACK_DELIVER
, pIpStats
->dwInDelivers
);
267 ConResPrintf(StdOut
, IDS_IP_OUT_REQUEST
, pIpStats
->dwOutRequests
);
268 ConResPrintf(StdOut
, IDS_IP_ROUTE_DISCARD
, pIpStats
->dwRoutingDiscards
);
269 ConResPrintf(StdOut
, IDS_IP_DISCARD_OUT_PACK
, pIpStats
->dwOutDiscards
);
270 ConResPrintf(StdOut
, IDS_IP_OUT_PACKET_NO_ROUTE
, pIpStats
->dwOutNoRoutes
);
271 ConResPrintf(StdOut
, IDS_IP_REASSEMBLE_REQUIRED
, pIpStats
->dwReasmReqds
);
272 ConResPrintf(StdOut
, IDS_IP_REASSEMBLE_SUCCESS
, pIpStats
->dwReasmOks
);
273 ConResPrintf(StdOut
, IDS_IP_REASSEMBLE_FAILURE
, pIpStats
->dwReasmFails
);
274 ConResPrintf(StdOut
, IDS_IP_DATAG_FRAG_SUCCESS
, pIpStats
->dwFragOks
);
275 ConResPrintf(StdOut
, IDS_IP_DATAG_FRAG_FAILURE
, pIpStats
->dwFragFails
);
276 ConResPrintf(StdOut
, IDS_IP_DATAG_FRAG_CREATE
, pIpStats
->dwFragCreates
);
280 DoFormatMessage(dwRetVal
);
283 HeapFree(GetProcessHeap(), 0, pIpStats
);
286 VOID
ShowIcmpStatistics()
288 PMIB_ICMP pIcmpStats
;
291 pIcmpStats
= (MIB_ICMP
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_ICMP
));
293 if ((dwRetVal
= GetIcmpStatistics(pIcmpStats
)) == NO_ERROR
)
295 ConResPuts(StdOut
, IDS_ICMP4_STAT_HEADER
);
296 ConResPuts(StdOut
, IDS_ICMP_THEADER
);
297 ConResPrintf(StdOut
, IDS_ICMP_MSG
,
298 pIcmpStats
->stats
.icmpInStats
.dwMsgs
, pIcmpStats
->stats
.icmpOutStats
.dwMsgs
);
299 ConResPrintf(StdOut
, IDS_ICMP_ERROR
,
300 pIcmpStats
->stats
.icmpInStats
.dwErrors
, pIcmpStats
->stats
.icmpOutStats
.dwErrors
);
301 ConResPrintf(StdOut
, IDS_ICMP_DEST_UNREACH
,
302 pIcmpStats
->stats
.icmpInStats
.dwDestUnreachs
, pIcmpStats
->stats
.icmpOutStats
.dwDestUnreachs
);
303 ConResPrintf(StdOut
, IDS_ICMP_TIME_EXCEED
,
304 pIcmpStats
->stats
.icmpInStats
.dwTimeExcds
, pIcmpStats
->stats
.icmpOutStats
.dwTimeExcds
);
305 ConResPrintf(StdOut
, IDS_ICMP_PARAM_PROBLEM
,
306 pIcmpStats
->stats
.icmpInStats
.dwParmProbs
, pIcmpStats
->stats
.icmpOutStats
.dwParmProbs
);
307 ConResPrintf(StdOut
, IDS_ICMP_SRC_QUENCHES
,
308 pIcmpStats
->stats
.icmpInStats
.dwSrcQuenchs
, pIcmpStats
->stats
.icmpOutStats
.dwSrcQuenchs
);
309 ConResPrintf(StdOut
, IDS_ICMP_REDIRECT
,
310 pIcmpStats
->stats
.icmpInStats
.dwRedirects
, pIcmpStats
->stats
.icmpOutStats
.dwRedirects
);
311 ConResPrintf(StdOut
, IDS_ICMP_ECHO
,
312 pIcmpStats
->stats
.icmpInStats
.dwEchos
, pIcmpStats
->stats
.icmpOutStats
.dwEchos
);
313 ConResPrintf(StdOut
, IDS_ICMP_ECHO_REPLY
,
314 pIcmpStats
->stats
.icmpInStats
.dwEchoReps
, pIcmpStats
->stats
.icmpOutStats
.dwEchoReps
);
315 ConResPrintf(StdOut
, IDS_ICMP_TIMESTAMP
,
316 pIcmpStats
->stats
.icmpInStats
.dwTimestamps
, pIcmpStats
->stats
.icmpOutStats
.dwTimestamps
);
317 ConResPrintf(StdOut
, IDS_ICMP_TIMESTAMP_REPLY
,
318 pIcmpStats
->stats
.icmpInStats
.dwTimestampReps
, pIcmpStats
->stats
.icmpOutStats
.dwTimestampReps
);
319 ConResPrintf(StdOut
, IDS_ICMP_ADDRESSS_MASK
,
320 pIcmpStats
->stats
.icmpInStats
.dwAddrMasks
, pIcmpStats
->stats
.icmpOutStats
.dwAddrMasks
);
321 ConResPrintf(StdOut
, IDS_ICMP_ADDRESSS_MASK_REPLY
,
322 pIcmpStats
->stats
.icmpInStats
.dwAddrMaskReps
, pIcmpStats
->stats
.icmpOutStats
.dwAddrMaskReps
);
326 DoFormatMessage(dwRetVal
);
329 HeapFree(GetProcessHeap(), 0, pIcmpStats
);
333 VOID
ShowTcpStatistics()
335 PMIB_TCPSTATS pTcpStats
;
338 pTcpStats
= (MIB_TCPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_TCPSTATS
));
340 if ((dwRetVal
= GetTcpStatistics(pTcpStats
)) == NO_ERROR
)
342 ConResPuts(StdOut
, IDS_TCP4_HEADER
);
343 ConResPrintf(StdOut
, IDS_TCP_ACTIVE_OPEN
, pTcpStats
->dwActiveOpens
);
344 ConResPrintf(StdOut
, IDS_TCP_PASS_OPEN
, pTcpStats
->dwPassiveOpens
);
345 ConResPrintf(StdOut
, IDS_TCP_FAIL_CONNECT
, pTcpStats
->dwAttemptFails
);
346 ConResPrintf(StdOut
, IDS_TCP_RESET_CONNECT
, pTcpStats
->dwEstabResets
);
347 ConResPrintf(StdOut
, IDS_TCP_CURRENT_CONNECT
, pTcpStats
->dwCurrEstab
);
348 ConResPrintf(StdOut
, IDS_TCP_SEG_RECEIVE
, pTcpStats
->dwInSegs
);
349 ConResPrintf(StdOut
, IDS_TCP_SEG_SENT
, pTcpStats
->dwOutSegs
);
350 ConResPrintf(StdOut
, IDS_TCP_SEG_RETRANSMIT
, pTcpStats
->dwRetransSegs
);
354 DoFormatMessage(dwRetVal
);
357 HeapFree(GetProcessHeap(), 0, pTcpStats
);
360 VOID
ShowUdpStatistics()
362 PMIB_UDPSTATS pUdpStats
;
365 pUdpStats
= (MIB_UDPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_UDPSTATS
));
367 if ((dwRetVal
= GetUdpStatistics(pUdpStats
)) == NO_ERROR
)
369 ConResPuts(StdOut
, IDS_UDP_IP4_HEADER
);
370 ConResPrintf(StdOut
, IDS_UDP_DATAG_RECEIVE
, pUdpStats
->dwInDatagrams
);
371 ConResPrintf(StdOut
, IDS_UDP_NO_PORT
, pUdpStats
->dwNoPorts
);
372 ConResPrintf(StdOut
, IDS_UDP_RECEIVE_ERROR
, pUdpStats
->dwInErrors
);
373 ConResPrintf(StdOut
, IDS_UDP_DATAG_SEND
, pUdpStats
->dwOutDatagrams
);
377 DoFormatMessage(dwRetVal
);
380 HeapFree(GetProcessHeap(), 0, pUdpStats
);
383 VOID
ShowEthernetStatistics()
385 PMIB_IFTABLE pIfTable
;
389 pIfTable
= (MIB_IFTABLE
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IFTABLE
));
391 if (GetIfTable(pIfTable
, &dwSize
, 0) == ERROR_INSUFFICIENT_BUFFER
)
393 HeapFree(GetProcessHeap(), 0, pIfTable
);
394 pIfTable
= (MIB_IFTABLE
*) HeapAlloc(GetProcessHeap(), 0, dwSize
);
396 if ((dwRetVal
= GetIfTable(pIfTable
, &dwSize
, 0)) == NO_ERROR
)
398 ConResPuts(StdOut
, IDS_ETHERNET_INTERFACE_STAT
);
399 ConResPuts(StdOut
, IDS_ETHERNET_THEADER
);
400 ConResPrintf(StdOut
, IDS_ETHERNET_BYTES
,
401 pIfTable
->table
[0].dwInOctets
, pIfTable
->table
[0].dwOutOctets
);
402 ConResPrintf(StdOut
, IDS_ETHERNET_UNICAST_PACKET
,
403 pIfTable
->table
[0].dwInUcastPkts
, pIfTable
->table
[0].dwOutUcastPkts
);
404 ConResPrintf(StdOut
, IDS_ETHERNET_NON_UNICAST_PACKET
,
405 pIfTable
->table
[0].dwInNUcastPkts
, pIfTable
->table
[0].dwOutNUcastPkts
);
406 ConResPrintf(StdOut
, IDS_ETHERNET_DISCARD
,
407 pIfTable
->table
[0].dwInDiscards
, pIfTable
->table
[0].dwOutDiscards
);
408 ConResPrintf(StdOut
, IDS_ETHERNET_ERROR
,
409 pIfTable
->table
[0].dwInErrors
, pIfTable
->table
[0].dwOutErrors
);
410 ConResPrintf(StdOut
, IDS_ETHERNET_UNKNOWN
,
411 pIfTable
->table
[0].dwInUnknownProtos
);
415 DoFormatMessage(dwRetVal
);
418 HeapFree(GetProcessHeap(), 0, pIfTable
);
423 PMIB_TCPTABLE_OWNER_PID tcpTable
;
426 CHAR HostIp
[HOSTNAMELEN
], HostPort
[PORTNAMELEN
];
427 CHAR RemoteIp
[HOSTNAMELEN
], RemotePort
[PORTNAMELEN
];
428 CHAR Host
[ADDRESSLEN
];
429 CHAR Remote
[ADDRESSLEN
];
432 /* Get the table of TCP endpoints */
433 dwSize
= sizeof (MIB_TCPTABLE_OWNER_PID
);
434 /* Should also work when we get new connections between 2 GetTcpTable()
438 tcpTable
= (PMIB_TCPTABLE_OWNER_PID
) HeapAlloc(GetProcessHeap(), 0, dwSize
);
439 error
= GetExtendedTcpTable(tcpTable
, &dwSize
, TRUE
, AF_INET
, TCP_TABLE_OWNER_PID_ALL
, 0);
440 if ( error
!= NO_ERROR
)
441 HeapFree(GetProcessHeap(), 0, tcpTable
);
443 while ( error
== ERROR_INSUFFICIENT_BUFFER
);
445 if (error
!= NO_ERROR
)
447 ConResPrintf(StdErr
, IDS_ERROR_TCP_SNAPSHOT
);
448 DoFormatMessage(error
);
452 /* Dump the TCP table */
453 for (i
= 0; i
< tcpTable
->dwNumEntries
; i
++)
455 /* If we aren't showing all connections, only display established, close wait
456 * and time wait. This is the default output for netstat */
457 if (bDoShowAllCons
|| (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_ESTAB
)
458 || (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_CLOSE_WAIT
)
459 || (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_TIME_WAIT
))
461 /* I've split this up so it's easier to follow */
462 GetIpHostName(TRUE
, tcpTable
->table
[i
].dwLocalAddr
, HostIp
, HOSTNAMELEN
);
463 GetPortName(tcpTable
->table
[i
].dwLocalPort
, "tcp", HostPort
, PORTNAMELEN
);
464 sprintf(Host
, "%s:%s", HostIp
, HostPort
);
466 if (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_LISTEN
)
468 sprintf(Remote
, "%s:0", HostIp
);
472 GetIpHostName(FALSE
, tcpTable
->table
[i
].dwRemoteAddr
, RemoteIp
, HOSTNAMELEN
);
473 GetPortName(tcpTable
->table
[i
].dwRemotePort
, "tcp", RemotePort
, PORTNAMELEN
);
474 sprintf(Remote
, "%s:%s", RemoteIp
, RemotePort
);
477 if (bDoShowProcessId
)
479 sprintf(PID
, "%ld", tcpTable
->table
[i
].dwOwningPid
);
486 ConPrintf(StdOut
, L
" %-6s %-22s %-22s %-11s %s\n", L
"TCP",
487 Host
, Remote
, TcpState
[tcpTable
->table
[i
].dwState
], PID
);
490 HeapFree(GetProcessHeap(), 0, tcpTable
);
495 PMIB_UDPTABLE_OWNER_PID udpTable
;
498 CHAR HostIp
[HOSTNAMELEN
], HostPort
[PORTNAMELEN
];
499 CHAR Host
[ADDRESSLEN
];
502 /* Get the table of UDP endpoints */
504 error
= GetExtendedUdpTable(NULL
, &dwSize
, TRUE
, AF_INET
, UDP_TABLE_OWNER_PID
, 0);
505 if (error
!= ERROR_INSUFFICIENT_BUFFER
)
507 ConResPuts(StdErr
, IDS_ERROR_UDP_ENDPOINT
);
508 DoFormatMessage(error
);
511 udpTable
= (PMIB_UDPTABLE_OWNER_PID
) HeapAlloc(GetProcessHeap(), 0, dwSize
);
512 error
= GetExtendedUdpTable(udpTable
, &dwSize
, TRUE
, AF_INET
, UDP_TABLE_OWNER_PID
, 0);
515 ConResPuts(StdErr
, IDS_ERROR_UDP_ENDPOINT_TABLE
);
516 DoFormatMessage(error
);
517 HeapFree(GetProcessHeap(), 0, udpTable
);
521 /* Dump the UDP table */
522 for (i
= 0; i
< udpTable
->dwNumEntries
; i
++)
525 /* I've split this up so it's easier to follow */
526 GetIpHostName(TRUE
, udpTable
->table
[i
].dwLocalAddr
, HostIp
, HOSTNAMELEN
);
527 GetPortName(udpTable
->table
[i
].dwLocalPort
, "tcp", HostPort
, PORTNAMELEN
);
529 sprintf(Host
, "%s:%s", HostIp
, HostPort
);
531 if (bDoShowProcessId
)
533 sprintf(PID
, "%ld", udpTable
->table
[i
].dwOwningPid
);
540 ConPrintf(StdOut
, L
" %-6s %-22s %-34s %s\n", L
"UDP", Host
, L
"*:*", PID
);
543 HeapFree(GetProcessHeap(), 0, udpTable
);
547 * Translate port numbers into their text equivalent if there is one
550 GetPortName(UINT Port
, PCSTR Proto
, CHAR Name
[], INT NameLen
)
552 struct servent
*pServent
;
556 sprintf(Name
, "%d", htons((WORD
)Port
));
559 /* Try to translate to a name */
560 if ((pServent
= getservbyport(Port
, Proto
)))
561 strcpy(Name
, pServent
->s_name
);
563 sprintf(Name
, "%d", htons((WORD
)Port
));
568 * convert addresses into dotted decimal or hostname
571 GetIpHostName(BOOL Local
, UINT IpAddr
, CHAR Name
[], int NameLen
)
573 // struct hostent *phostent;
576 /* display dotted decimal */
577 nIpAddr
= htonl(IpAddr
);
578 if (bDoShowNumbers
) {
579 sprintf(Name
, "%d.%d.%d.%d",
580 (nIpAddr
>> 24) & 0xFF,
581 (nIpAddr
>> 16) & 0xFF,
582 (nIpAddr
>> 8) & 0xFF,
589 /* try to resolve the name */
592 sprintf(Name
, "%d.%d.%d.%d",
593 (nIpAddr
>> 24) & 0xFF,
594 (nIpAddr
>> 16) & 0xFF,
595 (nIpAddr
>> 8) & 0xFF,
598 if (gethostname(Name
, NameLen
) != 0)
599 DoFormatMessage(WSAGetLastError());
601 } else if (IpAddr
== 0x0100007f) {
603 if (gethostname(Name
, NameLen
) != 0)
604 DoFormatMessage(WSAGetLastError());
606 strncpy(Name
, "localhost", 10);
608 // } else if (phostent = gethostbyaddr((char*)&ipaddr, sizeof(nipaddr), PF_INET)) {
609 // strcpy(name, phostent->h_name);
611 sprintf(Name
, "%d.%d.%d.%d",
612 ((nIpAddr
>> 24) & 0x000000FF),
613 ((nIpAddr
>> 16) & 0x000000FF),
614 ((nIpAddr
>> 8) & 0x000000FF),
615 ((nIpAddr
) & 0x000000FF));
622 * Parse command line parameters and set any options
623 * Run display output, looping over set intervals if a number is given
626 int wmain(int argc
, wchar_t *argv
[])
630 /* Initialize the Console Standard Streams */
633 if (WSAStartup(MAKEWORD(2, 2), &wsaData
) != 0)
635 ConResPrintf(StdErr
, IDS_ERROR_WSA_START
, WSAGetLastError());
639 if (ParseCmdline(argc
, argv
))
648 Sleep(Interval
*1000);