1 /*
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>
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     LPSTR Proto;
87     CHAR c;
88     INT i;
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] == '-' || argv[i][0] == '/'))
97         {
98             while ((c = *++argv[i]) != '\0')
99             {
100                 switch (tolower(c))
101                 {
102                     case 'a' :
103                         bDoShowAllCons = TRUE;
104                         break;
105                     case 'b' :
106                         bDoShowProcName = TRUE;
107                         break;
108                     case 'e' :
109                         bDoShowEthStats = TRUE;
110                         break;
111                     case 'n' :
112                         bDoShowNumbers = TRUE;
113                         break;
114                     case 'p' :
115                         bDoShowProtoCons = TRUE;
116                         Proto = argv[i+1];
117                         if (!_stricmp("IP", Proto))
118                             Protocol = IP;
119                         else if (!_stricmp("ICMP", Proto))
120                             Protocol = ICMP;
121                         else if (!_stricmp("TCP", Proto))
122                             Protocol = TCP;
123                         else if (!_stricmp("UDP", Proto))
124                             Protocol = UDP;
125                         else
126                         {
127                             Usage();
128                             return EXIT_FAILURE;
129                         }
130                         break;
131                     case 'r' :
132                         bDoShowRouteTable = TRUE;
133                         break;
134                     case 's' :
135                         bDoShowProtoStats = TRUE;
136                         break;
137                     case 'o' :
138                         bDoShowProcessId = TRUE;
139                         break;
140                     case 'v' :
141                         _tprintf(_T("got v\n"));
142                         bDoDispSeqComp = TRUE;
143                         break;
144                     default :
145                         Usage();
146                         return EXIT_FAILURE;
147                 }
148             }
149         }
150         else if (_istdigit(*argv[i]))
151         {
152             if (_stscanf(argv[i], "%lu", &Interval) != EOF)
153                 bLoopOutput = TRUE;
154             else
155                 return EXIT_FAILURE;
156         }
157 //        else
158 //        {
159 //            Usage();
160 //            EXIT_FAILURE;
161 //        }
162     }
163 
164     return EXIT_SUCCESS;
165 }
166 
167 /*
168  * Display table header
169  */
170 VOID DisplayTableHeader()
171 {
172     _tprintf(_T("\n  Proto  Local Address          Foreign Address        State"));
173     if (bDoShowProcessId)
174         _tprintf(_T("       Process\n"));
175     else
176         _tprintf(_T("\n"));
177 }
178 
179 
180 /*
181  * Simulate Microsofts netstat utility output
182  */
183 BOOL DisplayOutput()
184 {
185     if (bNoOptions)
186     {
187         DisplayTableHeader();
188         ShowTcpTable();
189         return EXIT_SUCCESS;
190     }
191 
192     if (bDoShowRouteTable)
193     {
194         /* mingw doesn't have lib for _tsystem */
195         if (system("route print") == -1)
196         {
197             _tprintf(_T("cannot find 'route.exe'\n"));
198             return EXIT_FAILURE;
199         }
200         return EXIT_SUCCESS;
201     }
202 
203     if (bDoShowEthStats)
204     {
205         ShowEthernetStatistics();
206         return EXIT_SUCCESS;
207     }
208 
209     if (bDoShowProtoCons)
210     {
211         switch (Protocol)
212         {
213                 case IP :
214                     if (bDoShowProtoStats)
215                     {
216                         ShowIpStatistics();
217                         return EXIT_SUCCESS;
218                     }
219                     break;
220                 case ICMP :
221                     if (bDoShowProtoStats)
222                     {
223                         ShowIcmpStatistics();
224                         return EXIT_SUCCESS;
225                     }
226                     break;
227                 case TCP :
228                     if (bDoShowProtoStats)
229                         ShowTcpStatistics();
230                     _tprintf(_T("\nActive Connections\n"));
231                     DisplayTableHeader();
232                     ShowTcpTable();
233                     break;
234                 case UDP :
235                     if (bDoShowProtoStats)
236                         ShowUdpStatistics();
237                     _tprintf(_T("\nActive Connections\n"));
238                     DisplayTableHeader();
239                     ShowUdpTable();
240                     break;
241                 default :
242                     break;
243         }
244     }
245     else if (bDoShowProtoStats)
246     {
247         ShowIpStatistics();
248         ShowIcmpStatistics();
249         ShowTcpStatistics();
250         ShowUdpStatistics();
251         return EXIT_SUCCESS;
252     }
253     else
254     {
255         _tprintf(_T("\nActive Connections\n"));
256         DisplayTableHeader();
257         ShowTcpTable();
258         if (bDoShowAllCons)
259             ShowUdpTable();
260     }
261     return EXIT_SUCCESS;
262 }
263 
264 VOID ShowIpStatistics()
265 {
266     PMIB_IPSTATS pIpStats;
267     DWORD dwRetVal;
268 
269     pIpStats = (MIB_IPSTATS*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPSTATS));
270 
271     if ((dwRetVal = GetIpStatistics(pIpStats)) == NO_ERROR)
272     {
273         _tprintf(_T("\nIPv4 Statistics\n\n"));
274         _tprintf(_T("  %-34s = %lu\n"), _T("Packets Received"), pIpStats->dwInReceives);
275         _tprintf(_T("  %-34s = %lu\n"), _T("Received Header Errors"), pIpStats->dwInHdrErrors);
276         _tprintf(_T("  %-34s = %lu\n"), _T("Received Address Errors"), pIpStats->dwInAddrErrors);
277         _tprintf(_T("  %-34s = %lu\n"), _T("Datagrams Forwarded"), pIpStats->dwForwDatagrams);
278         _tprintf(_T("  %-34s = %lu\n"), _T("Unknown Protocols Received"), pIpStats->dwInUnknownProtos);
279         _tprintf(_T("  %-34s = %lu\n"), _T("Received Packets Discarded"), pIpStats->dwInDiscards);
280         _tprintf(_T("  %-34s = %lu\n"), _T("Received Packets Delivered"), pIpStats->dwInDelivers);
281         _tprintf(_T("  %-34s = %lu\n"), _T("Output Requests"), pIpStats->dwOutRequests);
282         _tprintf(_T("  %-34s = %lu\n"), _T("Routing Discards"), pIpStats->dwRoutingDiscards);
283         _tprintf(_T("  %-34s = %lu\n"), _T("Discarded Output Packets"), pIpStats->dwOutDiscards);
284         _tprintf(_T("  %-34s = %lu\n"), _T("Output Packets No Route"), pIpStats->dwOutNoRoutes);
285         _tprintf(_T("  %-34s = %lu\n"), _T("Reassembly Required"), pIpStats->dwReasmReqds);
286         _tprintf(_T("  %-34s = %lu\n"), _T("Reassembly Succesful"), pIpStats->dwReasmOks);
287         _tprintf(_T("  %-34s = %lu\n"), _T("Reassembly Failures"), pIpStats->dwReasmFails);
288        // _tprintf(_T("  %-34s = %lu\n"), _T("Datagrams successfully fragmented"), NULL); /* FIXME: what is this one? */
289         _tprintf(_T("  %-34s = %lu\n"), _T("Datagrams Failing Fragmentation"), pIpStats->dwFragFails);
290         _tprintf(_T("  %-34s = %lu\n"), _T("Fragments Created"), pIpStats->dwFragCreates);
291     }
292     else
293         DoFormatMessage(dwRetVal);
294 
295     HeapFree(GetProcessHeap(), 0, pIpStats);
296 }
297 
298 VOID ShowIcmpStatistics()
299 {
300     PMIB_ICMP pIcmpStats;
301     DWORD dwRetVal;
302 
303     pIcmpStats = (MIB_ICMP*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_ICMP));
304 
305     if ((dwRetVal = GetIcmpStatistics(pIcmpStats)) == NO_ERROR)
306     {
307         _tprintf(_T("\nICMPv4 Statistics\n\n"));
308         _tprintf(_T("                            Received    Sent\n"));
309         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Messages"),
310             pIcmpStats->stats.icmpInStats.dwMsgs, pIcmpStats->stats.icmpOutStats.dwMsgs);
311         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Errors"),
312             pIcmpStats->stats.icmpInStats.dwErrors, pIcmpStats->stats.icmpOutStats.dwErrors);
313         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Destination Unreachable"),
314             pIcmpStats->stats.icmpInStats.dwDestUnreachs, pIcmpStats->stats.icmpOutStats.dwDestUnreachs);
315         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Time Exceeded"),
316             pIcmpStats->stats.icmpInStats.dwTimeExcds, pIcmpStats->stats.icmpOutStats.dwTimeExcds);
317         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Parameter Problems"),
318             pIcmpStats->stats.icmpInStats.dwParmProbs, pIcmpStats->stats.icmpOutStats.dwParmProbs);
319         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Source Quenches"),
320             pIcmpStats->stats.icmpInStats.dwSrcQuenchs, pIcmpStats->stats.icmpOutStats.dwSrcQuenchs);
321         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Redirects"),
322             pIcmpStats->stats.icmpInStats.dwRedirects, pIcmpStats->stats.icmpOutStats.dwRedirects);
323         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Echos"),
324             pIcmpStats->stats.icmpInStats.dwEchos, pIcmpStats->stats.icmpOutStats.dwEchos);
325         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Echo Replies"),
326             pIcmpStats->stats.icmpInStats.dwEchoReps, pIcmpStats->stats.icmpOutStats.dwEchoReps);
327         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Timestamps"),
328             pIcmpStats->stats.icmpInStats.dwTimestamps, pIcmpStats->stats.icmpOutStats.dwTimestamps);
329         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Timestamp Replies"),
330             pIcmpStats->stats.icmpInStats.dwTimestampReps, pIcmpStats->stats.icmpOutStats.dwTimestampReps);
331         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Address Masks"),
332             pIcmpStats->stats.icmpInStats.dwAddrMasks, pIcmpStats->stats.icmpOutStats.dwAddrMasks);
333         _tprintf(_T("  %-25s %-11lu %lu\n"), _T("Address Mask Replies"),
334             pIcmpStats->stats.icmpInStats.dwAddrMaskReps, pIcmpStats->stats.icmpOutStats.dwAddrMaskReps);
335     }
336     else
337         DoFormatMessage(dwRetVal);
338 
339     HeapFree(GetProcessHeap(), 0, pIcmpStats);
340 
341 }
342 
343 VOID ShowTcpStatistics()
344 {
345     PMIB_TCPSTATS pTcpStats;
346     DWORD dwRetVal;
347 
348     pTcpStats = (MIB_TCPSTATS*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_TCPSTATS));
349 
350     if ((dwRetVal = GetTcpStatistics(pTcpStats)) == NO_ERROR)
351     {
352         _tprintf(_T("\nTCP Statistics for IPv4\n\n"));
353         _tprintf(_T("  %-35s = %lu\n"), _T("Active Opens"), pTcpStats->dwActiveOpens);
354         _tprintf(_T("  %-35s = %lu\n"), _T("Passive Opens"), pTcpStats->dwPassiveOpens);
355         _tprintf(_T("  %-35s = %lu\n"), _T("Failed Connection Attempts"), pTcpStats->dwAttemptFails);
356         _tprintf(_T("  %-35s = %lu\n"), _T("Reset Connections"), pTcpStats->dwEstabResets);
357         _tprintf(_T("  %-35s = %lu\n"), _T("Current Connections"), pTcpStats->dwCurrEstab);
358         _tprintf(_T("  %-35s = %lu\n"), _T("Segments Received"), pTcpStats->dwInSegs);
359         _tprintf(_T("  %-35s = %lu\n"), _T("Segments Sent"), pTcpStats->dwOutSegs);
360         _tprintf(_T("  %-35s = %lu\n"), _T("Segments Retransmitted"), pTcpStats->dwRetransSegs);
361     }
362     else
363         DoFormatMessage(dwRetVal);
364 
365     HeapFree(GetProcessHeap(), 0, pTcpStats);
366 }
367 
368 VOID ShowUdpStatistics()
369 {
370     PMIB_UDPSTATS pUdpStats;
371     DWORD dwRetVal;
372 
373     pUdpStats = (MIB_UDPSTATS*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_UDPSTATS));
374 
375     if ((dwRetVal = GetUdpStatistics(pUdpStats)) == NO_ERROR)
376     {
377         _tprintf(_T("\nUDP Statistics for IPv4\n\n"));
378         _tprintf(_T("  %-21s = %lu\n"), _T("Datagrams Received"), pUdpStats->dwInDatagrams);
379         _tprintf(_T("  %-21s = %lu\n"), _T("No Ports"), pUdpStats->dwNoPorts);
380         _tprintf(_T("  %-21s = %lu\n"), _T("Receive Errors"), pUdpStats->dwInErrors);
381         _tprintf(_T("  %-21s = %lu\n"), _T("Datagrams Sent"), pUdpStats->dwOutDatagrams);
382     }
383     else
384         DoFormatMessage(dwRetVal);
385 
386     HeapFree(GetProcessHeap(), 0, pUdpStats);
387 }
388 
389 VOID ShowEthernetStatistics()
390 {
391     PMIB_IFTABLE pIfTable;
392     DWORD dwSize = 0;
393     DWORD dwRetVal = 0;
394 
395     pIfTable = (MIB_IFTABLE*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IFTABLE));
396 
397     if (GetIfTable(pIfTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER)
398     {
399         HeapFree(GetProcessHeap(), 0, pIfTable);
400         pIfTable = (MIB_IFTABLE*) HeapAlloc(GetProcessHeap(), 0, dwSize);
401 
402         if ((dwRetVal = GetIfTable(pIfTable, &dwSize, 0)) == NO_ERROR)
403         {
404             _tprintf(_T("Interface Statistics\n\n"));
405             _tprintf(_T("                           Received            Sent\n\n"));
406             _tprintf(_T("%-20s %14lu %15lu\n"), _T("Bytes"),
407                 pIfTable->table[0].dwInOctets, pIfTable->table[0].dwOutOctets);
408             _tprintf(_T("%-20s %14lu %15lu\n"), _T("Unicast packets"),
409                 pIfTable->table[0].dwInUcastPkts, pIfTable->table[0].dwOutUcastPkts);
410             _tprintf(_T("%-20s %14lu %15lu\n"), _T("Non-unicast packets"),
411                 pIfTable->table[0].dwInNUcastPkts, pIfTable->table[0].dwOutNUcastPkts);
412             _tprintf(_T("%-20s %14lu %15lu\n"), _T("Discards"),
413                 pIfTable->table[0].dwInDiscards, pIfTable->table[0].dwOutDiscards);
414             _tprintf(_T("%-20s %14lu %15lu\n"), _T("Errors"),
415                 pIfTable->table[0].dwInErrors, pIfTable->table[0].dwOutErrors);
416             _tprintf(_T("%-20s %14lu\n"), _T("Unknown Protocols"),
417                 pIfTable->table[0].dwInUnknownProtos);
418         }
419         else
420             DoFormatMessage(dwRetVal);
421     }
422     HeapFree(GetProcessHeap(), 0, pIfTable);
423 }
424 
425 VOID ShowTcpTable()
426 {
427     PMIB_TCPTABLE_OWNER_PID tcpTable;
428     DWORD error, dwSize;
429     DWORD i;
430     CHAR HostIp[HOSTNAMELEN], HostPort[PORTNAMELEN];
431     CHAR RemoteIp[HOSTNAMELEN], RemotePort[PORTNAMELEN];
432     CHAR Host[ADDRESSLEN];
433     CHAR Remote[ADDRESSLEN];
434     CHAR PID[64];
435 
436     /* Get the table of TCP endpoints */
437     dwSize = sizeof (MIB_TCPTABLE_OWNER_PID);
438     /* Should also work when we get new connections between 2 GetTcpTable()
439      * calls: */
440     do
441     {
442         tcpTable = (PMIB_TCPTABLE_OWNER_PID) HeapAlloc(GetProcessHeap(), 0, dwSize);
443         error = GetExtendedTcpTable(tcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
444         if ( error != NO_ERROR )
445             HeapFree(GetProcessHeap(), 0, tcpTable);
446     }
447     while  ( error == ERROR_INSUFFICIENT_BUFFER );
448 
449     if (error != NO_ERROR)
450     {
451         printf("Failed to snapshot TCP endpoints.\n");
452         DoFormatMessage(error);
453         exit(EXIT_FAILURE);
454     }
455 
456     /* Dump the TCP table */
457     for (i = 0; i < tcpTable->dwNumEntries; i++)
458     {
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))
464         {
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             sprintf(Host, "%s:%s", HostIp, HostPort);
469 
470             if (tcpTable->table[i].dwState ==  MIB_TCP_STATE_LISTEN)
471             {
472                 sprintf(Remote, "%s:0", HostIp);
473             }
474             else
475             {
476                 GetIpHostName(FALSE, tcpTable->table[i].dwRemoteAddr, RemoteIp, HOSTNAMELEN);
477                 GetPortName(tcpTable->table[i].dwRemotePort, "tcp", RemotePort, PORTNAMELEN);
478                 sprintf(Remote, "%s:%s", RemoteIp, RemotePort);
479             }
480 
481             if (bDoShowProcessId)
482             {
483                 sprintf(PID, "%ld", tcpTable->table[i].dwOwningPid);
484             }
485             else
486             {
487                 PID[0] = 0;
488             }
489 
490             _tprintf(_T("  %-6s %-22s %-22s %-11s %s\n"), _T("TCP"),
491             Host, Remote, TcpState[tcpTable->table[i].dwState], PID);
492         }
493     }
494     HeapFree(GetProcessHeap(), 0, tcpTable);
495 }
496 
497 VOID ShowUdpTable()
498 {
499     PMIB_UDPTABLE_OWNER_PID udpTable;
500     DWORD error, dwSize;
501     DWORD i;
502     CHAR HostIp[HOSTNAMELEN], HostPort[PORTNAMELEN];
503     CHAR Host[ADDRESSLEN];
504     CHAR PID[64];
505 
506     /* Get the table of UDP endpoints */
507     dwSize = 0;
508     error = GetExtendedUdpTable(NULL, &dwSize, TRUE, AF_INET, UDP_TABLE_OWNER_PID, 0);
509     if (error != ERROR_INSUFFICIENT_BUFFER)
510     {
511         printf("Failed to snapshot UDP endpoints.\n");
512         DoFormatMessage(error);
513         exit(EXIT_FAILURE);
514     }
515     udpTable = (PMIB_UDPTABLE_OWNER_PID) HeapAlloc(GetProcessHeap(), 0, dwSize);
516     error = GetExtendedUdpTable(udpTable, &dwSize, TRUE, AF_INET, UDP_TABLE_OWNER_PID, 0);
517     if (error)
518     {
519         printf("Failed to snapshot UDP endpoints table.\n");
520         DoFormatMessage(error);
521         HeapFree(GetProcessHeap(), 0, udpTable);
522         exit(EXIT_FAILURE);
523     }
524 
525     /* Dump the UDP table */
526     for (i = 0; i < udpTable->dwNumEntries; i++)
527     {
528 
529         /* I've split this up so it's easier to follow */
530         GetIpHostName(TRUE, udpTable->table[i].dwLocalAddr, HostIp, HOSTNAMELEN);
531         GetPortName(udpTable->table[i].dwLocalPort, "tcp", HostPort, PORTNAMELEN);
532 
533         sprintf(Host, "%s:%s", HostIp, HostPort);
534 
535         if (bDoShowProcessId)
536         {
537             sprintf(PID, "%ld", udpTable->table[i].dwOwningPid);
538         }
539         else
540         {
541             PID[0] = 0;
542         }
543 
544         _tprintf(_T("  %-6s %-22s %-34s %s\n"), _T("UDP"), Host,  _T("*:*"), PID);
545     }
546 
547     HeapFree(GetProcessHeap(), 0, udpTable);
548 }
549 
550 /*
551  * Translate port numbers into their text equivalent if there is one
552  */
553 PCHAR
554 GetPortName(UINT Port, PCSTR Proto, CHAR Name[], INT NameLen)
555 {
556     struct servent *pServent;
557 
558     if (bDoShowNumbers)
559     {
560         sprintf(Name, "%d", htons((WORD)Port));
561         return Name;
562     }
563     /* Try to translate to a name */
564     if ((pServent = getservbyport(Port, Proto)))
565         strcpy(Name, pServent->s_name );
566     else
567         sprintf(Name, "%d", htons((WORD)Port));
568     return Name;
569 }
570 
571 /*
572  * convert addresses into dotted decimal or hostname
573  */
574 PCHAR
575 GetIpHostName(BOOL Local, UINT IpAddr, CHAR Name[], int NameLen)
576 {
577 //  struct hostent *phostent;
578     UINT nIpAddr;
579 
580     /* display dotted decimal */
581     nIpAddr = htonl(IpAddr);
582     if (bDoShowNumbers) {
583         sprintf(Name, "%d.%d.%d.%d",
584             (nIpAddr >> 24) & 0xFF,
585             (nIpAddr >> 16) & 0xFF,
586             (nIpAddr >> 8) & 0xFF,
587             (nIpAddr) & 0xFF);
588         return Name;
589     }
590 
591     Name[0] = _T('\0');
592 
593     /* try to resolve the name */
594     if (!IpAddr) {
595         if (!Local) {
596             sprintf(Name, "%d.%d.%d.%d",
597                 (nIpAddr >> 24) & 0xFF,
598                 (nIpAddr >> 16) & 0xFF,
599                 (nIpAddr >> 8) & 0xFF,
600                 (nIpAddr) & 0xFF);
601         } else {
602             if (gethostname(Name, NameLen) != 0)
603                 DoFormatMessage(WSAGetLastError());
604         }
605     } else if (IpAddr == 0x0100007f) {
606         if (Local) {
607             if (gethostname(Name, NameLen) != 0)
608                 DoFormatMessage(WSAGetLastError());
609         } else {
610             _tcsncpy(Name, _T("localhost"), 10);
611         }
612 //  } else if (phostent = gethostbyaddr((char*)&ipaddr, sizeof(nipaddr), PF_INET)) {
613 //      strcpy(name, phostent->h_name);
614     } else {
615         sprintf(Name, "%d.%d.%d.%d",
616             ((nIpAddr >> 24) & 0x000000FF),
617             ((nIpAddr >> 16) & 0x000000FF),
618             ((nIpAddr >> 8) & 0x000000FF),
619             ((nIpAddr) & 0x000000FF));
620     }
621     return Name;
622 }
623 
624 VOID Usage()
625 {
626     _tprintf(_T("\nDisplays current TCP/IP protocol statistics and network connections.\n\n"
627     "NETSTAT [-a] [-e] [-n] [-p proto] [-r] [-s] [interval]\n\n"
628     "  -a            Displays all connections and listening ports.\n"
629     "  -e            Displays Ethernet statistics. May be combined with -s\n"
630     "                option\n"
631     "  -n            Displays address and port numbers in numeric form.\n"
632     "  -p proto      Shows connections for protocol 'proto' TCP or UDP.\n"
633     "                If used with the -s option to display\n"
634     "                per-protocol statistics, 'proto' may be TCP, UDP, or IP.\n"
635     "  -r            Displays the current routing table.\n"
636     "  -s            Displays per-protocol statistics. By default, Statistics are\n"
637     "                shown for IP, ICMP, TCP and UDP;\n"
638     "                the -p option may be used to specify a subset of the default.\n"
639     " -o             Displays the process ID for each connection.\n"
640     "  interval      Redisplays selected statistics every 'interval' seconds.\n"
641     "                Press CTRL+C to stop redisplaying. By default netstat will\n"
642     "                print the current information only once.\n"));
643 }
644 
645 /*
646  *
647  * Parse command line parameters and set any options
648  * Run display output, looping over set intervals if a number is given
649  *
650  */
651 int main(int argc, char *argv[])
652 {
653     WSADATA wsaData;
654 
655     if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
656     {
657         _tprintf(_T("WSAStartup() failed : %d\n"), WSAGetLastError());
658         return -1;
659     }
660 
661     if (ParseCmdline(argc, argv))
662         return -1;
663 
664     if (bLoopOutput)
665     {
666         while (1)
667         {
668             if (DisplayOutput())
669                 return -1;
670             Sleep(Interval*1000);
671         }
672     }
673 
674     if (DisplayOutput())
675         return -1;
676     else
677         return 0;
678 }
679