461abbf5c3caaf8defc596235caa39d76708eabc
[reactos.git] / modules / rosapps / applications / cmdutils / arping / arping.c
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
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
63 DataLength = FormatMessage(FORMAT_MESSAGE_FROM_STRING, Format, 0, 0, Buf,\
64 sizeof(Buf) / sizeof(WCHAR), &valist);
65
66 if(!DataLength)
67 {
68 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
69 {
70 va_end(valist);
71 return;
72 }
73
74 DataLength = FormatMessage(FORMAT_MESSAGE_FROM_STRING |\
75 FORMAT_MESSAGE_ALLOCATE_BUFFER,\
76 Format, 0, 0, (LPWSTR)&pBuf, 0, &valist);
77 }
78
79 if(!DataLength)
80 {
81 va_end(valist);
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
112 static VOID Usage(VOID)
113 {
114 FormatOutput(IDS_USAGE);
115 }
116
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
208 static BOOL WINAPI StopLoop(DWORD dwCtrlType)
209 {
210 NeverStop = FALSE;
211 PingCount = 0;
212
213 return TRUE;
214 }
215
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
280 static VOID Cleanup(VOID)
281 {
282 WSACleanup();
283 }
284
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
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
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
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 }