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 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 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 } 425