1c2c66affSColin Finck /*
2c2c66affSColin Finck  * COPYRIGHT:   See COPYING in the top level directory
3c2c66affSColin Finck  * PROJECT:     ReactOS ping utility
4c2c66affSColin Finck  * FILE:        applications/cmdutils/arping/arping.c
5c2c66affSColin Finck  * PURPOSE:     Network test utility
6c2c66affSColin Finck  * PROGRAMMERS: Pierre Schweitzer <pierre@reactos.org>
7c2c66affSColin Finck  */
8c2c66affSColin Finck 
9c2c66affSColin Finck #define WIN32_NO_STATUS
10c2c66affSColin Finck #include <stdarg.h>
11c2c66affSColin Finck #include <windef.h>
12c2c66affSColin Finck #include <winbase.h>
13c2c66affSColin Finck #include <winuser.h>
14c2c66affSColin Finck #include <winnls.h>
15c2c66affSColin Finck #include <wincon.h>
16c2c66affSColin Finck #define _INC_WINDOWS
17c2c66affSColin Finck #include <ws2tcpip.h>
18c2c66affSColin Finck #include <iphlpapi.h>
19c2c66affSColin Finck #include <ws2def.h>
20c2c66affSColin Finck #include <stdio.h>
21c2c66affSColin Finck #include <stdlib.h>
22c2c66affSColin Finck 
23c2c66affSColin Finck #include "resource.h"
24c2c66affSColin Finck 
25c2c66affSColin Finck BOOL                NeverStop;
26c2c66affSColin Finck UINT                PingCount;
27c2c66affSColin Finck WCHAR               TargetName[256];
28c2c66affSColin Finck WCHAR               SourceName[256];
29c2c66affSColin Finck DWORD               SourceAddr;
30c2c66affSColin Finck DWORD               TargetAddr;
31c2c66affSColin Finck WCHAR               TargetIP[16];
32c2c66affSColin Finck WCHAR               SourceIP[16];
33c2c66affSColin Finck SOCKADDR_IN         Target;
34c2c66affSColin Finck HANDLE              hStdOutput;
35c2c66affSColin Finck ULONG               Timeout;
36c2c66affSColin Finck LARGE_INTEGER       TicksPerMs;
37c2c66affSColin Finck LARGE_INTEGER       TicksPerUs;
38c2c66affSColin Finck BOOL                UsePerformanceCounter;
39c2c66affSColin Finck UINT                Sent;
40c2c66affSColin Finck UINT                Received;
41c2c66affSColin Finck 
FormatOutput(UINT uID,...)42c2c66affSColin Finck void FormatOutput(UINT uID, ...)
43c2c66affSColin Finck {
44c2c66affSColin Finck     va_list valist;
45c2c66affSColin Finck 
46c2c66affSColin Finck     WCHAR Buf[1024];
47c2c66affSColin Finck     CHAR AnsiBuf[1024];
48c2c66affSColin Finck     LPWSTR pBuf = Buf;
49c2c66affSColin Finck     PCHAR pAnsiBuf = AnsiBuf;
50c2c66affSColin Finck     WCHAR Format[1024];
51c2c66affSColin Finck     DWORD written;
52c2c66affSColin Finck     UINT DataLength;
53c2c66affSColin Finck     int AnsiLength;
54c2c66affSColin Finck 
55c2c66affSColin Finck     if (!LoadString(GetModuleHandle(NULL), uID,
56c2c66affSColin Finck                     Format, sizeof(Format) / sizeof(WCHAR)))
57c2c66affSColin Finck     {
58c2c66affSColin Finck         return;
59c2c66affSColin Finck     }
60c2c66affSColin Finck 
61c2c66affSColin Finck     va_start(valist, uID);
62c2c66affSColin Finck     DataLength = FormatMessage(FORMAT_MESSAGE_FROM_STRING, Format, 0, 0, Buf,\
63c2c66affSColin Finck                                sizeof(Buf) / sizeof(WCHAR), &valist);
64*cd4db318SSerge Gautherie     va_end(valist);
65c2c66affSColin Finck 
66c2c66affSColin Finck     if(!DataLength)
67c2c66affSColin Finck     {
68c2c66affSColin Finck         if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
69c2c66affSColin Finck         {
70c2c66affSColin Finck             return;
71c2c66affSColin Finck         }
72c2c66affSColin Finck 
73*cd4db318SSerge Gautherie         va_start(valist, uID);
74c2c66affSColin Finck         DataLength = FormatMessage(FORMAT_MESSAGE_FROM_STRING |\
75c2c66affSColin Finck                                     FORMAT_MESSAGE_ALLOCATE_BUFFER,\
76c2c66affSColin Finck                                    Format, 0, 0, (LPWSTR)&pBuf, 0, &valist);
77*cd4db318SSerge Gautherie         va_end(valist);
78c2c66affSColin Finck     }
79c2c66affSColin Finck 
80c2c66affSColin Finck     if(!DataLength)
81c2c66affSColin Finck     {
82c2c66affSColin Finck         return;
83c2c66affSColin Finck     }
84c2c66affSColin Finck 
85c2c66affSColin Finck     if(GetFileType(hStdOutput) == FILE_TYPE_CHAR)
86c2c66affSColin Finck     {
87c2c66affSColin Finck         /* Is a console or a printer */
88c2c66affSColin Finck         WriteConsole(hStdOutput, pBuf, DataLength, &written, NULL);
89c2c66affSColin Finck     }
90c2c66affSColin Finck     else
91c2c66affSColin Finck     {
92c2c66affSColin Finck         /* Is a pipe, socket, file or other */
93c2c66affSColin Finck         AnsiLength = WideCharToMultiByte(CP_ACP, 0, pBuf, DataLength,\
94c2c66affSColin Finck                                          NULL, 0, NULL, NULL);
95c2c66affSColin Finck 
96c2c66affSColin Finck         if(AnsiLength >= sizeof(AnsiBuf))
97c2c66affSColin Finck             pAnsiBuf = (PCHAR)HeapAlloc(GetProcessHeap(), 0, AnsiLength);
98c2c66affSColin Finck 
99c2c66affSColin Finck         AnsiLength = WideCharToMultiByte(CP_OEMCP, 0, pBuf, DataLength,\
100c2c66affSColin Finck                                          pAnsiBuf, AnsiLength, " ", NULL);
101c2c66affSColin Finck 
102c2c66affSColin Finck         WriteFile(hStdOutput, pAnsiBuf, AnsiLength, &written, NULL);
103c2c66affSColin Finck 
104c2c66affSColin Finck         if(pAnsiBuf != AnsiBuf)
105c2c66affSColin Finck             HeapFree(NULL, 0, pAnsiBuf);
106c2c66affSColin Finck     }
107c2c66affSColin Finck 
108c2c66affSColin Finck     if(pBuf != Buf)
109c2c66affSColin Finck         LocalFree(pBuf);
110c2c66affSColin Finck }
111c2c66affSColin Finck 
Usage(VOID)112c2c66affSColin Finck static VOID Usage(VOID)
113c2c66affSColin Finck {
114c2c66affSColin Finck     FormatOutput(IDS_USAGE);
115c2c66affSColin Finck }
116c2c66affSColin Finck 
ParseCmdline(int argc,LPWSTR argv[])117c2c66affSColin Finck static BOOL ParseCmdline(int argc, LPWSTR argv[])
118c2c66affSColin Finck {
119c2c66affSColin Finck     INT i;
120c2c66affSColin Finck 
121c2c66affSColin Finck     if (argc < 3)
122c2c66affSColin Finck     {
123c2c66affSColin Finck         Usage();
124c2c66affSColin Finck         return FALSE;
125c2c66affSColin Finck     }
126c2c66affSColin Finck 
127c2c66affSColin Finck     for (i = 1; i < argc; i++)
128c2c66affSColin Finck     {
129c2c66affSColin Finck         if (argv[i][0] == L'-' || argv[i][0] == L'/')
130c2c66affSColin Finck         {
131c2c66affSColin Finck             switch (argv[i][1])
132c2c66affSColin Finck             {
133c2c66affSColin Finck                 case L't': NeverStop = TRUE; break;
134c2c66affSColin Finck                 case L'n':
135c2c66affSColin Finck                     if (i + 1 < argc)
136c2c66affSColin Finck                     {
137c2c66affSColin Finck                         PingCount = wcstoul(argv[++i], NULL, 0);
138c2c66affSColin Finck 
139c2c66affSColin Finck                         if (PingCount == 0)
140c2c66affSColin Finck                         {
141c2c66affSColin Finck                             FormatOutput(IDS_BAD_VALUE_OPTION_N, UINT_MAX);
142c2c66affSColin Finck                             return FALSE;
143c2c66affSColin Finck                         }
144c2c66affSColin Finck                     }
145c2c66affSColin Finck                     else
146c2c66affSColin Finck                     {
147c2c66affSColin Finck                         FormatOutput(IDS_BAD_OPTION_FORMAT, argv[i]);
148c2c66affSColin Finck                         return FALSE;
149c2c66affSColin Finck                     }
150c2c66affSColin Finck                     break;
151c2c66affSColin Finck 
152c2c66affSColin Finck                 case L's':
153c2c66affSColin Finck                     if (SourceName[0] != 0)
154c2c66affSColin Finck                     {
155c2c66affSColin Finck                         FormatOutput(IDS_BAD_PARAMETER, argv[i]);
156c2c66affSColin Finck                         return FALSE;
157c2c66affSColin Finck                     }
158c2c66affSColin Finck 
159c2c66affSColin Finck                     if (i + 1 < argc)
160c2c66affSColin Finck                     {
161c2c66affSColin Finck                         wcscpy(SourceName, argv[++i]);
162c2c66affSColin Finck                     }
163c2c66affSColin Finck                     else
164c2c66affSColin Finck                     {
165c2c66affSColin Finck                         FormatOutput(IDS_BAD_OPTION_FORMAT, argv[i]);
166c2c66affSColin Finck                         return FALSE;
167c2c66affSColin Finck                     }
168c2c66affSColin Finck                     break;
169c2c66affSColin Finck 
170c2c66affSColin Finck                 case '?':
171c2c66affSColin Finck                     Usage();
172c2c66affSColin Finck                     return FALSE;
173c2c66affSColin Finck 
174c2c66affSColin Finck                 default:
175c2c66affSColin Finck                     FormatOutput(IDS_BAD_OPTION, argv[i]);
176c2c66affSColin Finck                     return FALSE;
177c2c66affSColin Finck             }
178c2c66affSColin Finck         }
179c2c66affSColin Finck         else
180c2c66affSColin Finck         {
181c2c66affSColin Finck             if (TargetName[0] != 0)
182c2c66affSColin Finck             {
183c2c66affSColin Finck                 FormatOutput(IDS_BAD_PARAMETER, argv[i]);
184c2c66affSColin Finck                 return FALSE;
185c2c66affSColin Finck             }
186c2c66affSColin Finck             else
187c2c66affSColin Finck             {
188c2c66affSColin Finck                 wcscpy(TargetName, argv[i]);
189c2c66affSColin Finck             }
190c2c66affSColin Finck         }
191c2c66affSColin Finck     }
192c2c66affSColin Finck 
193c2c66affSColin Finck     if (TargetName[0] == 0)
194c2c66affSColin Finck     {
195c2c66affSColin Finck         FormatOutput(IDS_DEST_MUST_BE_SPECIFIED);
196c2c66affSColin Finck         return FALSE;
197c2c66affSColin Finck     }
198c2c66affSColin Finck 
199c2c66affSColin Finck     if (SourceName[0] == 0)
200c2c66affSColin Finck     {
201c2c66affSColin Finck         FormatOutput(IDS_SRC_MUST_BE_SPECIFIED);
202c2c66affSColin Finck         return FALSE;
203c2c66affSColin Finck     }
204c2c66affSColin Finck 
205c2c66affSColin Finck     return TRUE;
206c2c66affSColin Finck }
207c2c66affSColin Finck 
StopLoop(DWORD dwCtrlType)208c2c66affSColin Finck static BOOL WINAPI StopLoop(DWORD dwCtrlType)
209c2c66affSColin Finck {
210c2c66affSColin Finck     NeverStop = FALSE;
211c2c66affSColin Finck     PingCount = 0;
212c2c66affSColin Finck 
213c2c66affSColin Finck     return TRUE;
214c2c66affSColin Finck }
215c2c66affSColin Finck 
Setup(VOID)216c2c66affSColin Finck static BOOL Setup(VOID)
217c2c66affSColin Finck {
218c2c66affSColin Finck     WORD     wVersionRequested;
219c2c66affSColin Finck     WSADATA  WsaData;
220c2c66affSColin Finck     INT      Status;
221c2c66affSColin Finck     PHOSTENT phe;
222c2c66affSColin Finck     CHAR     aTargetName[256];
223c2c66affSColin Finck     IN_ADDR  Target;
224c2c66affSColin Finck 
225c2c66affSColin Finck     wVersionRequested = MAKEWORD(2, 2);
226c2c66affSColin Finck 
227c2c66affSColin Finck     Status = WSAStartup(wVersionRequested, &WsaData);
228c2c66affSColin Finck     if (Status != 0)
229c2c66affSColin Finck     {
230c2c66affSColin Finck         FormatOutput(IDS_COULD_NOT_INIT_WINSOCK);
231c2c66affSColin Finck         return FALSE;
232c2c66affSColin Finck     }
233c2c66affSColin Finck 
234c2c66affSColin Finck     if (!WideCharToMultiByte(CP_ACP, 0, TargetName, -1, aTargetName,
235c2c66affSColin Finck                              sizeof(aTargetName), NULL, NULL))
236c2c66affSColin Finck     {
237c2c66affSColin Finck         FormatOutput(IDS_UNKNOWN_HOST, TargetName);
238c2c66affSColin Finck         return FALSE;
239c2c66affSColin Finck     }
240c2c66affSColin Finck 
241c2c66affSColin Finck     phe = NULL;
242c2c66affSColin Finck     TargetAddr = inet_addr(aTargetName);
243c2c66affSColin Finck     if (TargetAddr == INADDR_NONE)
244c2c66affSColin Finck     {
245c2c66affSColin Finck         phe = gethostbyname(aTargetName);
246c2c66affSColin Finck         if (phe == NULL)
247c2c66affSColin Finck         {
248c2c66affSColin Finck             FormatOutput(IDS_UNKNOWN_HOST, TargetName);
249c2c66affSColin Finck             return FALSE;
250c2c66affSColin Finck         }
251c2c66affSColin Finck 
252c2c66affSColin Finck         CopyMemory(&TargetAddr, phe->h_addr, phe->h_length);
253c2c66affSColin Finck     }
254c2c66affSColin Finck 
255c2c66affSColin Finck     Target.S_un.S_addr = TargetAddr;
256c2c66affSColin Finck     swprintf(TargetIP, L"%d.%d.%d.%d", Target.S_un.S_un_b.s_b1,
257c2c66affSColin Finck                                        Target.S_un.S_un_b.s_b2,
258c2c66affSColin Finck                                        Target.S_un.S_un_b.s_b3,
259c2c66affSColin Finck                                        Target.S_un.S_un_b.s_b4);
260c2c66affSColin Finck 
261c2c66affSColin Finck     if (!WideCharToMultiByte(CP_ACP, 0, SourceName, -1, aTargetName,
262c2c66affSColin Finck                              sizeof(aTargetName), NULL, NULL))
263c2c66affSColin Finck     {
264c2c66affSColin Finck         FormatOutput(IDS_UNKNOWN_HOST, SourceName);
265c2c66affSColin Finck         return FALSE;
266c2c66affSColin Finck     }
267c2c66affSColin Finck 
268c2c66affSColin Finck     SourceAddr = inet_addr(aTargetName);
269c2c66affSColin Finck     if (SourceAddr == INADDR_NONE)
270c2c66affSColin Finck     {
271c2c66affSColin Finck         FormatOutput(IDS_UNKNOWN_HOST, SourceName);
272c2c66affSColin Finck         return FALSE;
273c2c66affSColin Finck     }
274c2c66affSColin Finck 
275c2c66affSColin Finck     SetConsoleCtrlHandler(StopLoop, TRUE);
276c2c66affSColin Finck 
277c2c66affSColin Finck     return TRUE;
278c2c66affSColin Finck }
279c2c66affSColin Finck 
Cleanup(VOID)280c2c66affSColin Finck static VOID Cleanup(VOID)
281c2c66affSColin Finck {
282c2c66affSColin Finck     WSACleanup();
283c2c66affSColin Finck }
284c2c66affSColin Finck 
QueryTime(PLARGE_INTEGER Time)285c2c66affSColin Finck static VOID QueryTime(PLARGE_INTEGER Time)
286c2c66affSColin Finck {
287c2c66affSColin Finck     if (UsePerformanceCounter)
288c2c66affSColin Finck     {
289c2c66affSColin Finck         if (QueryPerformanceCounter(Time) == 0)
290c2c66affSColin Finck         {
291c2c66affSColin Finck             /* This should not happen, but we fall
292c2c66affSColin Finck                back to GetCurrentTick() if it does */
293c2c66affSColin Finck             Time->u.LowPart  = (ULONG)GetTickCount();
294c2c66affSColin Finck             Time->u.HighPart = 0;
295c2c66affSColin Finck 
296c2c66affSColin Finck             /* 1 tick per millisecond for GetCurrentTick() */
297c2c66affSColin Finck             TicksPerMs.QuadPart = 1;
298c2c66affSColin Finck             /* GetCurrentTick() cannot handle microseconds */
299c2c66affSColin Finck             TicksPerUs.QuadPart = 1;
300c2c66affSColin Finck 
301c2c66affSColin Finck             UsePerformanceCounter = FALSE;
302c2c66affSColin Finck         }
303c2c66affSColin Finck     }
304c2c66affSColin Finck     else
305c2c66affSColin Finck     {
306c2c66affSColin Finck         Time->u.LowPart  = (ULONG)GetTickCount();
307c2c66affSColin Finck         Time->u.HighPart = 0;
308c2c66affSColin Finck     }
309c2c66affSColin Finck }
310c2c66affSColin Finck 
TimeToMsString(LPWSTR String,ULONG Length,LARGE_INTEGER Time)311c2c66affSColin Finck static VOID TimeToMsString(LPWSTR String, ULONG Length, LARGE_INTEGER Time)
312c2c66affSColin Finck {
313c2c66affSColin Finck     WCHAR         Convstr[40];
314c2c66affSColin Finck     LARGE_INTEGER LargeTime;
315c2c66affSColin Finck     LPWSTR ms;
316c2c66affSColin Finck 
317c2c66affSColin Finck     LargeTime.QuadPart = Time.QuadPart / TicksPerMs.QuadPart;
318c2c66affSColin Finck 
319c2c66affSColin Finck     _i64tow(LargeTime.QuadPart, Convstr, 10);
320c2c66affSColin Finck     wcscpy(String, Convstr);
321c2c66affSColin Finck     ms = String + wcslen(String);
322c2c66affSColin Finck     LoadString(GetModuleHandle(NULL), IDS_MS, ms, Length - (ms - String));
323c2c66affSColin Finck }
324c2c66affSColin Finck 
Ping(VOID)325c2c66affSColin Finck static BOOL Ping(VOID)
326c2c66affSColin Finck {
327c2c66affSColin Finck     LARGE_INTEGER RelativeTime;
328c2c66affSColin Finck     LARGE_INTEGER LargeTime;
329c2c66affSColin Finck     LARGE_INTEGER SentTime;
330c2c66affSColin Finck     DWORD Ret;
331c2c66affSColin Finck     BYTE TargetHW[6];
332c2c66affSColin Finck     ULONG Size;
333c2c66affSColin Finck     WCHAR Sign[2];
334c2c66affSColin Finck     WCHAR Time[100];
335c2c66affSColin Finck     WCHAR StrHwAddr[18];
336c2c66affSColin Finck 
337c2c66affSColin Finck     QueryTime(&SentTime);
338c2c66affSColin Finck     Size = sizeof(TargetHW);
339c2c66affSColin Finck     memset(TargetHW, 0xff, Size);
340c2c66affSColin Finck     ++Sent;
341c2c66affSColin Finck     Ret = SendARP(TargetAddr, SourceAddr, (PULONG)TargetHW, &Size);
342c2c66affSColin Finck     if (Ret == ERROR_SUCCESS)
343c2c66affSColin Finck     {
344c2c66affSColin Finck         QueryTime(&LargeTime);
345c2c66affSColin Finck 
346c2c66affSColin Finck         RelativeTime.QuadPart = (LargeTime.QuadPart - SentTime.QuadPart);
347c2c66affSColin Finck 
348c2c66affSColin Finck         if ((RelativeTime.QuadPart / TicksPerMs.QuadPart) < 1)
349c2c66affSColin Finck         {
350c2c66affSColin Finck             wcscpy(Sign, L"<");
351c2c66affSColin Finck             LoadString(GetModuleHandle(NULL), IDS_1MS, Time, sizeof(Time) / sizeof(WCHAR));
352c2c66affSColin Finck         }
353c2c66affSColin Finck         else
354c2c66affSColin Finck         {
355c2c66affSColin Finck             wcscpy(Sign, L"=");
356c2c66affSColin Finck             TimeToMsString(Time, sizeof(Time) / sizeof(WCHAR), RelativeTime);
357c2c66affSColin Finck         }
358c2c66affSColin Finck 
359c2c66affSColin Finck         swprintf(StrHwAddr, L"%02x:%02x:%02x:%02x:%02x:%02x", TargetHW[0], TargetHW[1],
360c2c66affSColin Finck                                                               TargetHW[2], TargetHW[3],
361c2c66affSColin Finck                                                               TargetHW[4], TargetHW[5]);
362c2c66affSColin Finck         FormatOutput(IDS_REPLY_FROM, TargetIP, StrHwAddr, Sign, Time);
363c2c66affSColin Finck         Received++;
364c2c66affSColin Finck 
365c2c66affSColin Finck         return TRUE;
366c2c66affSColin Finck     }
367c2c66affSColin Finck 
368c2c66affSColin Finck     return FALSE;
369c2c66affSColin Finck }
370c2c66affSColin Finck 
wmain(int argc,LPWSTR argv[])371c2c66affSColin Finck int wmain(int argc, LPWSTR argv[])
372c2c66affSColin Finck {
373c2c66affSColin Finck     UINT Count;
374c2c66affSColin Finck     LARGE_INTEGER PerformanceCounterFrequency;
375c2c66affSColin Finck 
376c2c66affSColin Finck     PingCount = 4;
377c2c66affSColin Finck     Timeout = 1000;
378c2c66affSColin Finck     hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
379c2c66affSColin Finck 
380c2c66affSColin Finck     UsePerformanceCounter = QueryPerformanceFrequency(&PerformanceCounterFrequency);
381c2c66affSColin Finck 
382c2c66affSColin Finck     if (UsePerformanceCounter)
383c2c66affSColin Finck     {
384c2c66affSColin Finck         /* Performance counters may return incorrect results on some multiprocessor
385c2c66affSColin Finck            platforms so we restrict execution on the first processor. This may fail
386c2c66affSColin Finck            on Windows NT so we fall back to GetCurrentTick() for timing */
387c2c66affSColin Finck         if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0)
388c2c66affSColin Finck             UsePerformanceCounter = FALSE;
389c2c66affSColin Finck 
390c2c66affSColin Finck         /* Convert frequency to ticks per millisecond */
391c2c66affSColin Finck         TicksPerMs.QuadPart = PerformanceCounterFrequency.QuadPart / 1000;
392c2c66affSColin Finck         /* And to ticks per microsecond */
393c2c66affSColin Finck         TicksPerUs.QuadPart = PerformanceCounterFrequency.QuadPart / 1000000;
394c2c66affSColin Finck     }
395c2c66affSColin Finck     if (!UsePerformanceCounter)
396c2c66affSColin Finck     {
397c2c66affSColin Finck         /* 1 tick per millisecond for GetCurrentTick() */
398c2c66affSColin Finck         TicksPerMs.QuadPart = 1;
399c2c66affSColin Finck         /* GetCurrentTick() cannot handle microseconds */
400c2c66affSColin Finck         TicksPerUs.QuadPart = 1;
401c2c66affSColin Finck     }
402c2c66affSColin Finck 
403c2c66affSColin Finck     if (!ParseCmdline(argc, argv) || !Setup())
404c2c66affSColin Finck     {
405c2c66affSColin Finck         return 1;
406c2c66affSColin Finck     }
407c2c66affSColin Finck 
408c2c66affSColin Finck     FormatOutput(IDS_ARPING_TO_FROM, TargetIP, SourceName);
409c2c66affSColin Finck 
410c2c66affSColin Finck     Count = 0;
411c2c66affSColin Finck     while (Count < PingCount || NeverStop)
412c2c66affSColin Finck     {
413c2c66affSColin Finck         Ping();
414c2c66affSColin Finck         Count++;
415c2c66affSColin Finck         if (Count < PingCount || NeverStop)
416c2c66affSColin Finck             Sleep(Timeout);
417c2c66affSColin Finck     }
418c2c66affSColin Finck 
419c2c66affSColin Finck     Cleanup();
420c2c66affSColin Finck 
421c2c66affSColin Finck     FormatOutput(IDS_ARPING_STATISTICS, Sent, Received);
422c2c66affSColin Finck 
423c2c66affSColin Finck     return 0;
424c2c66affSColin Finck }
425