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 if (_wsystem(L
"route print") == -1)
181 ConResPuts(StdErr
, IDS_ERROR_ROUTE
);
189 ShowEthernetStatistics();
193 if (bDoShowProtoCons
)
198 if (bDoShowProtoStats
)
205 if (bDoShowProtoStats
)
207 ShowIcmpStatistics();
212 if (bDoShowProtoStats
)
214 ConResPuts(StdOut
, IDS_ACTIVE_CONNECT
);
215 DisplayTableHeader();
219 if (bDoShowProtoStats
)
221 ConResPuts(StdOut
, IDS_ACTIVE_CONNECT
);
222 DisplayTableHeader();
229 else if (bDoShowProtoStats
)
232 ShowIcmpStatistics();
239 ConResPuts(StdOut
, IDS_ACTIVE_CONNECT
);
240 DisplayTableHeader();
248 VOID
ShowIpStatistics(VOID
)
250 PMIB_IPSTATS pIpStats
;
253 pIpStats
= (MIB_IPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPSTATS
));
255 if ((dwRetVal
= GetIpStatistics(pIpStats
)) == NO_ERROR
)
257 ConResPuts(StdOut
, IDS_IP4_STAT_HEADER
);
258 ConResPrintf(StdOut
, IDS_IP_PACK_REC
, pIpStats
->dwInReceives
);
259 ConResPrintf(StdOut
, IDS_IP_HEAD_REC_ERROR
, pIpStats
->dwInHdrErrors
);
260 ConResPrintf(StdOut
, IDS_IP_ADDR_REC_ERROR
, pIpStats
->dwInAddrErrors
);
261 ConResPrintf(StdOut
, IDS_IP_DATAG_FWD
, pIpStats
->dwForwDatagrams
);
262 ConResPrintf(StdOut
, IDS_IP_UNKNOWN_PRO_REC
, pIpStats
->dwInUnknownProtos
);
263 ConResPrintf(StdOut
, IDS_IP_REC_PACK_DISCARD
, pIpStats
->dwInDiscards
);
264 ConResPrintf(StdOut
, IDS_IP_REC_PACK_DELIVER
, pIpStats
->dwInDelivers
);
265 ConResPrintf(StdOut
, IDS_IP_OUT_REQUEST
, pIpStats
->dwOutRequests
);
266 ConResPrintf(StdOut
, IDS_IP_ROUTE_DISCARD
, pIpStats
->dwRoutingDiscards
);
267 ConResPrintf(StdOut
, IDS_IP_DISCARD_OUT_PACK
, pIpStats
->dwOutDiscards
);
268 ConResPrintf(StdOut
, IDS_IP_OUT_PACKET_NO_ROUTE
, pIpStats
->dwOutNoRoutes
);
269 ConResPrintf(StdOut
, IDS_IP_REASSEMBLE_REQUIRED
, pIpStats
->dwReasmReqds
);
270 ConResPrintf(StdOut
, IDS_IP_REASSEMBLE_SUCCESS
, pIpStats
->dwReasmOks
);
271 ConResPrintf(StdOut
, IDS_IP_REASSEMBLE_FAILURE
, pIpStats
->dwReasmFails
);
272 ConResPrintf(StdOut
, IDS_IP_DATAG_FRAG_SUCCESS
, pIpStats
->dwFragOks
);
273 ConResPrintf(StdOut
, IDS_IP_DATAG_FRAG_FAILURE
, pIpStats
->dwFragFails
);
274 ConResPrintf(StdOut
, IDS_IP_DATAG_FRAG_CREATE
, pIpStats
->dwFragCreates
);
278 DoFormatMessage(dwRetVal
);
281 HeapFree(GetProcessHeap(), 0, pIpStats
);
284 VOID
ShowIcmpStatistics(VOID
)
286 PMIB_ICMP pIcmpStats
;
289 pIcmpStats
= (MIB_ICMP
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_ICMP
));
291 if ((dwRetVal
= GetIcmpStatistics(pIcmpStats
)) == NO_ERROR
)
293 ConResPuts(StdOut
, IDS_ICMP4_STAT_HEADER
);
294 ConResPuts(StdOut
, IDS_ICMP_THEADER
);
295 ConResPrintf(StdOut
, IDS_ICMP_MSG
,
296 pIcmpStats
->stats
.icmpInStats
.dwMsgs
, pIcmpStats
->stats
.icmpOutStats
.dwMsgs
);
297 ConResPrintf(StdOut
, IDS_ICMP_ERROR
,
298 pIcmpStats
->stats
.icmpInStats
.dwErrors
, pIcmpStats
->stats
.icmpOutStats
.dwErrors
);
299 ConResPrintf(StdOut
, IDS_ICMP_DEST_UNREACH
,
300 pIcmpStats
->stats
.icmpInStats
.dwDestUnreachs
, pIcmpStats
->stats
.icmpOutStats
.dwDestUnreachs
);
301 ConResPrintf(StdOut
, IDS_ICMP_TIME_EXCEED
,
302 pIcmpStats
->stats
.icmpInStats
.dwTimeExcds
, pIcmpStats
->stats
.icmpOutStats
.dwTimeExcds
);
303 ConResPrintf(StdOut
, IDS_ICMP_PARAM_PROBLEM
,
304 pIcmpStats
->stats
.icmpInStats
.dwParmProbs
, pIcmpStats
->stats
.icmpOutStats
.dwParmProbs
);
305 ConResPrintf(StdOut
, IDS_ICMP_SRC_QUENCHES
,
306 pIcmpStats
->stats
.icmpInStats
.dwSrcQuenchs
, pIcmpStats
->stats
.icmpOutStats
.dwSrcQuenchs
);
307 ConResPrintf(StdOut
, IDS_ICMP_REDIRECT
,
308 pIcmpStats
->stats
.icmpInStats
.dwRedirects
, pIcmpStats
->stats
.icmpOutStats
.dwRedirects
);
309 ConResPrintf(StdOut
, IDS_ICMP_ECHO
,
310 pIcmpStats
->stats
.icmpInStats
.dwEchos
, pIcmpStats
->stats
.icmpOutStats
.dwEchos
);
311 ConResPrintf(StdOut
, IDS_ICMP_ECHO_REPLY
,
312 pIcmpStats
->stats
.icmpInStats
.dwEchoReps
, pIcmpStats
->stats
.icmpOutStats
.dwEchoReps
);
313 ConResPrintf(StdOut
, IDS_ICMP_TIMESTAMP
,
314 pIcmpStats
->stats
.icmpInStats
.dwTimestamps
, pIcmpStats
->stats
.icmpOutStats
.dwTimestamps
);
315 ConResPrintf(StdOut
, IDS_ICMP_TIMESTAMP_REPLY
,
316 pIcmpStats
->stats
.icmpInStats
.dwTimestampReps
, pIcmpStats
->stats
.icmpOutStats
.dwTimestampReps
);
317 ConResPrintf(StdOut
, IDS_ICMP_ADDRESSS_MASK
,
318 pIcmpStats
->stats
.icmpInStats
.dwAddrMasks
, pIcmpStats
->stats
.icmpOutStats
.dwAddrMasks
);
319 ConResPrintf(StdOut
, IDS_ICMP_ADDRESSS_MASK_REPLY
,
320 pIcmpStats
->stats
.icmpInStats
.dwAddrMaskReps
, pIcmpStats
->stats
.icmpOutStats
.dwAddrMaskReps
);
324 DoFormatMessage(dwRetVal
);
327 HeapFree(GetProcessHeap(), 0, pIcmpStats
);
331 VOID
ShowTcpStatistics(VOID
)
333 PMIB_TCPSTATS pTcpStats
;
336 pTcpStats
= (MIB_TCPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_TCPSTATS
));
338 if ((dwRetVal
= GetTcpStatistics(pTcpStats
)) == NO_ERROR
)
340 ConResPuts(StdOut
, IDS_TCP4_HEADER
);
341 ConResPrintf(StdOut
, IDS_TCP_ACTIVE_OPEN
, pTcpStats
->dwActiveOpens
);
342 ConResPrintf(StdOut
, IDS_TCP_PASS_OPEN
, pTcpStats
->dwPassiveOpens
);
343 ConResPrintf(StdOut
, IDS_TCP_FAIL_CONNECT
, pTcpStats
->dwAttemptFails
);
344 ConResPrintf(StdOut
, IDS_TCP_RESET_CONNECT
, pTcpStats
->dwEstabResets
);
345 ConResPrintf(StdOut
, IDS_TCP_CURRENT_CONNECT
, pTcpStats
->dwCurrEstab
);
346 ConResPrintf(StdOut
, IDS_TCP_SEG_RECEIVE
, pTcpStats
->dwInSegs
);
347 ConResPrintf(StdOut
, IDS_TCP_SEG_SENT
, pTcpStats
->dwOutSegs
);
348 ConResPrintf(StdOut
, IDS_TCP_SEG_RETRANSMIT
, pTcpStats
->dwRetransSegs
);
352 DoFormatMessage(dwRetVal
);
355 HeapFree(GetProcessHeap(), 0, pTcpStats
);
358 VOID
ShowUdpStatistics(VOID
)
360 PMIB_UDPSTATS pUdpStats
;
363 pUdpStats
= (MIB_UDPSTATS
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_UDPSTATS
));
365 if ((dwRetVal
= GetUdpStatistics(pUdpStats
)) == NO_ERROR
)
367 ConResPuts(StdOut
, IDS_UDP_IP4_HEADER
);
368 ConResPrintf(StdOut
, IDS_UDP_DATAG_RECEIVE
, pUdpStats
->dwInDatagrams
);
369 ConResPrintf(StdOut
, IDS_UDP_NO_PORT
, pUdpStats
->dwNoPorts
);
370 ConResPrintf(StdOut
, IDS_UDP_RECEIVE_ERROR
, pUdpStats
->dwInErrors
);
371 ConResPrintf(StdOut
, IDS_UDP_DATAG_SEND
, pUdpStats
->dwOutDatagrams
);
375 DoFormatMessage(dwRetVal
);
378 HeapFree(GetProcessHeap(), 0, pUdpStats
);
381 VOID
ShowEthernetStatistics(VOID
)
383 PMIB_IFTABLE pIfTable
;
387 pIfTable
= (MIB_IFTABLE
*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IFTABLE
));
389 if (GetIfTable(pIfTable
, &dwSize
, 0) == ERROR_INSUFFICIENT_BUFFER
)
391 HeapFree(GetProcessHeap(), 0, pIfTable
);
392 pIfTable
= (MIB_IFTABLE
*) HeapAlloc(GetProcessHeap(), 0, dwSize
);
394 if ((dwRetVal
= GetIfTable(pIfTable
, &dwSize
, 0)) == NO_ERROR
)
396 ConResPuts(StdOut
, IDS_ETHERNET_INTERFACE_STAT
);
397 ConResPuts(StdOut
, IDS_ETHERNET_THEADER
);
398 ConResPrintf(StdOut
, IDS_ETHERNET_BYTES
,
399 pIfTable
->table
[0].dwInOctets
, pIfTable
->table
[0].dwOutOctets
);
400 ConResPrintf(StdOut
, IDS_ETHERNET_UNICAST_PACKET
,
401 pIfTable
->table
[0].dwInUcastPkts
, pIfTable
->table
[0].dwOutUcastPkts
);
402 ConResPrintf(StdOut
, IDS_ETHERNET_NON_UNICAST_PACKET
,
403 pIfTable
->table
[0].dwInNUcastPkts
, pIfTable
->table
[0].dwOutNUcastPkts
);
404 ConResPrintf(StdOut
, IDS_ETHERNET_DISCARD
,
405 pIfTable
->table
[0].dwInDiscards
, pIfTable
->table
[0].dwOutDiscards
);
406 ConResPrintf(StdOut
, IDS_ETHERNET_ERROR
,
407 pIfTable
->table
[0].dwInErrors
, pIfTable
->table
[0].dwOutErrors
);
408 ConResPrintf(StdOut
, IDS_ETHERNET_UNKNOWN
,
409 pIfTable
->table
[0].dwInUnknownProtos
);
413 DoFormatMessage(dwRetVal
);
416 HeapFree(GetProcessHeap(), 0, pIfTable
);
419 VOID
ShowTcpTable(VOID
)
421 PMIB_TCPTABLE_OWNER_PID tcpTable
;
424 CHAR HostIp
[HOSTNAMELEN
], HostPort
[PORTNAMELEN
];
425 CHAR RemoteIp
[HOSTNAMELEN
], RemotePort
[PORTNAMELEN
];
426 CHAR Host
[ADDRESSLEN
];
427 CHAR Remote
[ADDRESSLEN
];
430 /* Get the table of TCP endpoints */
431 dwSize
= sizeof (MIB_TCPTABLE_OWNER_PID
);
432 /* Should also work when we get new connections between 2 GetTcpTable()
436 tcpTable
= (PMIB_TCPTABLE_OWNER_PID
) HeapAlloc(GetProcessHeap(), 0, dwSize
);
437 error
= GetExtendedTcpTable(tcpTable
, &dwSize
, TRUE
, AF_INET
, TCP_TABLE_OWNER_PID_ALL
, 0);
438 if ( error
!= NO_ERROR
)
439 HeapFree(GetProcessHeap(), 0, tcpTable
);
441 while ( error
== ERROR_INSUFFICIENT_BUFFER
);
443 if (error
!= NO_ERROR
)
445 ConResPrintf(StdErr
, IDS_ERROR_TCP_SNAPSHOT
);
446 DoFormatMessage(error
);
450 /* Dump the TCP table */
451 for (i
= 0; i
< tcpTable
->dwNumEntries
; i
++)
453 /* If we aren't showing all connections, only display established, close wait
454 * and time wait. This is the default output for netstat */
455 if (bDoShowAllCons
|| (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_ESTAB
)
456 || (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_CLOSE_WAIT
)
457 || (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_TIME_WAIT
))
459 /* I've split this up so it's easier to follow */
460 GetIpHostName(TRUE
, tcpTable
->table
[i
].dwLocalAddr
, HostIp
, sizeof(HostIp
));
461 GetPortName(tcpTable
->table
[i
].dwLocalPort
, "tcp", HostPort
, sizeof(HostPort
));
462 sprintf(Host
, "%s:%s", HostIp
, HostPort
);
464 if (tcpTable
->table
[i
].dwState
== MIB_TCP_STATE_LISTEN
)
466 sprintf(Remote
, "%s:0", HostIp
);
470 GetIpHostName(FALSE
, tcpTable
->table
[i
].dwRemoteAddr
, RemoteIp
, sizeof(RemoteIp
));
471 GetPortName(tcpTable
->table
[i
].dwRemotePort
, "tcp", RemotePort
, sizeof(RemotePort
));
472 sprintf(Remote
, "%s:%s", RemoteIp
, RemotePort
);
475 if (bDoShowProcessId
)
477 sprintf(PID
, "%ld", tcpTable
->table
[i
].dwOwningPid
);
484 ConPrintf(StdOut
, L
" %-6s %-22S %-22S %-11s %S\n", L
"TCP",
485 Host
, Remote
, TcpState
[tcpTable
->table
[i
].dwState
], PID
);
488 HeapFree(GetProcessHeap(), 0, tcpTable
);
491 VOID
ShowUdpTable(VOID
)
493 PMIB_UDPTABLE_OWNER_PID udpTable
;
496 CHAR HostIp
[HOSTNAMELEN
], HostPort
[PORTNAMELEN
];
497 CHAR Host
[ADDRESSLEN
];
500 /* Get the table of UDP endpoints */
502 error
= GetExtendedUdpTable(NULL
, &dwSize
, TRUE
, AF_INET
, UDP_TABLE_OWNER_PID
, 0);
503 if (error
!= ERROR_INSUFFICIENT_BUFFER
)
505 ConResPuts(StdErr
, IDS_ERROR_UDP_ENDPOINT
);
506 DoFormatMessage(error
);
509 udpTable
= (PMIB_UDPTABLE_OWNER_PID
) HeapAlloc(GetProcessHeap(), 0, dwSize
);
510 error
= GetExtendedUdpTable(udpTable
, &dwSize
, TRUE
, AF_INET
, UDP_TABLE_OWNER_PID
, 0);
513 ConResPuts(StdErr
, IDS_ERROR_UDP_ENDPOINT_TABLE
);
514 DoFormatMessage(error
);
515 HeapFree(GetProcessHeap(), 0, udpTable
);
519 /* Dump the UDP table */
520 for (i
= 0; i
< udpTable
->dwNumEntries
; i
++)
523 /* I've split this up so it's easier to follow */
524 GetIpHostName(TRUE
, udpTable
->table
[i
].dwLocalAddr
, HostIp
, sizeof(HostIp
));
525 GetPortName(udpTable
->table
[i
].dwLocalPort
, "tcp", HostPort
, sizeof(HostPort
));
527 sprintf(Host
, "%s:%s", HostIp
, HostPort
);
529 if (bDoShowProcessId
)
531 sprintf(PID
, "%ld", udpTable
->table
[i
].dwOwningPid
);
538 ConPrintf(StdOut
, L
" %-6s %-22S %-34s %S\n", L
"UDP", Host
, L
"*:*", PID
);
541 HeapFree(GetProcessHeap(), 0, udpTable
);
545 * Translate port numbers into their text equivalent if there is one
548 GetPortName(UINT Port
, PCSTR Proto
, CHAR Name
[], INT NameLen
)
550 struct servent
*pServent
;
554 sprintf(Name
, "%d", htons((WORD
)Port
));
557 /* Try to translate to a name */
558 if ((pServent
= getservbyport(Port
, Proto
)))
559 strcpy(Name
, pServent
->s_name
);
561 sprintf(Name
, "%d", htons((WORD
)Port
));
566 * convert addresses into dotted decimal or hostname
569 GetIpHostName(BOOL Local
, UINT IpAddr
, CHAR Name
[], INT NameLen
)
571 // struct hostent *phostent;
574 /* display dotted decimal */
575 nIpAddr
= htonl(IpAddr
);
576 if (bDoShowNumbers
) {
577 sprintf(Name
, "%d.%d.%d.%d",
578 (nIpAddr
>> 24) & 0xFF,
579 (nIpAddr
>> 16) & 0xFF,
580 (nIpAddr
>> 8) & 0xFF,
587 /* try to resolve the name */
590 sprintf(Name
, "%d.%d.%d.%d",
591 (nIpAddr
>> 24) & 0xFF,
592 (nIpAddr
>> 16) & 0xFF,
593 (nIpAddr
>> 8) & 0xFF,
596 if (gethostname(Name
, NameLen
) != 0)
597 DoFormatMessage(WSAGetLastError());
599 } else if (IpAddr
== 0x0100007f) {
601 if (gethostname(Name
, NameLen
) != 0)
602 DoFormatMessage(WSAGetLastError());
604 strncpy(Name
, "localhost", 10);
606 // } else if (phostent = gethostbyaddr((char*)&ipaddr, sizeof(nipaddr), PF_INET)) {
607 // strcpy(name, phostent->h_name);
609 sprintf(Name
, "%d.%d.%d.%d",
610 ((nIpAddr
>> 24) & 0x000000FF),
611 ((nIpAddr
>> 16) & 0x000000FF),
612 ((nIpAddr
>> 8) & 0x000000FF),
613 ((nIpAddr
) & 0x000000FF));
620 * Parse command line parameters and set any options
621 * Run display output, looping over set intervals if a number is given
624 int wmain(int argc
, wchar_t *argv
[])
628 /* Initialize the Console Standard Streams */
631 if (WSAStartup(MAKEWORD(2, 2), &wsaData
) != 0)
633 ConResPrintf(StdErr
, IDS_ERROR_WSA_START
, WSAGetLastError());
637 if (ParseCmdline(argc
, argv
))
646 Sleep(Interval
*1000);