1 /* 2 * Copyright (c) 2015 Tim Crawford 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * of this software and associated documentation files (the "Software"), to deal 6 * in the Software without restriction, including without limitation the rights 7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 * copies of the Software, and to permit persons to whom the Software is 9 * furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in all 12 * copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 */ 22 /* 23 * PROJECT: ReactOS Ping Command 24 * LICENSE: MIT 25 * FILE: base/applications/network/ping/ping.c 26 * PURPOSE: Network test utility 27 * PROGRAMMERS: Tim Crawford <crawfxrd@gmail.com> 28 */ 29 30 #include <stdlib.h> 31 32 #define WIN32_LEAN_AND_MEAN 33 34 #define WIN32_NO_STATUS 35 #include <windef.h> 36 #include <winbase.h> 37 #include <winsock2.h> 38 #include <ws2tcpip.h> 39 #include <iphlpapi.h> 40 #include <icmpapi.h> 41 42 #include <conutils.h> 43 44 #include "resource.h" 45 46 #define NDEBUG 47 #include <debug.h> 48 49 #define SIZEOF_ICMP_ERROR 8 50 #define SIZEOF_IO_STATUS_BLOCK 8 51 #define DEFAULT_TIMEOUT 1000 52 #define MAX_SEND_SIZE 65500 53 54 static BOOL ParseCmdLine(int argc, PWSTR argv[]); 55 static BOOL ResolveTarget(PCWSTR target); 56 static void Ping(void); 57 static void PrintStats(void); 58 static BOOL WINAPI ConsoleCtrlHandler(DWORD ControlType); 59 60 static HANDLE hIcmpFile = INVALID_HANDLE_VALUE; 61 static ULONG Timeout = 4000; 62 static int Family = AF_UNSPEC; 63 static ULONG RequestSize = 32; 64 static ULONG PingCount = 4; 65 static BOOL PingForever = FALSE; 66 static PADDRINFOW Target = NULL; 67 static PCWSTR TargetName = NULL; 68 static WCHAR Address[46]; 69 static WCHAR CanonName[NI_MAXHOST]; 70 static BOOL ResolveAddress = FALSE; 71 72 static ULONG RTTMax = 0; 73 static ULONG RTTMin = 0; 74 static ULONG RTTTotal = 0; 75 static ULONG EchosSent = 0; 76 static ULONG EchosReceived = 0; 77 static ULONG EchosSuccessful = 0; 78 79 static IP_OPTION_INFORMATION IpOptions; 80 81 int 82 wmain(int argc, WCHAR *argv[]) 83 { 84 WSADATA wsaData; 85 ULONG i; 86 DWORD StrLen = 46; 87 int Status; 88 89 /* Initialize the Console Standard Streams */ 90 ConInitStdStreams(); 91 92 IpOptions.Ttl = 128; 93 94 if (!ParseCmdLine(argc, argv)) 95 return 1; 96 97 if (!SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE)) 98 { 99 DPRINT("Failed to set control handler: %lu\n", GetLastError()); 100 return 1; 101 } 102 103 Status = WSAStartup(MAKEWORD(2, 2), &wsaData); 104 if (Status != 0) 105 { 106 ConResPrintf(StdErr, IDS_WINSOCK_FAIL, Status); 107 return 1; 108 } 109 110 if (!ResolveTarget(TargetName)) 111 { 112 WSACleanup(); 113 return 1; 114 } 115 116 if (WSAAddressToStringW(Target->ai_addr, (DWORD)Target->ai_addrlen, NULL, Address, &StrLen) != 0) 117 { 118 DPRINT("WSAAddressToStringW failed: %d\n", WSAGetLastError()); 119 FreeAddrInfoW(Target); 120 WSACleanup(); 121 return 1; 122 } 123 124 if (Family == AF_INET6) 125 hIcmpFile = Icmp6CreateFile(); 126 else 127 hIcmpFile = IcmpCreateFile(); 128 129 130 if (hIcmpFile == INVALID_HANDLE_VALUE) 131 { 132 DPRINT("IcmpCreateFile failed: %lu\n", GetLastError()); 133 FreeAddrInfoW(Target); 134 WSACleanup(); 135 return 1; 136 } 137 138 if (*CanonName) 139 ConResPrintf(StdOut, IDS_PINGING_HOSTNAME, CanonName, Address); 140 else 141 ConResPrintf(StdOut, IDS_PINGING_ADDRESS, Address); 142 143 ConResPrintf(StdOut, IDS_PING_SIZE, RequestSize); 144 145 Ping(); 146 147 i = 1; 148 while (i < PingCount) 149 { 150 Sleep(1000); 151 Ping(); 152 153 if (!PingForever) 154 i++; 155 } 156 157 PrintStats(); 158 159 IcmpCloseHandle(hIcmpFile); 160 FreeAddrInfoW(Target); 161 WSACleanup(); 162 163 return 0; 164 } 165 166 static 167 BOOL 168 ParseCmdLine(int argc, PWSTR argv[]) 169 { 170 int i; 171 172 if (argc < 2) 173 { 174 ConResPrintf(StdOut, IDS_USAGE); 175 return FALSE; 176 } 177 178 for (i = 1; i < argc; i++) 179 { 180 if (argv[i][0] == L'-' || argv[i][0] == L'/') 181 { 182 switch (argv[i][1]) 183 { 184 case L't': 185 PingForever = TRUE; 186 break; 187 188 case L'a': 189 ResolveAddress = TRUE; 190 break; 191 192 case L'n': 193 { 194 if (i + 1 < argc) 195 { 196 PingForever = FALSE; 197 PingCount = wcstoul(argv[++i], NULL, 0); 198 if (PingCount == 0) 199 { 200 ConResPrintf(StdErr, IDS_BAD_VALUE, argv[i - 1], 1, UINT_MAX); 201 return FALSE; 202 } 203 } 204 else 205 { 206 ConResPrintf(StdErr, IDS_MISSING_VALUE, argv[i]); 207 return FALSE; 208 } 209 break; 210 } 211 212 case L'l': 213 { 214 if (i + 1 < argc) 215 { 216 RequestSize = wcstoul(argv[++i], NULL, 0); 217 if (RequestSize > MAX_SEND_SIZE) 218 { 219 ConResPrintf(StdErr, IDS_BAD_VALUE, argv[i - 1], 0, MAX_SEND_SIZE); 220 return FALSE; 221 } 222 } 223 else 224 { 225 ConResPrintf(StdErr, IDS_MISSING_VALUE, argv[i]); 226 return FALSE; 227 } 228 break; 229 } 230 231 case L'f': 232 { 233 if (Family == AF_INET6) 234 { 235 ConResPrintf(StdErr, IDS_WRONG_FAMILY, argv[i], L"IPv4"); 236 return FALSE; 237 } 238 239 Family = AF_INET; 240 IpOptions.Flags |= IP_FLAG_DF; 241 break; 242 } 243 244 case L'i': 245 { 246 if (i + 1 < argc) 247 { 248 ULONG Ttl = wcstoul(argv[++i], NULL, 0); 249 250 if ((Ttl == 0) || (Ttl > UCHAR_MAX)) 251 { 252 ConResPrintf(StdErr, IDS_BAD_VALUE, argv[i - 1], 1, UCHAR_MAX); 253 return FALSE; 254 } 255 256 IpOptions.Ttl = (UCHAR)Ttl; 257 } 258 else 259 { 260 ConResPrintf(StdErr, IDS_MISSING_VALUE, argv[i]); 261 return FALSE; 262 } 263 break; 264 } 265 266 case L'v': 267 { 268 if (Family == AF_INET6) 269 { 270 ConResPrintf(StdErr, IDS_WRONG_FAMILY, argv[i], L"IPv4"); 271 return FALSE; 272 } 273 274 Family = AF_INET; 275 276 if (i + 1 < argc) 277 { 278 /* This option has been deprecated. Don't do anything. */ 279 i++; 280 } 281 else 282 { 283 ConResPrintf(StdErr, IDS_MISSING_VALUE, argv[i]); 284 return FALSE; 285 } 286 287 break; 288 } 289 290 case L'w': 291 { 292 if (i + 1 < argc) 293 { 294 Timeout = wcstoul(argv[++i], NULL, 0); 295 if (Timeout < DEFAULT_TIMEOUT) 296 Timeout = DEFAULT_TIMEOUT; 297 } 298 else 299 { 300 ConResPrintf(StdErr, IDS_MISSING_VALUE, argv[i]); 301 return FALSE; 302 } 303 break; 304 } 305 306 case L'R': 307 { 308 if (Family == AF_INET) 309 { 310 ConResPrintf(StdErr, IDS_WRONG_FAMILY, argv[i], L"IPv6"); 311 return FALSE; 312 } 313 314 Family = AF_INET6; 315 316 /* This option has been deprecated. Don't do anything. */ 317 break; 318 } 319 320 case L'4': 321 { 322 if (Family == AF_INET6) 323 { 324 ConResPrintf(StdErr, IDS_WRONG_FAMILY, argv[i], L"IPv4"); 325 return FALSE; 326 } 327 328 Family = AF_INET; 329 break; 330 } 331 332 case L'6': 333 { 334 if (Family == AF_INET) 335 { 336 ConResPrintf(StdErr, IDS_WRONG_FAMILY, argv[i], L"IPv6"); 337 return FALSE; 338 } 339 340 Family = AF_INET6; 341 break; 342 } 343 344 case L'?': 345 ConResPrintf(StdOut, IDS_USAGE); 346 return FALSE; 347 348 default: 349 ConResPrintf(StdErr, IDS_BAD_OPTION, argv[i]); 350 ConResPrintf(StdErr, IDS_USAGE); 351 return FALSE; 352 } 353 } 354 else 355 { 356 if (TargetName != NULL) 357 { 358 ConResPrintf(StdErr, IDS_BAD_PARAMETER, argv[i]); 359 return FALSE; 360 } 361 362 TargetName = argv[i]; 363 } 364 } 365 366 if (TargetName == NULL) 367 { 368 ConResPrintf(StdErr, IDS_MISSING_ADDRESS); 369 return FALSE; 370 } 371 372 return TRUE; 373 } 374 375 static 376 BOOL 377 ResolveTarget(PCWSTR target) 378 { 379 ADDRINFOW hints; 380 int Status; 381 382 ZeroMemory(&hints, sizeof(hints)); 383 hints.ai_family = Family; 384 hints.ai_flags = AI_NUMERICHOST; 385 386 Status = GetAddrInfoW(target, NULL, &hints, &Target); 387 if (Status != 0) 388 { 389 hints.ai_flags = AI_CANONNAME; 390 391 Status = GetAddrInfoW(target, NULL, &hints, &Target); 392 if (Status != 0) 393 { 394 ConResPrintf(StdOut, IDS_UNKNOWN_HOST, target); 395 return FALSE; 396 } 397 398 wcsncpy(CanonName, Target->ai_canonname, wcslen(Target->ai_canonname)); 399 } 400 else if (ResolveAddress) 401 { 402 Status = GetNameInfoW(Target->ai_addr, Target->ai_addrlen, 403 CanonName, _countof(CanonName), 404 NULL, 0, 405 NI_NAMEREQD); 406 if (Status != 0) 407 { 408 DPRINT("GetNameInfoW failed: %d\n", WSAGetLastError()); 409 } 410 } 411 412 Family = Target->ai_family; 413 414 return TRUE; 415 } 416 417 static 418 void 419 Ping(void) 420 { 421 PVOID ReplyBuffer = NULL; 422 PVOID SendBuffer = NULL; 423 DWORD ReplySize = 0; 424 DWORD Status; 425 426 SendBuffer = malloc(RequestSize); 427 if (SendBuffer == NULL) 428 { 429 ConResPrintf(StdErr, IDS_NO_RESOURCES); 430 exit(1); 431 } 432 433 ZeroMemory(SendBuffer, RequestSize); 434 435 if (Family == AF_INET6) 436 ReplySize += sizeof(ICMPV6_ECHO_REPLY); 437 else 438 ReplySize += sizeof(ICMP_ECHO_REPLY); 439 440 ReplySize += RequestSize + SIZEOF_ICMP_ERROR + SIZEOF_IO_STATUS_BLOCK; 441 442 ReplyBuffer = malloc(ReplySize); 443 if (ReplyBuffer == NULL) 444 { 445 ConResPrintf(StdErr, IDS_NO_RESOURCES); 446 free(SendBuffer); 447 exit(1); 448 } 449 450 ZeroMemory(ReplyBuffer, ReplySize); 451 452 EchosSent++; 453 454 if (Family == AF_INET6) 455 { 456 struct sockaddr_in6 Source; 457 458 ZeroMemory(&Source, sizeof(Source)); 459 Source.sin6_family = AF_INET6; 460 461 Status = Icmp6SendEcho2(hIcmpFile, NULL, NULL, NULL, 462 &Source, 463 (struct sockaddr_in6 *)Target->ai_addr, 464 SendBuffer, (USHORT)RequestSize, &IpOptions, 465 ReplyBuffer, ReplySize, Timeout); 466 } 467 else 468 { 469 Status = IcmpSendEcho2(hIcmpFile, NULL, NULL, NULL, 470 ((PSOCKADDR_IN)Target->ai_addr)->sin_addr.s_addr, 471 SendBuffer, (USHORT)RequestSize, &IpOptions, 472 ReplyBuffer, ReplySize, Timeout); 473 } 474 475 free(SendBuffer); 476 477 if (Status == 0) 478 { 479 Status = GetLastError(); 480 switch (Status) 481 { 482 case IP_REQ_TIMED_OUT: 483 ConResPrintf(StdOut, IDS_REQUEST_TIMED_OUT); 484 break; 485 486 default: 487 ConResPrintf(StdOut, IDS_TRANSMIT_FAILED, Status); 488 break; 489 } 490 } 491 else 492 { 493 SOCKADDR_IN6 SockAddrIn6; 494 SOCKADDR_IN SockAddrIn; 495 PSOCKADDR SockAddr; 496 socklen_t Size; 497 498 EchosReceived++; 499 500 ZeroMemory(&SockAddrIn, sizeof(SockAddrIn)); 501 ZeroMemory(&SockAddrIn6, sizeof(SockAddrIn6)); 502 503 if (Family == AF_INET6) 504 { 505 PICMPV6_ECHO_REPLY pEchoReply; 506 PIPV6_ADDRESS_EX Ipv6Addr; 507 508 pEchoReply = (PICMPV6_ECHO_REPLY)ReplyBuffer; 509 510 Ipv6Addr = (PIPV6_ADDRESS_EX)&pEchoReply->Address; 511 SockAddrIn6.sin6_family = AF_INET6; 512 CopyMemory(SockAddrIn6.sin6_addr.u.Word, Ipv6Addr->sin6_addr, sizeof(SockAddrIn6.sin6_addr)); 513 //SockAddrIn6.sin6_addr = Ipv6Addr->sin6_addr; 514 SockAddr = (PSOCKADDR)&SockAddrIn6; 515 Size = sizeof(SOCKADDR_IN6); 516 517 GetNameInfoW(SockAddr, 518 Size, 519 Address, 520 NI_MAXHOST, 521 NULL, 522 0, 523 NI_NUMERICHOST); 524 525 ConResPrintf(StdOut, IDS_REPLY_FROM, Address); 526 527 switch (pEchoReply->Status) 528 { 529 case IP_SUCCESS: 530 { 531 EchosSuccessful++; 532 533 if (pEchoReply->RoundTripTime == 0) 534 ConResPrintf(StdOut, IDS_REPLY_TIME_0MS); 535 else 536 ConResPrintf(StdOut, IDS_REPLY_TIME_MS, pEchoReply->RoundTripTime); 537 538 if (pEchoReply->RoundTripTime < RTTMin || RTTMin == 0) 539 RTTMin = pEchoReply->RoundTripTime; 540 541 if (pEchoReply->RoundTripTime > RTTMax || RTTMax == 0) 542 RTTMax = pEchoReply->RoundTripTime; 543 544 ConPuts(StdOut, L"\n"); 545 546 RTTTotal += pEchoReply->RoundTripTime; 547 break; 548 } 549 550 case IP_DEST_NET_UNREACHABLE: 551 ConResPrintf(StdOut, IDS_DEST_NET_UNREACHABLE); 552 break; 553 554 case IP_DEST_HOST_UNREACHABLE: 555 ConResPrintf(StdOut, IDS_DEST_HOST_UNREACHABLE); 556 break; 557 558 case IP_TTL_EXPIRED_TRANSIT: 559 ConResPrintf(StdOut, IDS_TTL_EXPIRED); 560 break; 561 562 default: 563 ConResPrintf(StdOut, IDS_REPLY_STATUS, pEchoReply->Status); 564 break; 565 } 566 } 567 else 568 { 569 PICMP_ECHO_REPLY pEchoReply; 570 IPAddr *IP4Addr; 571 572 pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer; 573 574 IP4Addr = (IPAddr *)&pEchoReply->Address; 575 SockAddrIn.sin_family = AF_INET; 576 SockAddrIn.sin_addr.S_un.S_addr = *IP4Addr; 577 SockAddr = (PSOCKADDR)&SockAddrIn; 578 Size = sizeof(SOCKADDR_IN); 579 580 GetNameInfoW(SockAddr, 581 Size, 582 Address, 583 NI_MAXHOST, 584 NULL, 585 0, 586 NI_NUMERICHOST); 587 588 ConResPrintf(StdOut, IDS_REPLY_FROM, Address); 589 590 switch (pEchoReply->Status) 591 { 592 case IP_SUCCESS: 593 { 594 EchosSuccessful++; 595 596 ConResPrintf(StdOut, IDS_REPLY_BYTES, pEchoReply->DataSize); 597 598 if (pEchoReply->RoundTripTime == 0) 599 ConResPrintf(StdOut, IDS_REPLY_TIME_0MS); 600 else 601 ConResPrintf(StdOut, IDS_REPLY_TIME_MS, pEchoReply->RoundTripTime); 602 603 ConResPrintf(StdOut, IDS_REPLY_TTL, pEchoReply->Options.Ttl); 604 605 if (pEchoReply->RoundTripTime < RTTMin || RTTMin == 0) 606 RTTMin = pEchoReply->RoundTripTime; 607 608 if (pEchoReply->RoundTripTime > RTTMax || RTTMax == 0) 609 RTTMax = pEchoReply->RoundTripTime; 610 611 RTTTotal += pEchoReply->RoundTripTime; 612 break; 613 } 614 615 case IP_DEST_NET_UNREACHABLE: 616 ConResPrintf(StdOut, IDS_DEST_NET_UNREACHABLE); 617 break; 618 619 case IP_DEST_HOST_UNREACHABLE: 620 ConResPrintf(StdOut, IDS_DEST_HOST_UNREACHABLE); 621 break; 622 623 case IP_TTL_EXPIRED_TRANSIT: 624 ConResPrintf(StdOut, IDS_TTL_EXPIRED); 625 break; 626 627 default: 628 ConResPrintf(StdOut, IDS_REPLY_STATUS, pEchoReply->Status); 629 break; 630 } 631 } 632 } 633 634 free(ReplyBuffer); 635 } 636 637 static 638 void 639 PrintStats(void) 640 { 641 ULONG EchosLost = EchosSent - EchosReceived; 642 ULONG PercentLost = (ULONG)((EchosLost / (double)EchosSent) * 100.0); 643 644 ConResPrintf(StdOut, IDS_STATISTICS, Address, EchosSent, EchosReceived, EchosLost, PercentLost); 645 646 if (EchosSuccessful > 0) 647 { 648 ULONG RTTAverage = RTTTotal / EchosSuccessful; 649 ConResPrintf(StdOut, IDS_APPROXIMATE_RTT, RTTMin, RTTMax, RTTAverage); 650 } 651 } 652 653 static 654 BOOL 655 WINAPI 656 ConsoleCtrlHandler(DWORD ControlType) 657 { 658 switch (ControlType) 659 { 660 case CTRL_C_EVENT: 661 PrintStats(); 662 ConResPrintf(StdOut, IDS_CTRL_C); 663 return FALSE; 664 665 case CTRL_BREAK_EVENT: 666 PrintStats(); 667 ConResPrintf(StdOut, IDS_CTRL_BREAK); 668 return TRUE; 669 670 case CTRL_CLOSE_EVENT: 671 PrintStats(); 672 return FALSE; 673 674 default: 675 return FALSE; 676 } 677 } 678