2 * ReactOS Win32 Applications
3 * Copyright (C) 2005 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS netstat utility
23 * FILE: apps/utils/net/netstat/netstat.c
24 * PURPOSE: display IP stack statistics
25 * PROGRAMMERS: Ged Murphy (gedmurphy@gmail.com)
27 * Ged Murphy 19/09/05 Created
28 * Some ideas/code taken from Rob Dickinson's original app
34 * sort function return values.
35 * implement -b, -o and -v
36 * clean up GetIpHostName
37 * command line parser needs more work
48 enum ProtoType
{IP
, TCP
, UDP
, ICMP
} Protocol
;
49 DWORD Interval
; /* time to pause between printing output */
51 /* TCP endpoint states */
52 TCHAR TcpState
[][32] = {
70 * format message string and display output
72 DWORD
DoFormatMessage(DWORD ErrorCode
)
77 if ((RetVal
= FormatMessage(
78 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
79 FORMAT_MESSAGE_FROM_SYSTEM
|
80 FORMAT_MESSAGE_IGNORE_INSERTS
,
83 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), /* Default language */
88 _tprintf(_T("%s"), (LPTSTR
)lpMsgBuf
);
91 /* return number of TCHAR's stored in output buffer
92 * excluding '\0' - as FormatMessage does*/
102 * Parse command line parameters and set any options
105 BOOL
ParseCmdline(int argc
, char* argv
[])
111 if ((argc
== 1) || (isdigit(*argv
[1])))
114 /* Parse command line for options we have been given. */
115 for (i
= 1; i
< argc
; i
++)
117 if ( (argc
> 1)&&(argv
[i
][0] == '-') )
121 while ((c
= *++argv
[i
]) != '\0')
126 //_tprintf(_T("got a\n"));
127 bDoShowAllCons
= TRUE
;
130 //_tprintf(_T("got e\n"));
131 bDoShowEthStats
= TRUE
;
134 //_tprintf(_T("got n\n"));
135 bDoShowNumbers
= TRUE
;
138 //_tprintf(_T("got s\n"));
139 bDoShowProtoStats
= TRUE
;
142 //_tprintf(_T("got p\n"));
143 bDoShowProtoCons
= TRUE
;
145 strncpy(Proto
, (++argv
)[i
], sizeof(Proto
));
146 if (!_tcsicmp( "IP", Proto
))
148 else if (!_tcsicmp( "ICMP", Proto
))
150 else if (!_tcsicmp( "TCP", Proto
))
152 else if (!_tcsicmp( "UDP", Proto
))
159 (--argv
)[i
]; /* move pointer back down to previous argv */
162 bDoShowRouteTable
= TRUE
;
165 _tprintf(_T("got v\n"));
166 bDoDispSeqComp
= TRUE
;
174 else if (isdigit(*argv
[i
]))
176 _stscanf(argv
[i
], "%lu", &Interval
);
191 * Simulate Microsofts netstat utility output
197 _tprintf(_T("\n Proto Local Address Foreign Address State\n"));
202 if (bDoShowRouteTable
)
204 if (system("route print") == -1)
206 //mingw doesn't have lib for _tsystem
207 _tprintf(_T("cannot find 'route.exe'\n"));
215 ShowEthernetStatistics();
219 if (bDoShowProtoCons
)
224 if (bDoShowProtoStats
)
231 if (bDoShowProtoStats
)
233 ShowIcmpStatistics();
238 if (bDoShowProtoStats
)
240 _tprintf(_T("\nActive Connections\n"));
241 _tprintf(_T("\n Proto Local Address Foreign Address State\n"));
245 if (bDoShowProtoStats
)
247 _tprintf(_T("\nActive Connections\n"));
248 _tprintf(_T("\n Proto Local Address Foreign Address State\n"));
255 else if (bDoShowProtoStats
)
258 ShowIcmpStatistics();
263 else //if (bDoShowAllCons)
265 _tprintf(_T("\nActive Connections\n"));
266 _tprintf(_T("\n Proto Local Address Foreign Address State\n"));
276 VOID
ShowIpStatistics()
278 PMIB_IPSTATS pIpStats
;
281 pIpStats
= (MIB_IPSTATS
*) malloc(sizeof(MIB_IPSTATS
));
283 if ((dwRetVal
= GetIpStatistics(pIpStats
)) == NO_ERROR
)
285 _tprintf(_T("\nIPv4 Statistics\n\n"));
286 _tprintf(_T(" %-34s = %lu\n"), _T("Packets Recieved"), pIpStats
->dwInReceives
);
287 _tprintf(_T(" %-34s = %lu\n"), _T("Received Header Errors"), pIpStats
->dwInHdrErrors
);
288 _tprintf(_T(" %-34s = %lu\n"), _T("Received Address Errors"), pIpStats
->dwInAddrErrors
);
289 _tprintf(_T(" %-34s = %lu\n"), _T("Datagrams Forwarded"), pIpStats
->dwForwDatagrams
);
290 _tprintf(_T(" %-34s = %lu\n"), _T("Unknown Protocols Recieved"), pIpStats
->dwInUnknownProtos
);
291 _tprintf(_T(" %-34s = %lu\n"), _T("Received Packets Discarded"), pIpStats
->dwInDiscards
);
292 _tprintf(_T(" %-34s = %lu\n"), _T("Recieved Packets Delivered"), pIpStats
->dwInDelivers
);
293 _tprintf(_T(" %-34s = %lu\n"), _T("Output Requests"), pIpStats
->dwOutRequests
);
294 _tprintf(_T(" %-34s = %lu\n"), _T("Routing Discards"), pIpStats
->dwRoutingDiscards
);
295 _tprintf(_T(" %-34s = %lu\n"), _T("Discarded Output Packets"), pIpStats
->dwOutDiscards
);
296 _tprintf(_T(" %-34s = %lu\n"), _T("Output Packets No Route"), pIpStats
->dwOutNoRoutes
);
297 _tprintf(_T(" %-34s = %lu\n"), _T("Reassembly Required"), pIpStats
->dwReasmReqds
);
298 _tprintf(_T(" %-34s = %lu\n"), _T("Reassembly Succesful"), pIpStats
->dwReasmOks
);
299 _tprintf(_T(" %-34s = %lu\n"), _T("Reassembly Failures"), pIpStats
->dwReasmFails
);
300 // _tprintf(_T(" %-34s = %lu\n"), _T("Datagrams succesfully fragmented"), NULL); /* FIXME: what is this one? */
301 _tprintf(_T(" %-34s = %lu\n"), _T("Datagrams Failing Fragmentation"), pIpStats
->dwFragFails
);
302 _tprintf(_T(" %-34s = %lu\n"), _T("Fragments Created"), pIpStats
->dwFragCreates
);
305 DoFormatMessage(dwRetVal
);
308 VOID
ShowIcmpStatistics()
310 PMIB_ICMP pIcmpStats
;
313 pIcmpStats
= (MIB_ICMP
*) malloc(sizeof(MIB_ICMP
));
315 if ((dwRetVal
= GetIcmpStatistics(pIcmpStats
)) == NO_ERROR
)
317 _tprintf(_T("\nICMPv4 Statistics\n\n"));
318 _tprintf(_T(" Received Sent\n"));
319 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Messages"),
320 pIcmpStats
->stats
.icmpInStats
.dwMsgs
, pIcmpStats
->stats
.icmpOutStats
.dwMsgs
);
321 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Errors"),
322 pIcmpStats
->stats
.icmpInStats
.dwErrors
, pIcmpStats
->stats
.icmpOutStats
.dwErrors
);
323 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Destination Unreachable"),
324 pIcmpStats
->stats
.icmpInStats
.dwDestUnreachs
, pIcmpStats
->stats
.icmpOutStats
.dwDestUnreachs
);
325 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Time Exceeded"),
326 pIcmpStats
->stats
.icmpInStats
.dwTimeExcds
, pIcmpStats
->stats
.icmpOutStats
.dwTimeExcds
);
327 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Parameter Problems"),
328 pIcmpStats
->stats
.icmpInStats
.dwParmProbs
, pIcmpStats
->stats
.icmpOutStats
.dwParmProbs
);
329 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Source Quenches"),
330 pIcmpStats
->stats
.icmpInStats
.dwSrcQuenchs
, pIcmpStats
->stats
.icmpOutStats
.dwSrcQuenchs
);
331 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Redirects"),
332 pIcmpStats
->stats
.icmpInStats
.dwRedirects
, pIcmpStats
->stats
.icmpOutStats
.dwRedirects
);
333 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Echos"),
334 pIcmpStats
->stats
.icmpInStats
.dwEchos
, pIcmpStats
->stats
.icmpOutStats
.dwEchos
);
335 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Echo Replies"),
336 pIcmpStats
->stats
.icmpInStats
.dwEchoReps
, pIcmpStats
->stats
.icmpOutStats
.dwEchoReps
);
337 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Timestamps"),
338 pIcmpStats
->stats
.icmpInStats
.dwTimestamps
, pIcmpStats
->stats
.icmpOutStats
.dwTimestamps
);
339 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Timestamp Replies"),
340 pIcmpStats
->stats
.icmpInStats
.dwTimestampReps
, pIcmpStats
->stats
.icmpOutStats
.dwTimestampReps
);
341 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Address Masks"),
342 pIcmpStats
->stats
.icmpInStats
.dwAddrMasks
, pIcmpStats
->stats
.icmpOutStats
.dwAddrMasks
);
343 _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Address Mask Replies"),
344 pIcmpStats
->stats
.icmpInStats
.dwAddrMaskReps
, pIcmpStats
->stats
.icmpOutStats
.dwAddrMaskReps
);
347 DoFormatMessage(dwRetVal
);
351 VOID
ShowTcpStatistics()
353 PMIB_TCPSTATS pTcpStats
;
356 pTcpStats
= (MIB_TCPSTATS
*) malloc(sizeof(MIB_TCPSTATS
));
358 if ((dwRetVal
= GetTcpStatistics(pTcpStats
)) == NO_ERROR
)
360 _tprintf(_T("\nTCP Statistics for IPv4\n\n"));
361 _tprintf(_T(" %-35s = %lu\n"), _T("Active Opens"), pTcpStats
->dwActiveOpens
);
362 _tprintf(_T(" %-35s = %lu\n"), _T("Passive Opens"), pTcpStats
->dwPassiveOpens
);
363 _tprintf(_T(" %-35s = %lu\n"), _T("Failed Connection Attempts"), pTcpStats
->dwAttemptFails
);
364 _tprintf(_T(" %-35s = %lu\n"), _T("Reset Connections"), pTcpStats
->dwEstabResets
);
365 _tprintf(_T(" %-35s = %lu\n"), _T("Current Connections"), pTcpStats
->dwCurrEstab
);
366 _tprintf(_T(" %-35s = %lu\n"), _T("Segments Recieved"), pTcpStats
->dwInSegs
);
367 _tprintf(_T(" %-35s = %lu\n"), _T("Segments Sent"), pTcpStats
->dwOutSegs
);
368 _tprintf(_T(" %-35s = %lu\n"), _T("Segments Retransmitted"), pTcpStats
->dwRetransSegs
);
371 DoFormatMessage(dwRetVal
);
374 VOID
ShowUdpStatistics()
376 PMIB_UDPSTATS pUdpStats
;
379 pUdpStats
= (MIB_UDPSTATS
*) malloc(sizeof(MIB_UDPSTATS
));
381 if ((dwRetVal
= GetUdpStatistics(pUdpStats
)) == NO_ERROR
)
383 _tprintf(_T("\nUDP Statistics for IPv4\n\n"));
384 _tprintf(_T(" %-21s = %lu\n"), _T("Datagrams Recieved"), pUdpStats
->dwInDatagrams
);
385 _tprintf(_T(" %-21s = %lu\n"), _T("No Ports"), pUdpStats
->dwNoPorts
);
386 _tprintf(_T(" %-21s = %lu\n"), _T("Recieve Errors"), pUdpStats
->dwInErrors
);
387 _tprintf(_T(" %-21s = %lu\n"), _T("Datagrams Sent"), pUdpStats
->dwOutDatagrams
);
390 DoFormatMessage(dwRetVal
);
393 VOID
ShowEthernetStatistics()
395 PMIB_IFTABLE pIfTable
;
399 pIfTable
= (MIB_IFTABLE
*) malloc(sizeof(MIB_IFTABLE
));
401 if (GetIfTable(pIfTable
, &dwSize
, 0) == ERROR_INSUFFICIENT_BUFFER
)
403 GlobalFree(pIfTable
);
404 pIfTable
= (MIB_IFTABLE
*) malloc(dwSize
);
406 if ((dwRetVal
= GetIfTable(pIfTable
, &dwSize
, 0)) == NO_ERROR
)
408 _tprintf(_T("Interface Statistics\n\n"));
409 _tprintf(_T(" Received Sent\n\n"));
410 _tprintf(_T("%-20s %14lu %15lu\n"), _T("Bytes"),
411 pIfTable
->table
[0].dwInOctets
, pIfTable
->table
[0].dwOutOctets
);
412 _tprintf(_T("%-20s %14lu %15lu\n"), _T("Unicast packets"),
413 pIfTable
->table
[0].dwInUcastPkts
, pIfTable
->table
[0].dwOutUcastPkts
);
414 _tprintf(_T("%-20s %14lu %15lu\n"), _T("Non-unicast packets"),
415 pIfTable
->table
[0].dwInNUcastPkts
, pIfTable
->table
[0].dwOutNUcastPkts
);
416 _tprintf(_T("%-20s %14lu %15lu\n"), _T("Discards"),
417 pIfTable
->table
[0].dwInDiscards
, pIfTable
->table
[0].dwOutDiscards
);
418 _tprintf(_T("%-20s %14lu %15lu\n"), _T("Errors"),
419 pIfTable
->table
[0].dwInErrors
, pIfTable
->table
[0].dwOutErrors
);
420 _tprintf(_T("%-20s %14lu\n"), _T("Unknown Protocols"),
421 pIfTable
->table
[0].dwInUnknownProtos
);
424 DoFormatMessage(dwRetVal
);
430 PMIB_TCPTABLE tcpTable
;
433 CHAR HostIp
[HOSTNAMELEN
], HostPort
[PORTNAMELEN
];
434 CHAR RemoteIp
[HOSTNAMELEN
], RemotePort
[PORTNAMELEN
];
435 CHAR Host
[ADDRESSLEN
];
436 CHAR Remote
[ADDRESSLEN
];
438 /* Get the table of TCP endpoints */
440 error
= GetTcpTable(NULL
, &dwSize
, TRUE
);
441 if (error
!= ERROR_INSUFFICIENT_BUFFER
)
443 printf("Failed to snapshot TCP endpoints.\n");
444 DoFormatMessage(error
);
447 tcpTable
= (PMIB_TCPTABLE
)malloc(dwSize
);
448 error
= GetTcpTable(tcpTable
, &dwSize
, TRUE
);
451 printf("Failed to snapshot TCP endpoints table.\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 GetIpHostName(FALSE
, tcpTable
->table
[i
].dwRemoteAddr
, RemoteIp
, HOSTNAMELEN
);
469 GetPortName(tcpTable
->table
[i
].dwRemotePort
, "tcp", RemotePort
, PORTNAMELEN
);
471 sprintf(Host
, "%s:%s", HostIp
, HostPort
);
472 sprintf(Remote
, "%s:%s", RemoteIp
, RemotePort
);
474 _tprintf(_T(" %-6s %-22s %-22s %s\n"), _T("TCP"),
475 Host
, Remote
, TcpState
[tcpTable
->table
[i
].dwState
]);
483 PMIB_UDPTABLE udpTable
;
486 CHAR HostIp
[HOSTNAMELEN
], HostPort
[PORTNAMELEN
];
487 CHAR Host
[ADDRESSLEN
];
489 /* Get the table of UDP endpoints */
491 error
= GetUdpTable(NULL
, &dwSize
, TRUE
);
492 if (error
!= ERROR_INSUFFICIENT_BUFFER
)
494 printf("Failed to snapshot UDP endpoints.\n");
495 DoFormatMessage(error
);
498 udpTable
= (PMIB_UDPTABLE
)malloc(dwSize
);
499 error
= GetUdpTable(udpTable
, &dwSize
, TRUE
);
502 printf("Failed to snapshot UDP endpoints table.\n");
503 DoFormatMessage(error
);
507 /* Dump the UDP table */
508 for (i
= 0; i
< udpTable
->dwNumEntries
; i
++)
511 /* I've split this up so it's easier to follow */
512 GetIpHostName(TRUE
, udpTable
->table
[i
].dwLocalAddr
, HostIp
, HOSTNAMELEN
);
513 GetPortName(udpTable
->table
[i
].dwLocalPort
, "tcp", HostPort
, PORTNAMELEN
);
515 sprintf(Host
, "%s:%s", HostIp
, HostPort
);
517 _tprintf(_T(" %-6s %-22s %-22s\n"), _T("UDP"), Host
, _T(":*:"));
523 * Translate port numbers into their text equivalent if there is one
526 GetPortName(UINT Port
, PCSTR Proto
, CHAR Name
[], INT NameLen
)
528 struct servent
*pSrvent
;
532 sprintf(Name
, "%d", htons((WORD
)Port
));
535 /* Try to translate to a name */
536 if ((pSrvent
= getservbyport(Port
, Proto
)))
537 strcpy(Name
, pSrvent
->s_name
);
539 sprintf(Name
, "%d", htons((WORD
)Port
));
547 // Translate IP addresses into their name-resolved form if possible.
550 GetIpHostName(BOOL Local
, UINT IpAddr
, CHAR Name
[], int NameLen
)
552 // struct hostent *phostent;
555 // Does the user want raw numbers?
556 nIpAddr
= htonl(IpAddr
);
557 if (bDoShowNumbers
) {
558 sprintf(Name
, "%d.%d.%d.%d",
559 (nIpAddr
>> 24) & 0xFF,
560 (nIpAddr
>> 16) & 0xFF,
561 (nIpAddr
>> 8) & 0xFF,
568 // Try to translate to a name
571 sprintf(Name
, "%d.%d.%d.%d",
572 (nIpAddr
>> 24) & 0xFF,
573 (nIpAddr
>> 16) & 0xFF,
574 (nIpAddr
>> 8) & 0xFF,
577 //gethostname(name, namelen);
579 } else if (IpAddr
== 0x0100007f) {
581 //gethostname(name, namelen);
583 strcpy(Name
, "localhost");
585 // } else if (phostent = gethostbyaddr((char*)&ipaddr, sizeof(nipaddr), PF_INET)) {
586 // strcpy(name, phostent->h_name);
588 sprintf(Name
, "%d.%d.%d.%d",
589 ((nIpAddr
>> 24) & 0x000000FF),
590 ((nIpAddr
>> 16) & 0x000000FF),
591 ((nIpAddr
>> 8) & 0x000000FF),
592 ((nIpAddr
) & 0x000000FF));
599 _tprintf(_T("Displays current TCP/IP protocol statistics and network connections.\n\n"
600 "NETSTAT [-a] [-e] [-n] [-s] [-p proto] [-r] [interval]\n\n"
601 " -a Displays all connections and listening ports.\n"
602 " -e Displays Ethernet statistics. May be combined with -s\n"
604 " -n Displays address and port numbers in numeric form.\n"
605 " -p proto Shows connections for protocol 'proto' TCP or UDP.\n"
606 " If used with the -s option to display\n"
607 " per-protocol statistics, 'proto' may be TCP, UDP, or IP.\n"
608 " -r Displays the current routing table.\n"
609 " -s Displays per-protocol statistics. By default, Statistics are\n"
610 " shown for IP, ICMP, TCP and UDP;\n"
611 " the -p option may be used to specify a subset of the default.\n"
612 " interval Redisplays selected statistics every 'interval' seconds.\n"
613 " Press CTRL+C to stop redisplaying. By default netstat will\n"
614 " print the current information only once.\n"));
622 * Parse command line parameters and set any options
623 * Run display output, looping over set intervals if a number is given
626 int main(int argc
, char *argv
[])
628 if (ParseCmdline(argc
, argv
))
637 Sleep(Interval
*1000);