1 /*
2  * PROJECT:     ReactOS netstat utility
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        apps/utils/net/netstat/netstat.c
5  * PURPOSE:     display IP stack statistics
6  * COPYRIGHT:   Copyright 2005 Ged Murphy <gedmurphy@gmail.com>
7  */
8 /*
9  * TODO:
10  * sort function return values.
11  * implement -b, -o and -v
12  * clean up GetIpHostName
13  * command line parser needs more work
14  */
15 
16 #define WIN32_NO_STATUS
17 #include <stdarg.h>
18 #include <windef.h>
19 #include <winbase.h>
20 #define _INC_WINDOWS
21 #include <winsock2.h>
22 #include <tchar.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <iphlpapi.h>
26 
27 #include "netstat.h"
28 
29 enum ProtoType {IP, TCP, UDP, ICMP} Protocol;
30 DWORD Interval; /* time to pause between printing output */
31 
32 /* TCP endpoint states */
33 TCHAR TcpState[][32] = {
34     _T("???"),
35     _T("CLOSED"),
36     _T("LISTENING"),
37     _T("SYN_SENT"),
38     _T("SYN_RCVD"),
39     _T("ESTABLISHED"),
40     _T("FIN_WAIT1"),
41     _T("FIN_WAIT2"),
42     _T("CLOSE_WAIT"),
43     _T("CLOSING"),
44     _T("LAST_ACK"),
45     _T("TIME_WAIT"),
46     _T("DELETE_TCB")
47 };
48 
49 /*
50  * format message string and display output
51  */
52 DWORD DoFormatMessage(DWORD ErrorCode)
53 {
54     LPVOID lpMsgBuf;
55     DWORD RetVal;
56 
57     if ((RetVal = FormatMessage(
58             FORMAT_MESSAGE_ALLOCATE_BUFFER |
59             FORMAT_MESSAGE_FROM_SYSTEM |
60             FORMAT_MESSAGE_IGNORE_INSERTS,
61             NULL,
62             ErrorCode,
63             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
64             (LPTSTR) &lpMsgBuf,
65             0,
66             NULL )))
67     {
68         _tprintf(_T("%s"), (LPTSTR)lpMsgBuf);
69 
70         LocalFree(lpMsgBuf);
71         /* return number of TCHAR's stored in output buffer
72          * excluding '\0' - as FormatMessage does*/
73         return RetVal;
74     }
75     else
76         return 0;
77 }
78 
79 /*
80  *
81  * Parse command line parameters and set any options
82  *
83  */
84 BOOL ParseCmdline(int argc, char* argv[])
85 {
86     INT i;
87 
88     TCHAR Proto[5];
89 
90     if ((argc == 1) || (_istdigit(*argv[1])))
91         bNoOptions = TRUE;
92 
93     /* Parse command line for options we have been given. */
94     for (i = 1; i < argc; i++)
95     {
96         if ( (argc > 1)&&(argv[i][0] == '-') )
97         {
98             TCHAR c;
99 
100             while ((c = *++argv[i]) != '\0')
101             {
102                 switch (tolower(c))
103                 {
104                     case 'a' :
105                         bDoShowAllCons = TRUE;
106                         break;
107                     case 'b' :
108                         bDoShowProcName = TRUE;
109                         break;
110                     case 'e' :
111                         bDoShowEthStats = TRUE;
112                         break;
113                     case 'n' :
114                         bDoShowNumbers = TRUE;
115                         break;
116                     case 's' :
117                         bDoShowProtoStats = TRUE;
118                         break;
119                     case 'p' :
120                         bDoShowProtoCons = TRUE;
121 
122                         strncpy(Proto, (++argv)[i], sizeof(Proto));
123                         if (!_tcsicmp( "IP", Proto ))
124                             Protocol = IP;
125                         else if (!_tcsicmp( "ICMP", Proto ))
126                             Protocol = ICMP;
127                         else if (!_tcsicmp( "TCP", Proto ))
128                             Protocol = TCP;
129                         else if (!_tcsicmp( "UDP", Proto ))
130                             Protocol = UDP;
131                         else
132                         {
133                             Usage();
134                             return EXIT_FAILURE;
135                         }
136                         --i; /* move pointer back down to previous argv */
137                         break;
138                     case 'r' :
139                         bDoShowRouteTable = TRUE;
140                         break;
141                     case 'v' :
142                         _tprintf(_T("got v\n"));
143                         bDoDispSeqComp = TRUE;
144                         break;
145                     default :
146                         Usage();
147                         return EXIT_FAILURE;
148                 }
149             }
150         }
151         else if (_istdigit(*argv[i]))
152         {
153             if (_stscanf(argv[i], "%lu", &Interval) != EOF)
154                 bLoopOutput = TRUE;
155             else
156                 return EXIT_FAILURE;
157         }
158 //        else
159 //        {
160 //            Usage();
161 //            EXIT_FAILURE;
162 //        }
163     }
164 
165     return EXIT_SUCCESS;
166 }
167 
168 /*
169  * Simulate Microsofts netstat utility output
170  */
171 BOOL DisplayOutput()
172 {
173     if (bNoOptions)
174     {
175         _tprintf(_T("\n  Proto  Local Address          Foreign Address        State\n"));
176         ShowTcpTable();
177         return EXIT_SUCCESS;
178     }
179 
180     if (bDoShowRouteTable)
181     {
182         /* mingw doesn't have lib for _tsystem */
183         if (system("route print") == -1)
184         {
185             _tprintf(_T("cannot find 'route.exe'\n"));
186             return EXIT_FAILURE;
187         }
188         return EXIT_SUCCESS;
189     }
190 
191     if (bDoShowEthStats)
192     {
193         ShowEthernetStatistics();
194         return EXIT_SUCCESS;
195     }
196 
197     if (bDoShowProtoCons)
198     {
199         switch (Protocol)
200         {
201                 case IP :
202                     if (bDoShowProtoStats)
203                     {
204                         ShowIpStatistics();
205                         return EXIT_SUCCESS;
206                     }
207                     break;
208                 case ICMP :
209                     if (bDoShowProtoStats)
210                     {
211                         ShowIcmpStatistics();
212                         return EXIT_SUCCESS;
213                     }
214                     break;
215                 case TCP :
216                     if (bDoShowProtoStats)
217                         ShowTcpStatistics();
218                     _tprintf(_T("\nActive Connections\n"));
219                     _tprintf(_T("\n  Proto  Local Address          Foreign Address        State\n"));
220                     ShowTcpTable();
221                     break;
222                 case UDP :
223                     if (bDoShowProtoStats)
224                         ShowUdpStatistics();
225                     _tprintf(_T("\nActive Connections\n"));
226                     _tprintf(_T("\n  Proto  Local Address          Foreign Address        State\n"));
227                     ShowUdpTable();
228                     break;
229                 default :
230                     break;
231         }
232     }
233     else if (bDoShowProtoStats)
234     {
235         ShowIpStatistics();
236         ShowIcmpStatistics();
237         ShowTcpStatistics();
238         ShowUdpStatistics();
239         return EXIT_SUCCESS;
240     }
241     else
242     {
243         _tprintf(_T("\nActive Connections\n"));
244         _tprintf(_T("\n  Proto  Local Address          Foreign Address        State\n"));
245         ShowTcpTable();
246 		if (bDoShowAllCons)
247             ShowUdpTable();
248     }
249     return EXIT_SUCCESS;
250 }
251 
252 VOID ShowIpStatistics()
253 {
254     PMIB_IPSTATS pIpStats;
255     DWORD dwRetVal;
256 
257     pIpStats = (MIB_IPSTATS*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPSTATS));
258 
259     if ((dwRetVal = GetIpStatistics(pIpStats)) == NO_ERROR)
260     {
261         _tprintf(_T("\nIPv4 Statistics\n\n"));
262         _tprintf(_T("  %-34s = %lu\n"), _T("Packets Received"), pIpStats->dwInReceives);
263         _tprintf(_T("  %-34s = %lu\n"), _T("Received Header Errors"), pIpStats->dwInHdrErrors);
264         _tprintf(_T("  %-34s = %lu\n"), _T("Received Address Errors"), pIpStats->dwInAddrErrors);
265         _tprintf(_T("  %-34s = %lu\n"), _T("Datagrams Forwarded"), pIpStats->dwForwDatagrams);
266         _tprintf(_T("  %-34s = %lu\n"), _T("Unknown Protocols Received"), pIpStats->dwInUnknownProtos);
267         _tprintf(_T("  %-34s = %lu\n"), _T("Received Packets Discarded"), pIpStats->dwInDiscards);
268         _tprintf(_T("  %-34s = %lu\n"), _T("Received Packets Delivered"), pIpStats->dwInDelivers);
269         _tprintf(_T("  %-34s = %lu\n"), _T("Output Requests"), pIpStats->dwOutRequests);
270         _tprintf(_T("  %-34s = %lu\n"), _T("Routing Discards"), pIpStats->dwRoutingDiscards);
271         _tprintf(_T("  %-34s = %lu\n"), _T("Discarded Output Packets"), pIpStats->dwOutDiscards);
272         _tprintf(_T("  %-34s = %lu\n"), _T("Output Packets No Route"), pIpStats->dwOutNoRoutes);
273         _tprintf(_T("  %-34s = %lu\n"), _T("Reassembly Required"), pIpStats->dwReasmReqds);
274         _tprintf(_T("  %-34s = %lu\n"), _T("Reassembly Succesful"), pIpStats->dwReasmOks);
275         _tprintf(_T("  %-34s = %lu\n"), _T("Reassembly Failures"), pIpStats->dwReasmFails);
276        // _tprintf(_T("  %-34s = %lu\n"), _T("Datagrams succesfully fragmented"), NULL); /* FIXME: what is this one? */
277         _tprintf(_T("  %-34s = %lu\n"), _T("Datagrams Failing Fragmentation"), pIpStats->dwFragFails);
278         _tprintf(_T("  %-34s = %lu\n"), _T("Fragments Created"), pIpStats->dwFragCreates);
279     }
280     else
281         DoFormatMessage(dwRetVal);
282 
283     HeapFree(GetProcessHeap(), 0, pIpStats);
284 }
285 
286 VOID ShowIcmpStatistics()
287 {
288     PMIB_ICMP pIcmpStats;
289     DWORD dwRetVal;
290 
291     pIcmpStats = (MIB_ICMP*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_ICMP));
292 
293     if ((dwRetVal = GetIcmpStatistics(pIcmpStats)) == NO_ERROR)
294     {
295         _tprintf(_T("\nICMPv4 Statistics\n\n"));
296         _tprintf(_T("                            Received    Sent\n"));
297         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Messages"),
298             pIcmpStats->stats.icmpInStats.dwMsgs, pIcmpStats->stats.icmpOutStats.dwMsgs);
299         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Errors"),
300             pIcmpStats->stats.icmpInStats.dwErrors, pIcmpStats->stats.icmpOutStats.dwErrors);
301         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Destination Unreachable"),
302             pIcmpStats->stats.icmpInStats.dwDestUnreachs, pIcmpStats->stats.icmpOutStats.dwDestUnreachs);
303         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Time Exceeded"),
304             pIcmpStats->stats.icmpInStats.dwTimeExcds, pIcmpStats->stats.icmpOutStats.dwTimeExcds);
305         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Parameter Problems"),
306             pIcmpStats->stats.icmpInStats.dwParmProbs, pIcmpStats->stats.icmpOutStats.dwParmProbs);
307         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Source Quenches"),
308             pIcmpStats->stats.icmpInStats.dwSrcQuenchs, pIcmpStats->stats.icmpOutStats.dwSrcQuenchs);
309         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Redirects"),
310             pIcmpStats->stats.icmpInStats.dwRedirects, pIcmpStats->stats.icmpOutStats.dwRedirects);
311         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Echos"),
312             pIcmpStats->stats.icmpInStats.dwEchos, pIcmpStats->stats.icmpOutStats.dwEchos);
313         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Echo Replies"),
314             pIcmpStats->stats.icmpInStats.dwEchoReps, pIcmpStats->stats.icmpOutStats.dwEchoReps);
315         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Timestamps"),
316             pIcmpStats->stats.icmpInStats.dwTimestamps, pIcmpStats->stats.icmpOutStats.dwTimestamps);
317         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Timestamp Replies"),
318             pIcmpStats->stats.icmpInStats.dwTimestampReps, pIcmpStats->stats.icmpOutStats.dwTimestampReps);
319         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Address Masks"),
320             pIcmpStats->stats.icmpInStats.dwAddrMasks, pIcmpStats->stats.icmpOutStats.dwAddrMasks);
321         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Address Mask Replies"),
322             pIcmpStats->stats.icmpInStats.dwAddrMaskReps, pIcmpStats->stats.icmpOutStats.dwAddrMaskReps);
323     }
324     else
325         DoFormatMessage(dwRetVal);
326 
327     HeapFree(GetProcessHeap(), 0, pIcmpStats);
328 
329 }
330 
331 VOID ShowTcpStatistics()
332 {
333     PMIB_TCPSTATS pTcpStats;
334     DWORD dwRetVal;
335 
336     pTcpStats = (MIB_TCPSTATS*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_TCPSTATS));
337 
338     if ((dwRetVal = GetTcpStatistics(pTcpStats)) == NO_ERROR)
339     {
340         _tprintf(_T("\nTCP Statistics for IPv4\n\n"));
341         _tprintf(_T("  %-35s = %lu\n"), _T("Active Opens"), pTcpStats->dwActiveOpens);
342         _tprintf(_T("  %-35s = %lu\n"), _T("Passive Opens"), pTcpStats->dwPassiveOpens);
343         _tprintf(_T("  %-35s = %lu\n"), _T("Failed Connection Attempts"), pTcpStats->dwAttemptFails);
344         _tprintf(_T("  %-35s = %lu\n"), _T("Reset Connections"), pTcpStats->dwEstabResets);
345         _tprintf(_T("  %-35s = %lu\n"), _T("Current Connections"), pTcpStats->dwCurrEstab);
346         _tprintf(_T("  %-35s = %lu\n"), _T("Segments Received"), pTcpStats->dwInSegs);
347         _tprintf(_T("  %-35s = %lu\n"), _T("Segments Sent"), pTcpStats->dwOutSegs);
348         _tprintf(_T("  %-35s = %lu\n"), _T("Segments Retransmitted"), pTcpStats->dwRetransSegs);
349     }
350     else
351         DoFormatMessage(dwRetVal);
352 
353     HeapFree(GetProcessHeap(), 0, pTcpStats);
354 }
355 
356 VOID ShowUdpStatistics()
357 {
358     PMIB_UDPSTATS pUdpStats;
359     DWORD dwRetVal;
360 
361     pUdpStats = (MIB_UDPSTATS*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_UDPSTATS));
362 
363     if ((dwRetVal = GetUdpStatistics(pUdpStats)) == NO_ERROR)
364     {
365         _tprintf(_T("\nUDP Statistics for IPv4\n\n"));
366         _tprintf(_T("  %-21s = %lu\n"), _T("Datagrams Received"), pUdpStats->dwInDatagrams);
367         _tprintf(_T("  %-21s = %lu\n"), _T("No Ports"), pUdpStats->dwNoPorts);
368         _tprintf(_T("  %-21s = %lu\n"), _T("Receive Errors"), pUdpStats->dwInErrors);
369         _tprintf(_T("  %-21s = %lu\n"), _T("Datagrams Sent"), pUdpStats->dwOutDatagrams);
370     }
371     else
372         DoFormatMessage(dwRetVal);
373 
374     HeapFree(GetProcessHeap(), 0, pUdpStats);
375 }
376 
377 VOID ShowEthernetStatistics()
378 {
379     PMIB_IFTABLE pIfTable;
380     DWORD dwSize = 0;
381     DWORD dwRetVal = 0;
382 
383     pIfTable = (MIB_IFTABLE*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IFTABLE));
384 
385     if (GetIfTable(pIfTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER)
386     {
387         HeapFree(GetProcessHeap(), 0, pIfTable);
388         pIfTable = (MIB_IFTABLE*) HeapAlloc(GetProcessHeap(), 0, dwSize);
389 
390         if ((dwRetVal = GetIfTable(pIfTable, &dwSize, 0)) == NO_ERROR)
391         {
392             _tprintf(_T("Interface Statistics\n\n"));
393             _tprintf(_T("                           Received            Sent\n\n"));
394             _tprintf(_T("%-20s %14lu %15lu\n"), _T("Bytes"),
395                 pIfTable->table[0].dwInOctets, pIfTable->table[0].dwOutOctets);
396             _tprintf(_T("%-20s %14lu %15lu\n"), _T("Unicast packets"),
397                 pIfTable->table[0].dwInUcastPkts, pIfTable->table[0].dwOutUcastPkts);
398             _tprintf(_T("%-20s %14lu %15lu\n"), _T("Non-unicast packets"),
399                 pIfTable->table[0].dwInNUcastPkts, pIfTable->table[0].dwOutNUcastPkts);
400             _tprintf(_T("%-20s %14lu %15lu\n"), _T("Discards"),
401                 pIfTable->table[0].dwInDiscards, pIfTable->table[0].dwOutDiscards);
402             _tprintf(_T("%-20s %14lu %15lu\n"), _T("Errors"),
403                 pIfTable->table[0].dwInErrors, pIfTable->table[0].dwOutErrors);
404             _tprintf(_T("%-20s %14lu\n"), _T("Unknown Protocols"),
405                 pIfTable->table[0].dwInUnknownProtos);
406         }
407         else
408             DoFormatMessage(dwRetVal);
409     }
410     HeapFree(GetProcessHeap(), 0, pIfTable);
411 }
412 
413 VOID ShowTcpTable()
414 {
415     PMIB_TCPTABLE tcpTable;
416     DWORD error, dwSize;
417     DWORD i;
418     CHAR HostIp[HOSTNAMELEN], HostPort[PORTNAMELEN];
419     CHAR RemoteIp[HOSTNAMELEN], RemotePort[PORTNAMELEN];
420     CHAR Host[ADDRESSLEN];
421     CHAR Remote[ADDRESSLEN];
422 
423     /* Get the table of TCP endpoints */
424     dwSize = sizeof (MIB_TCPTABLE);
425     /* Should also work when we get new connections between 2 GetTcpTable()
426      * calls: */
427     do
428     {
429         tcpTable = (PMIB_TCPTABLE) HeapAlloc(GetProcessHeap(), 0, dwSize);
430         error = GetTcpTable(tcpTable, &dwSize, TRUE);
431         if ( error != NO_ERROR )
432             HeapFree(GetProcessHeap(), 0, tcpTable);
433     }
434     while  ( error == ERROR_INSUFFICIENT_BUFFER );
435 
436     if (error != NO_ERROR)
437     {
438         printf("Failed to snapshot TCP endpoints.\n");
439         DoFormatMessage(error);
440         exit(EXIT_FAILURE);
441     }
442 
443     /* Dump the TCP table */
444     for (i = 0; i < tcpTable->dwNumEntries; i++)
445     {
446         /* If we aren't showing all connections, only display established, close wait
447          * and time wait. This is the default output for netstat */
448         if (bDoShowAllCons || (tcpTable->table[i].dwState ==  MIB_TCP_STATE_ESTAB)
449             || (tcpTable->table[i].dwState ==  MIB_TCP_STATE_CLOSE_WAIT)
450             || (tcpTable->table[i].dwState ==  MIB_TCP_STATE_TIME_WAIT))
451         {
452             /* I've split this up so it's easier to follow */
453             GetIpHostName(TRUE, tcpTable->table[i].dwLocalAddr, HostIp, HOSTNAMELEN);
454             GetPortName(tcpTable->table[i].dwLocalPort, "tcp", HostPort, PORTNAMELEN);
455             GetIpHostName(FALSE, tcpTable->table[i].dwRemoteAddr, RemoteIp, HOSTNAMELEN);
456             GetPortName(tcpTable->table[i].dwRemotePort, "tcp", RemotePort, PORTNAMELEN);
457 
458             sprintf(Host, "%s:%s", HostIp, HostPort);
459             sprintf(Remote, "%s:%s", RemoteIp, RemotePort);
460 
461             _tprintf(_T("  %-6s %-22s %-22s %s\n"), _T("TCP"),
462             Host, Remote, TcpState[tcpTable->table[i].dwState]);
463         }
464     }
465     HeapFree(GetProcessHeap(), 0, tcpTable);
466 }
467 
468 VOID ShowUdpTable()
469 {
470     PMIB_UDPTABLE udpTable;
471     DWORD error, dwSize;
472     DWORD i;
473     CHAR HostIp[HOSTNAMELEN], HostPort[PORTNAMELEN];
474     CHAR Host[ADDRESSLEN];
475 
476     /* Get the table of UDP endpoints */
477     dwSize = 0;
478     error = GetUdpTable(NULL, &dwSize, TRUE);
479     if (error != ERROR_INSUFFICIENT_BUFFER)
480     {
481         printf("Failed to snapshot UDP endpoints.\n");
482         DoFormatMessage(error);
483         exit(EXIT_FAILURE);
484     }
485     udpTable = (PMIB_UDPTABLE) HeapAlloc(GetProcessHeap(), 0, dwSize);
486     error = GetUdpTable(udpTable, &dwSize, TRUE);
487     if (error)
488     {
489         printf("Failed to snapshot UDP endpoints table.\n");
490         DoFormatMessage(error);
491         HeapFree(GetProcessHeap(), 0, udpTable);
492         exit(EXIT_FAILURE);
493     }
494 
495     /* Dump the UDP table */
496     for (i = 0; i < udpTable->dwNumEntries; i++)
497     {
498 
499         /* I've split this up so it's easier to follow */
500         GetIpHostName(TRUE, udpTable->table[i].dwLocalAddr, HostIp, HOSTNAMELEN);
501         GetPortName(udpTable->table[i].dwLocalPort, "tcp", HostPort, PORTNAMELEN);
502 
503         sprintf(Host, "%s:%s", HostIp, HostPort);
504 
505         _tprintf(_T("  %-6s %-22s %-22s\n"), _T("UDP"), Host,  _T("*:*"));
506     }
507 
508     HeapFree(GetProcessHeap(), 0, udpTable);
509 }
510 
511 /*
512  * Translate port numbers into their text equivalent if there is one
513  */
514 PCHAR
515 GetPortName(UINT Port, PCSTR Proto, CHAR Name[], INT NameLen)
516 {
517     struct servent *pSrvent;
518 
519     if (bDoShowNumbers)
520     {
521         sprintf(Name, "%d", htons((WORD)Port));
522         return Name;
523     }
524     /* Try to translate to a name */
525     if ((pSrvent = getservbyport(Port, Proto)))
526         strcpy(Name, pSrvent->s_name );
527     else
528         sprintf(Name, "%d", htons((WORD)Port));
529     return Name;
530 }
531 
532 /*
533  * convert addresses into dotted decimal or hostname
534  */
535 PCHAR
536 GetIpHostName(BOOL Local, UINT IpAddr, CHAR Name[], int NameLen)
537 {
538 //  struct hostent *phostent;
539     UINT nIpAddr;
540 
541     /* display dotted decimal */
542     nIpAddr = htonl(IpAddr);
543     if (bDoShowNumbers) {
544         sprintf(Name, "%d.%d.%d.%d",
545             (nIpAddr >> 24) & 0xFF,
546             (nIpAddr >> 16) & 0xFF,
547             (nIpAddr >> 8) & 0xFF,
548             (nIpAddr) & 0xFF);
549         return Name;
550     }
551 
552     Name[0] = _T('\0');
553 
554     /* try to resolve the name */
555     if (!IpAddr) {
556         if (!Local) {
557             sprintf(Name, "%d.%d.%d.%d",
558                 (nIpAddr >> 24) & 0xFF,
559                 (nIpAddr >> 16) & 0xFF,
560                 (nIpAddr >> 8) & 0xFF,
561                 (nIpAddr) & 0xFF);
562         } else {
563             if (gethostname(Name, NameLen) != 0)
564                 DoFormatMessage(WSAGetLastError());
565         }
566     } else if (IpAddr == 0x0100007f) {
567         if (Local) {
568             if (gethostname(Name, NameLen) != 0)
569                 DoFormatMessage(WSAGetLastError());
570         } else {
571             _tcsncpy(Name, _T("localhost"), 10);
572         }
573 //  } else if (phostent = gethostbyaddr((char*)&ipaddr, sizeof(nipaddr), PF_INET)) {
574 //      strcpy(name, phostent->h_name);
575     } else {
576         sprintf(Name, "%d.%d.%d.%d",
577             ((nIpAddr >> 24) & 0x000000FF),
578             ((nIpAddr >> 16) & 0x000000FF),
579             ((nIpAddr >> 8) & 0x000000FF),
580             ((nIpAddr) & 0x000000FF));
581     }
582     return Name;
583 }
584 
585 VOID Usage()
586 {
587     _tprintf(_T("\nDisplays current TCP/IP protocol statistics and network connections.\n\n"
588     "NETSTAT [-a] [-e] [-n] [-s] [-p proto] [-r] [interval]\n\n"
589     "  -a            Displays all connections and listening ports.\n"
590     "  -e            Displays Ethernet statistics. May be combined with -s\n"
591     "                option\n"
592     "  -n            Displays address and port numbers in numeric form.\n"
593     "  -p proto      Shows connections for protocol 'proto' TCP or UDP.\n"
594     "                If used with the -s option to display\n"
595     "                per-protocol statistics, 'proto' may be TCP, UDP, or IP.\n"
596     "  -r            Displays the current routing table.\n"
597     "  -s            Displays per-protocol statistics. By default, Statistics are\n"
598     "                shown for IP, ICMP, TCP and UDP;\n"
599     "                the -p option may be used to specify a subset of the default.\n"
600     "  interval      Redisplays selected statistics every 'interval' seconds.\n"
601     "                Press CTRL+C to stop redisplaying. By default netstat will\n"
602     "                print the current information only once.\n"));
603 }
604 
605 /*
606  *
607  * Parse command line parameters and set any options
608  * Run display output, looping over set intervals if a number is given
609  *
610  */
611 int main(int argc, char *argv[])
612 {
613     WSADATA wsaData;
614 
615     if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
616     {
617         _tprintf(_T("WSAStartup() failed : %d\n"), WSAGetLastError());
618         return -1;
619     }
620 
621     if (ParseCmdline(argc, argv))
622         return -1;
623 
624     if (bLoopOutput)
625     {
626         while (1)
627         {
628             if (DisplayOutput())
629                 return -1;
630             Sleep(Interval*1000);
631         }
632     }
633 
634     if (DisplayOutput())
635         return -1;
636     else
637         return 0;
638 }
639