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