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 <wchar.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <iphlpapi.h>
26 
27 #include <conutils.h>
28 
29 #include "netstat.h"
30 #include "resource.h"
31 
32 enum ProtoType {IP, TCP, UDP, ICMP} Protocol;
33 DWORD Interval; /* time to pause between printing output */
34 
35 /* TCP endpoint states */
36 PCWSTR TcpState[] = {
37     L"???",
38     L"CLOSED",
39     L"LISTENING",
40     L"SYN_SENT",
41     L"SYN_RCVD",
42     L"ESTABLISHED",
43     L"FIN_WAIT1",
44     L"FIN_WAIT2",
45     L"CLOSE_WAIT",
46     L"CLOSING",
47     L"LAST_ACK",
48     L"TIME_WAIT",
49     L"DELETE_TCB"
50 };
51 
52 /*
53  * format message string and display output
54  */
55 VOID DoFormatMessage(DWORD ErrorCode)
56 {
57     if (ErrorCode == ERROR_SUCCESS)
58         return;
59 
60     ConMsgPuts(StdErr, FORMAT_MESSAGE_FROM_SYSTEM,
61                NULL, ErrorCode, LANG_USER_DEFAULT);
62 }
63 
64 /*
65  *
66  * Parse command line parameters and set any options
67  *
68  */
69 BOOL ParseCmdline(int argc, wchar_t* argv[])
70 {
71     LPWSTR Proto;
72     WCHAR c;
73     INT i;
74 
75     if ((argc == 1) || (iswdigit(*argv[1])))
76         bNoOptions = TRUE;
77 
78     /* Parse command line for options we have been given. */
79     for (i = 1; i < argc; i++)
80     {
81         if ((argc > 1) && (argv[i][0] == L'-' || argv[i][0] == L'/'))
82         {
83             while ((c = *++argv[i]) != L'\0')
84             {
85                 switch (towlower(c))
86                 {
87                     case L'a':
88                         bDoShowAllCons = TRUE;
89                         break;
90                     case L'b':
91                         bDoShowProcName = TRUE;
92                         break;
93                     case L'e':
94                         bDoShowEthStats = TRUE;
95                         break;
96                     case L'n':
97                         bDoShowNumbers = TRUE;
98                         break;
99                     case L'p':
100                         bDoShowProtoCons = TRUE;
101                         Proto = argv[i+1];
102                         if (!_wcsicmp(L"IP", Proto))
103                             Protocol = IP;
104                         else if (!_wcsicmp(L"ICMP", Proto))
105                             Protocol = ICMP;
106                         else if (!_wcsicmp(L"TCP", Proto))
107                             Protocol = TCP;
108                         else if (!_wcsicmp(L"UDP", Proto))
109                             Protocol = UDP;
110                         else
111                         {
112                             ConResPuts(StdOut, IDS_USAGE);
113                             return EXIT_FAILURE;
114                         }
115                         break;
116                     case L'r':
117                         bDoShowRouteTable = TRUE;
118                         break;
119                     case L's':
120                         bDoShowProtoStats = TRUE;
121                         break;
122                     case L'o':
123                         bDoShowProcessId = TRUE;
124                         break;
125                     case L'v':
126                         // FIXME!
127                         ConPuts(StdOut, L"got v\n");
128                         bDoDispSeqComp = TRUE;
129                         break;
130                     default :
131                         ConResPuts(StdOut, IDS_USAGE);
132                         return EXIT_FAILURE;
133                 }
134             }
135         }
136         else if (iswdigit(*argv[i]) != 0)
137         {
138             if (swscanf(argv[i], L"%lu", &Interval) != EOF)
139                 bLoopOutput = TRUE;
140             else
141                 return EXIT_FAILURE;
142         }
143 //        else
144 //        {
145 //            ConResPrintf(StdOut, IDS_USAGE);
146 //            return EXIT_FAILURE;
147 //        }
148     }
149 
150     return EXIT_SUCCESS;
151 }
152 
153 /*
154  * Display table header
155  */
156 VOID DisplayTableHeader(VOID)
157 {
158     ConResPuts(StdOut, IDS_DISPLAY_THEADER);
159     if (bDoShowProcessId)
160         ConResPuts(StdOut, IDS_DISPLAY_PROCESS);
161     else
162         ConPuts(StdOut, L"\n");
163 }
164 
165 /*
166  * Simulate Microsofts netstat utility output
167  */
168 BOOL DisplayOutput(VOID)
169 {
170     if (bNoOptions)
171     {
172         DisplayTableHeader();
173         ShowTcpTable();
174         return EXIT_SUCCESS;
175     }
176 
177     if (bDoShowRouteTable)
178     {
179         if (_wsystem(L"route print") == -1)
180         {
181             ConResPuts(StdErr, IDS_ERROR_ROUTE);
182             return EXIT_FAILURE;
183         }
184         return EXIT_SUCCESS;
185     }
186 
187     if (bDoShowEthStats)
188     {
189         ShowEthernetStatistics();
190         return EXIT_SUCCESS;
191     }
192 
193     if (bDoShowProtoCons)
194     {
195         switch (Protocol)
196         {
197             case IP:
198                 if (bDoShowProtoStats)
199                 {
200                     ShowIpStatistics();
201                     return EXIT_SUCCESS;
202                 }
203                 break;
204             case ICMP:
205                 if (bDoShowProtoStats)
206                 {
207                     ShowIcmpStatistics();
208                     return EXIT_SUCCESS;
209                 }
210                 break;
211             case TCP:
212                 if (bDoShowProtoStats)
213                     ShowTcpStatistics();
214                 ConResPuts(StdOut, IDS_ACTIVE_CONNECT);
215                 DisplayTableHeader();
216                 ShowTcpTable();
217                 break;
218             case UDP:
219                 if (bDoShowProtoStats)
220                     ShowUdpStatistics();
221                 ConResPuts(StdOut, IDS_ACTIVE_CONNECT);
222                 DisplayTableHeader();
223                 ShowUdpTable();
224                 break;
225             default:
226                 break;
227         }
228     }
229     else if (bDoShowProtoStats)
230     {
231         ShowIpStatistics();
232         ShowIcmpStatistics();
233         ShowTcpStatistics();
234         ShowUdpStatistics();
235         return EXIT_SUCCESS;
236     }
237     else
238     {
239         ConResPuts(StdOut, IDS_ACTIVE_CONNECT);
240         DisplayTableHeader();
241         ShowTcpTable();
242         if (bDoShowAllCons)
243             ShowUdpTable();
244     }
245     return EXIT_SUCCESS;
246 }
247 
248 VOID ShowIpStatistics(VOID)
249 {
250     PMIB_IPSTATS pIpStats;
251     DWORD dwRetVal;
252 
253     pIpStats = (MIB_IPSTATS*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPSTATS));
254 
255     if ((dwRetVal = GetIpStatistics(pIpStats)) == NO_ERROR)
256     {
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);
275     }
276     else
277     {
278         DoFormatMessage(dwRetVal);
279     }
280 
281     HeapFree(GetProcessHeap(), 0, pIpStats);
282 }
283 
284 VOID ShowIcmpStatistics(VOID)
285 {
286     PMIB_ICMP pIcmpStats;
287     DWORD dwRetVal;
288 
289     pIcmpStats = (MIB_ICMP*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_ICMP));
290 
291     if ((dwRetVal = GetIcmpStatistics(pIcmpStats)) == NO_ERROR)
292     {
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);
321     }
322     else
323     {
324         DoFormatMessage(dwRetVal);
325     }
326 
327     HeapFree(GetProcessHeap(), 0, pIcmpStats);
328 
329 }
330 
331 VOID ShowTcpStatistics(VOID)
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         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);
349     }
350     else
351     {
352         DoFormatMessage(dwRetVal);
353     }
354 
355     HeapFree(GetProcessHeap(), 0, pTcpStats);
356 }
357 
358 VOID ShowUdpStatistics(VOID)
359 {
360     PMIB_UDPSTATS pUdpStats;
361     DWORD dwRetVal;
362 
363     pUdpStats = (MIB_UDPSTATS*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_UDPSTATS));
364 
365     if ((dwRetVal = GetUdpStatistics(pUdpStats)) == NO_ERROR)
366     {
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);
372     }
373     else
374     {
375         DoFormatMessage(dwRetVal);
376     }
377 
378     HeapFree(GetProcessHeap(), 0, pUdpStats);
379 }
380 
381 VOID ShowEthernetStatistics(VOID)
382 {
383     PMIB_IFTABLE pIfTable;
384     DWORD dwSize = 0;
385     DWORD dwRetVal = 0;
386 
387     pIfTable = (MIB_IFTABLE*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IFTABLE));
388 
389     if (GetIfTable(pIfTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER)
390     {
391         HeapFree(GetProcessHeap(), 0, pIfTable);
392         pIfTable = (MIB_IFTABLE*) HeapAlloc(GetProcessHeap(), 0, dwSize);
393 
394         if ((dwRetVal = GetIfTable(pIfTable, &dwSize, 0)) == NO_ERROR)
395         {
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);
410         }
411         else
412         {
413             DoFormatMessage(dwRetVal);
414         }
415     }
416     HeapFree(GetProcessHeap(), 0, pIfTable);
417 }
418 
419 VOID ShowTcpTable(VOID)
420 {
421     PMIB_TCPTABLE_OWNER_PID tcpTable;
422     DWORD error, dwSize;
423     DWORD i;
424     CHAR HostIp[HOSTNAMELEN], HostPort[PORTNAMELEN];
425     CHAR RemoteIp[HOSTNAMELEN], RemotePort[PORTNAMELEN];
426     CHAR Host[ADDRESSLEN];
427     CHAR Remote[ADDRESSLEN];
428     CHAR PID[64];
429 
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()
433      * calls: */
434     do
435     {
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);
440     }
441     while  ( error == ERROR_INSUFFICIENT_BUFFER );
442 
443     if (error != NO_ERROR)
444     {
445         ConResPrintf(StdErr, IDS_ERROR_TCP_SNAPSHOT);
446         DoFormatMessage(error);
447         exit(EXIT_FAILURE);
448     }
449 
450     /* Dump the TCP table */
451     for (i = 0; i < tcpTable->dwNumEntries; i++)
452     {
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))
458         {
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);
463 
464             if (tcpTable->table[i].dwState ==  MIB_TCP_STATE_LISTEN)
465             {
466                 sprintf(Remote, "%s:0", HostIp);
467             }
468             else
469             {
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);
473             }
474 
475             if (bDoShowProcessId)
476             {
477                 sprintf(PID, "%ld", tcpTable->table[i].dwOwningPid);
478             }
479             else
480             {
481                 PID[0] = 0;
482             }
483 
484             ConPrintf(StdOut, L"  %-6s %-22S %-22S %-11s %S\n", L"TCP",
485                       Host, Remote, TcpState[tcpTable->table[i].dwState], PID);
486         }
487     }
488     HeapFree(GetProcessHeap(), 0, tcpTable);
489 }
490 
491 VOID ShowUdpTable(VOID)
492 {
493     PMIB_UDPTABLE_OWNER_PID udpTable;
494     DWORD error, dwSize;
495     DWORD i;
496     CHAR HostIp[HOSTNAMELEN], HostPort[PORTNAMELEN];
497     CHAR Host[ADDRESSLEN];
498     CHAR PID[64];
499 
500     /* Get the table of UDP endpoints */
501     dwSize = 0;
502     error = GetExtendedUdpTable(NULL, &dwSize, TRUE, AF_INET, UDP_TABLE_OWNER_PID, 0);
503     if (error != ERROR_INSUFFICIENT_BUFFER)
504     {
505         ConResPuts(StdErr, IDS_ERROR_UDP_ENDPOINT);
506         DoFormatMessage(error);
507         exit(EXIT_FAILURE);
508     }
509     udpTable = (PMIB_UDPTABLE_OWNER_PID) HeapAlloc(GetProcessHeap(), 0, dwSize);
510     error = GetExtendedUdpTable(udpTable, &dwSize, TRUE, AF_INET, UDP_TABLE_OWNER_PID, 0);
511     if (error)
512     {
513         ConResPuts(StdErr, IDS_ERROR_UDP_ENDPOINT_TABLE);
514         DoFormatMessage(error);
515         HeapFree(GetProcessHeap(), 0, udpTable);
516         exit(EXIT_FAILURE);
517     }
518 
519     /* Dump the UDP table */
520     for (i = 0; i < udpTable->dwNumEntries; i++)
521     {
522 
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));
526 
527         sprintf(Host, "%s:%s", HostIp, HostPort);
528 
529         if (bDoShowProcessId)
530         {
531             sprintf(PID, "%ld", udpTable->table[i].dwOwningPid);
532         }
533         else
534         {
535             PID[0] = 0;
536         }
537 
538         ConPrintf(StdOut, L"  %-6s %-22S %-34s %S\n", L"UDP", Host, L"*:*", PID);
539     }
540 
541     HeapFree(GetProcessHeap(), 0, udpTable);
542 }
543 
544 /*
545  * Translate port numbers into their text equivalent if there is one
546  */
547 PCHAR
548 GetPortName(UINT Port, PCSTR Proto, CHAR Name[], INT NameLen)
549 {
550     struct servent *pServent;
551 
552     if (bDoShowNumbers)
553     {
554         sprintf(Name, "%d", htons((WORD)Port));
555         return Name;
556     }
557     /* Try to translate to a name */
558     if ((pServent = getservbyport(Port, Proto)))
559         strcpy(Name, pServent->s_name );
560     else
561         sprintf(Name, "%d", htons((WORD)Port));
562     return Name;
563 }
564 
565 /*
566  * convert addresses into dotted decimal or hostname
567  */
568 PCHAR
569 GetIpHostName(BOOL Local, UINT IpAddr, CHAR Name[], INT NameLen)
570 {
571 //  struct hostent *phostent;
572     UINT nIpAddr;
573 
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,
581             (nIpAddr) & 0xFF);
582         return Name;
583     }
584 
585     Name[0] = '\0';
586 
587     /* try to resolve the name */
588     if (!IpAddr) {
589         if (!Local) {
590             sprintf(Name, "%d.%d.%d.%d",
591                 (nIpAddr >> 24) & 0xFF,
592                 (nIpAddr >> 16) & 0xFF,
593                 (nIpAddr >> 8) & 0xFF,
594                 (nIpAddr) & 0xFF);
595         } else {
596             if (gethostname(Name, NameLen) != 0)
597                 DoFormatMessage(WSAGetLastError());
598         }
599     } else if (IpAddr == 0x0100007f) {
600         if (Local) {
601             if (gethostname(Name, NameLen) != 0)
602                 DoFormatMessage(WSAGetLastError());
603         } else {
604             strncpy(Name, "localhost", 10);
605         }
606 //  } else if (phostent = gethostbyaddr((char*)&ipaddr, sizeof(nipaddr), PF_INET)) {
607 //      strcpy(name, phostent->h_name);
608     } else {
609         sprintf(Name, "%d.%d.%d.%d",
610             ((nIpAddr >> 24) & 0x000000FF),
611             ((nIpAddr >> 16) & 0x000000FF),
612             ((nIpAddr >> 8) & 0x000000FF),
613             ((nIpAddr) & 0x000000FF));
614     }
615     return Name;
616 }
617 
618 /*
619  *
620  * Parse command line parameters and set any options
621  * Run display output, looping over set intervals if a number is given
622  *
623  */
624 int wmain(int argc, wchar_t *argv[])
625 {
626     WSADATA wsaData;
627 
628     /* Initialize the Console Standard Streams */
629     ConInitStdStreams();
630 
631     if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
632     {
633         ConResPrintf(StdErr, IDS_ERROR_WSA_START, WSAGetLastError());
634         return -1;
635     }
636 
637     if (ParseCmdline(argc, argv))
638         return -1;
639 
640     if (bLoopOutput)
641     {
642         while (1)
643         {
644             if (DisplayOutput())
645                 return -1;
646             Sleep(Interval*1000);
647         }
648     }
649 
650     if (DisplayOutput())
651         return -1;
652     else
653         return 0;
654 }
655