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 #include <stdio.h>
17 #include <stdlib.h>
18 #include <wchar.h>
19 
20 #define WIN32_NO_STATUS
21 #include <windef.h>
22 #include <winbase.h>
23 #define _INC_WINDOWS
24 #include <winsock2.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 FALSE;
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 FALSE;
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 FALSE;
142         }
143 //        else
144 //        {
145 //            ConResPrintf(StdOut, IDS_USAGE);
146 //            return FALSE;
147 //        }
148     }
149 
150     return TRUE;
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         return ShowTcpTable();
174     }
175 
176     if (bDoShowRouteTable)
177     {
178         if (_wsystem(L"route print") == -1)
179         {
180             ConResPuts(StdErr, IDS_ERROR_ROUTE);
181             return FALSE;
182         }
183         return TRUE;
184     }
185 
186     if (bDoShowEthStats)
187     {
188         ShowEthernetStatistics();
189         return TRUE;
190     }
191 
192     if (bDoShowProtoCons)
193     {
194         switch (Protocol)
195         {
196             case IP:
197                 if (bDoShowProtoStats)
198                     ShowIpStatistics();
199                 return TRUE;
200             case ICMP:
201                 if (bDoShowProtoStats)
202                     ShowIcmpStatistics();
203                 return TRUE;
204             case TCP:
205                 if (bDoShowProtoStats)
206                     ShowTcpStatistics();
207                 ConResPuts(StdOut, IDS_ACTIVE_CONNECT);
208                 DisplayTableHeader();
209                 return ShowTcpTable();
210             case UDP:
211                 if (bDoShowProtoStats)
212                     ShowUdpStatistics();
213                 ConResPuts(StdOut, IDS_ACTIVE_CONNECT);
214                 DisplayTableHeader();
215                 return ShowUdpTable();
216             default:
217                 break;
218         }
219     }
220     else if (bDoShowProtoStats)
221     {
222         ShowIpStatistics();
223         ShowIcmpStatistics();
224         ShowTcpStatistics();
225         ShowUdpStatistics();
226         return TRUE;
227     }
228     else
229     {
230         ConResPuts(StdOut, IDS_ACTIVE_CONNECT);
231         DisplayTableHeader();
232         if (ShowTcpTable() && bDoShowAllCons)
233             ShowUdpTable();
234     }
235 
236     return TRUE;
237 }
238 
239 VOID ShowIpStatistics(VOID)
240 {
241     PMIB_IPSTATS pIpStats;
242     DWORD dwRetVal;
243 
244     pIpStats = (MIB_IPSTATS*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPSTATS));
245 
246     if ((dwRetVal = GetIpStatistics(pIpStats)) == NO_ERROR)
247     {
248         ConResPuts(StdOut, IDS_IP4_STAT_HEADER);
249         ConResPrintf(StdOut, IDS_IP_PACK_REC, pIpStats->dwInReceives);
250         ConResPrintf(StdOut, IDS_IP_HEAD_REC_ERROR, pIpStats->dwInHdrErrors);
251         ConResPrintf(StdOut, IDS_IP_ADDR_REC_ERROR, pIpStats->dwInAddrErrors);
252         ConResPrintf(StdOut, IDS_IP_DATAG_FWD, pIpStats->dwForwDatagrams);
253         ConResPrintf(StdOut, IDS_IP_UNKNOWN_PRO_REC, pIpStats->dwInUnknownProtos);
254         ConResPrintf(StdOut, IDS_IP_REC_PACK_DISCARD, pIpStats->dwInDiscards);
255         ConResPrintf(StdOut, IDS_IP_REC_PACK_DELIVER, pIpStats->dwInDelivers);
256         ConResPrintf(StdOut, IDS_IP_OUT_REQUEST, pIpStats->dwOutRequests);
257         ConResPrintf(StdOut, IDS_IP_ROUTE_DISCARD, pIpStats->dwRoutingDiscards);
258         ConResPrintf(StdOut, IDS_IP_DISCARD_OUT_PACK, pIpStats->dwOutDiscards);
259         ConResPrintf(StdOut, IDS_IP_OUT_PACKET_NO_ROUTE, pIpStats->dwOutNoRoutes);
260         ConResPrintf(StdOut, IDS_IP_REASSEMBLE_REQUIRED, pIpStats->dwReasmReqds);
261         ConResPrintf(StdOut, IDS_IP_REASSEMBLE_SUCCESS, pIpStats->dwReasmOks);
262         ConResPrintf(StdOut, IDS_IP_REASSEMBLE_FAILURE, pIpStats->dwReasmFails);
263         ConResPrintf(StdOut, IDS_IP_DATAG_FRAG_SUCCESS, pIpStats->dwFragOks);
264         ConResPrintf(StdOut, IDS_IP_DATAG_FRAG_FAILURE, pIpStats->dwFragFails);
265         ConResPrintf(StdOut, IDS_IP_DATAG_FRAG_CREATE, pIpStats->dwFragCreates);
266     }
267     else
268     {
269         DoFormatMessage(dwRetVal);
270     }
271 
272     HeapFree(GetProcessHeap(), 0, pIpStats);
273 }
274 
275 VOID ShowIcmpStatistics(VOID)
276 {
277     PMIB_ICMP pIcmpStats;
278     DWORD dwRetVal;
279 
280     pIcmpStats = (MIB_ICMP*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_ICMP));
281 
282     if ((dwRetVal = GetIcmpStatistics(pIcmpStats)) == NO_ERROR)
283     {
284         ConResPuts(StdOut, IDS_ICMP4_STAT_HEADER);
285         ConResPuts(StdOut, IDS_ICMP_THEADER);
286         ConResPrintf(StdOut, IDS_ICMP_MSG,
287             pIcmpStats->stats.icmpInStats.dwMsgs, pIcmpStats->stats.icmpOutStats.dwMsgs);
288         ConResPrintf(StdOut, IDS_ICMP_ERROR,
289             pIcmpStats->stats.icmpInStats.dwErrors, pIcmpStats->stats.icmpOutStats.dwErrors);
290         ConResPrintf(StdOut, IDS_ICMP_DEST_UNREACH,
291             pIcmpStats->stats.icmpInStats.dwDestUnreachs, pIcmpStats->stats.icmpOutStats.dwDestUnreachs);
292         ConResPrintf(StdOut, IDS_ICMP_TIME_EXCEED,
293             pIcmpStats->stats.icmpInStats.dwTimeExcds, pIcmpStats->stats.icmpOutStats.dwTimeExcds);
294         ConResPrintf(StdOut, IDS_ICMP_PARAM_PROBLEM,
295             pIcmpStats->stats.icmpInStats.dwParmProbs, pIcmpStats->stats.icmpOutStats.dwParmProbs);
296         ConResPrintf(StdOut, IDS_ICMP_SRC_QUENCHES,
297             pIcmpStats->stats.icmpInStats.dwSrcQuenchs, pIcmpStats->stats.icmpOutStats.dwSrcQuenchs);
298         ConResPrintf(StdOut, IDS_ICMP_REDIRECT,
299             pIcmpStats->stats.icmpInStats.dwRedirects, pIcmpStats->stats.icmpOutStats.dwRedirects);
300         ConResPrintf(StdOut, IDS_ICMP_ECHO,
301             pIcmpStats->stats.icmpInStats.dwEchos, pIcmpStats->stats.icmpOutStats.dwEchos);
302         ConResPrintf(StdOut, IDS_ICMP_ECHO_REPLY,
303             pIcmpStats->stats.icmpInStats.dwEchoReps, pIcmpStats->stats.icmpOutStats.dwEchoReps);
304         ConResPrintf(StdOut, IDS_ICMP_TIMESTAMP,
305             pIcmpStats->stats.icmpInStats.dwTimestamps, pIcmpStats->stats.icmpOutStats.dwTimestamps);
306         ConResPrintf(StdOut, IDS_ICMP_TIMESTAMP_REPLY,
307             pIcmpStats->stats.icmpInStats.dwTimestampReps, pIcmpStats->stats.icmpOutStats.dwTimestampReps);
308         ConResPrintf(StdOut, IDS_ICMP_ADDRESSS_MASK,
309             pIcmpStats->stats.icmpInStats.dwAddrMasks, pIcmpStats->stats.icmpOutStats.dwAddrMasks);
310         ConResPrintf(StdOut, IDS_ICMP_ADDRESSS_MASK_REPLY,
311             pIcmpStats->stats.icmpInStats.dwAddrMaskReps, pIcmpStats->stats.icmpOutStats.dwAddrMaskReps);
312     }
313     else
314     {
315         DoFormatMessage(dwRetVal);
316     }
317 
318     HeapFree(GetProcessHeap(), 0, pIcmpStats);
319 
320 }
321 
322 VOID ShowTcpStatistics(VOID)
323 {
324     MIB_TCPSTATS tcpStats;
325     DWORD dwRetVal;
326 
327     if ((dwRetVal = GetTcpStatistics(&tcpStats)) == NO_ERROR)
328     {
329         ConResPuts(StdOut, IDS_TCP4_HEADER);
330         ConResPrintf(StdOut, IDS_TCP_ACTIVE_OPEN, tcpStats.dwActiveOpens);
331         ConResPrintf(StdOut, IDS_TCP_PASS_OPEN, tcpStats.dwPassiveOpens);
332         ConResPrintf(StdOut, IDS_TCP_FAIL_CONNECT, tcpStats.dwAttemptFails);
333         ConResPrintf(StdOut, IDS_TCP_RESET_CONNECT, tcpStats.dwEstabResets);
334         ConResPrintf(StdOut, IDS_TCP_CURRENT_CONNECT, tcpStats.dwCurrEstab);
335         ConResPrintf(StdOut, IDS_TCP_SEG_RECEIVE, tcpStats.dwInSegs);
336         ConResPrintf(StdOut, IDS_TCP_SEG_SENT, tcpStats.dwOutSegs);
337         ConResPrintf(StdOut, IDS_TCP_SEG_RETRANSMIT, tcpStats.dwRetransSegs);
338     }
339     else
340     {
341         DoFormatMessage(dwRetVal);
342     }
343 }
344 
345 VOID ShowUdpStatistics(VOID)
346 {
347     MIB_UDPSTATS udpStats;
348     DWORD dwRetVal;
349 
350     if ((dwRetVal = GetUdpStatistics(&udpStats)) == NO_ERROR)
351     {
352         ConResPuts(StdOut, IDS_UDP_IP4_HEADER);
353         ConResPrintf(StdOut, IDS_UDP_DATAG_RECEIVE, udpStats.dwInDatagrams);
354         ConResPrintf(StdOut, IDS_UDP_NO_PORT, udpStats.dwNoPorts);
355         ConResPrintf(StdOut, IDS_UDP_RECEIVE_ERROR, udpStats.dwInErrors);
356         ConResPrintf(StdOut, IDS_UDP_DATAG_SEND, udpStats.dwOutDatagrams);
357     }
358     else
359     {
360         DoFormatMessage(dwRetVal);
361     }
362 }
363 
364 VOID ShowEthernetStatistics(VOID)
365 {
366     PMIB_IFTABLE pIfTable;
367     DWORD dwSize = 0;
368     DWORD dwRetVal = 0;
369 
370     pIfTable = (MIB_IFTABLE*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IFTABLE));
371 
372     if (GetIfTable(pIfTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER)
373     {
374         HeapFree(GetProcessHeap(), 0, pIfTable);
375         pIfTable = (MIB_IFTABLE*) HeapAlloc(GetProcessHeap(), 0, dwSize);
376 
377         if ((dwRetVal = GetIfTable(pIfTable, &dwSize, 0)) == NO_ERROR)
378         {
379             ConResPuts(StdOut, IDS_ETHERNET_INTERFACE_STAT);
380             ConResPuts(StdOut, IDS_ETHERNET_THEADER);
381             ConResPrintf(StdOut, IDS_ETHERNET_BYTES,
382                 pIfTable->table[0].dwInOctets, pIfTable->table[0].dwOutOctets);
383             ConResPrintf(StdOut, IDS_ETHERNET_UNICAST_PACKET,
384                 pIfTable->table[0].dwInUcastPkts, pIfTable->table[0].dwOutUcastPkts);
385             ConResPrintf(StdOut, IDS_ETHERNET_NON_UNICAST_PACKET,
386                 pIfTable->table[0].dwInNUcastPkts, pIfTable->table[0].dwOutNUcastPkts);
387             ConResPrintf(StdOut, IDS_ETHERNET_DISCARD,
388                 pIfTable->table[0].dwInDiscards, pIfTable->table[0].dwOutDiscards);
389             ConResPrintf(StdOut, IDS_ETHERNET_ERROR,
390                 pIfTable->table[0].dwInErrors, pIfTable->table[0].dwOutErrors);
391             ConResPrintf(StdOut, IDS_ETHERNET_UNKNOWN,
392                 pIfTable->table[0].dwInUnknownProtos);
393         }
394         else
395         {
396             DoFormatMessage(dwRetVal);
397         }
398     }
399     HeapFree(GetProcessHeap(), 0, pIfTable);
400 }
401 
402 BOOL ShowTcpTable(VOID)
403 {
404     PMIB_TCPTABLE_OWNER_PID tcpTable;
405     DWORD error, dwSize;
406     DWORD i;
407     CHAR HostIp[HOSTNAMELEN], HostPort[PORTNAMELEN];
408     CHAR RemoteIp[HOSTNAMELEN], RemotePort[PORTNAMELEN];
409     CHAR Host[ADDRESSLEN];
410     CHAR Remote[ADDRESSLEN];
411     CHAR PID[64];
412 
413     /* Get the table of TCP endpoints */
414     dwSize = sizeof (MIB_TCPTABLE_OWNER_PID);
415     /* Should also work when we get new connections between 2 GetTcpTable()
416      * calls: */
417     do
418     {
419         tcpTable = (PMIB_TCPTABLE_OWNER_PID) HeapAlloc(GetProcessHeap(), 0, dwSize);
420         error = GetExtendedTcpTable(tcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
421         if ( error != NO_ERROR )
422             HeapFree(GetProcessHeap(), 0, tcpTable);
423     }
424     while  ( error == ERROR_INSUFFICIENT_BUFFER );
425 
426     if (error != NO_ERROR)
427     {
428         ConResPrintf(StdErr, IDS_ERROR_TCP_SNAPSHOT);
429         DoFormatMessage(error);
430         HeapFree(GetProcessHeap(), 0, tcpTable);
431         return FALSE;
432     }
433 
434     /* Dump the TCP table */
435     for (i = 0; i < tcpTable->dwNumEntries; i++)
436     {
437         /* If we aren't showing all connections, only display established, close wait
438          * and time wait. This is the default output for netstat */
439         if (bDoShowAllCons || (tcpTable->table[i].dwState ==  MIB_TCP_STATE_ESTAB)
440             || (tcpTable->table[i].dwState ==  MIB_TCP_STATE_CLOSE_WAIT)
441             || (tcpTable->table[i].dwState ==  MIB_TCP_STATE_TIME_WAIT))
442         {
443             /* I've split this up so it's easier to follow */
444             GetIpHostName(TRUE, tcpTable->table[i].dwLocalAddr, HostIp, sizeof(HostIp));
445             GetPortName(tcpTable->table[i].dwLocalPort, "tcp", HostPort, sizeof(HostPort));
446             sprintf(Host, "%s:%s", HostIp, HostPort);
447 
448             if (tcpTable->table[i].dwState ==  MIB_TCP_STATE_LISTEN)
449             {
450                 sprintf(Remote, "%s:0", HostIp);
451             }
452             else
453             {
454                 GetIpHostName(FALSE, tcpTable->table[i].dwRemoteAddr, RemoteIp, sizeof(RemoteIp));
455                 GetPortName(tcpTable->table[i].dwRemotePort, "tcp", RemotePort, sizeof(RemotePort));
456                 sprintf(Remote, "%s:%s", RemoteIp, RemotePort);
457             }
458 
459             if (bDoShowProcessId)
460             {
461                 sprintf(PID, "%ld", tcpTable->table[i].dwOwningPid);
462             }
463             else
464             {
465                 PID[0] = 0;
466             }
467 
468             ConPrintf(StdOut, L"  %-6s %-22S %-22S %-11s %S\n", L"TCP",
469                       Host, Remote, TcpState[tcpTable->table[i].dwState], PID);
470         }
471     }
472 
473     HeapFree(GetProcessHeap(), 0, tcpTable);
474     return TRUE;
475 }
476 
477 BOOL ShowUdpTable(VOID)
478 {
479     PMIB_UDPTABLE_OWNER_PID udpTable;
480     DWORD error, dwSize;
481     DWORD i;
482     CHAR HostIp[HOSTNAMELEN], HostPort[PORTNAMELEN];
483     CHAR Host[ADDRESSLEN];
484     CHAR PID[64];
485 
486     /* Get the table of UDP endpoints */
487     dwSize = 0;
488     error = GetExtendedUdpTable(NULL, &dwSize, TRUE, AF_INET, UDP_TABLE_OWNER_PID, 0);
489     if (error != ERROR_INSUFFICIENT_BUFFER)
490     {
491         ConResPuts(StdErr, IDS_ERROR_UDP_ENDPOINT);
492         DoFormatMessage(error);
493         return FALSE;
494     }
495     udpTable = (PMIB_UDPTABLE_OWNER_PID) HeapAlloc(GetProcessHeap(), 0, dwSize);
496     error = GetExtendedUdpTable(udpTable, &dwSize, TRUE, AF_INET, UDP_TABLE_OWNER_PID, 0);
497     if (error)
498     {
499         ConResPuts(StdErr, IDS_ERROR_UDP_ENDPOINT_TABLE);
500         DoFormatMessage(error);
501         HeapFree(GetProcessHeap(), 0, udpTable);
502         return FALSE;
503     }
504 
505     /* Dump the UDP table */
506     for (i = 0; i < udpTable->dwNumEntries; i++)
507     {
508 
509         /* I've split this up so it's easier to follow */
510         GetIpHostName(TRUE, udpTable->table[i].dwLocalAddr, HostIp, sizeof(HostIp));
511         GetPortName(udpTable->table[i].dwLocalPort, "udp", HostPort, sizeof(HostPort));
512 
513         sprintf(Host, "%s:%s", HostIp, HostPort);
514 
515         if (bDoShowProcessId)
516         {
517             sprintf(PID, "%ld", udpTable->table[i].dwOwningPid);
518         }
519         else
520         {
521             PID[0] = 0;
522         }
523 
524         ConPrintf(StdOut, L"  %-6s %-22S %-34s %S\n", L"UDP", Host, L"*:*", PID);
525     }
526 
527     HeapFree(GetProcessHeap(), 0, udpTable);
528     return TRUE;
529 }
530 
531 /*
532  * Translate port numbers into their text equivalent if there is one
533  */
534 PCHAR
535 GetPortName(UINT Port, PCSTR Proto, CHAR Name[], INT NameLen)
536 {
537     struct servent *pServent;
538 
539     if (bDoShowNumbers)
540     {
541         sprintf(Name, "%d", htons((WORD)Port));
542         return Name;
543     }
544     /* Try to translate to a name */
545     if ((pServent = getservbyport(Port, Proto)))
546         strcpy(Name, pServent->s_name );
547     else
548         sprintf(Name, "%d", htons((WORD)Port));
549     return Name;
550 }
551 
552 /*
553  * convert addresses into dotted decimal or hostname
554  */
555 PCHAR
556 GetIpHostName(BOOL Local, UINT IpAddr, CHAR Name[], INT NameLen)
557 {
558 //  struct hostent *phostent;
559     UINT nIpAddr;
560 
561     /* display dotted decimal */
562     nIpAddr = htonl(IpAddr);
563     if (bDoShowNumbers) {
564         sprintf(Name, "%d.%d.%d.%d",
565             (nIpAddr >> 24) & 0xFF,
566             (nIpAddr >> 16) & 0xFF,
567             (nIpAddr >> 8) & 0xFF,
568             (nIpAddr) & 0xFF);
569         return Name;
570     }
571 
572     Name[0] = '\0';
573 
574     /* try to resolve the name */
575     if (!IpAddr) {
576         if (!Local) {
577             sprintf(Name, "%d.%d.%d.%d",
578                 (nIpAddr >> 24) & 0xFF,
579                 (nIpAddr >> 16) & 0xFF,
580                 (nIpAddr >> 8) & 0xFF,
581                 (nIpAddr) & 0xFF);
582         } else {
583             if (gethostname(Name, NameLen) != 0)
584                 DoFormatMessage(WSAGetLastError());
585         }
586     } else if (IpAddr == 0x0100007f) {
587         if (Local) {
588             if (gethostname(Name, NameLen) != 0)
589                 DoFormatMessage(WSAGetLastError());
590         } else {
591             strncpy(Name, "localhost", 10);
592         }
593 //  } else if (phostent = gethostbyaddr((char*)&ipaddr, sizeof(nipaddr), PF_INET)) {
594 //      strcpy(name, phostent->h_name);
595     } else {
596         sprintf(Name, "%d.%d.%d.%d",
597             ((nIpAddr >> 24) & 0x000000FF),
598             ((nIpAddr >> 16) & 0x000000FF),
599             ((nIpAddr >> 8) & 0x000000FF),
600             ((nIpAddr) & 0x000000FF));
601     }
602     return Name;
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 wmain(int argc, wchar_t *argv[])
612 {
613     BOOL Success;
614     WSADATA wsaData;
615 
616     /* Initialize the Console Standard Streams */
617     ConInitStdStreams();
618 
619     if (!ParseCmdline(argc, argv))
620         return EXIT_FAILURE;
621 
622     if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
623     {
624         ConResPrintf(StdErr, IDS_ERROR_WSA_START, WSAGetLastError());
625         return EXIT_FAILURE;
626     }
627 
628     Success = DisplayOutput();
629     while (bLoopOutput && Success)
630     {
631         Sleep(Interval*1000);
632         Success = DisplayOutput();
633     }
634 
635     WSACleanup();
636     return (Success ? EXIT_SUCCESS : EXIT_FAILURE);
637 }
638