2 * Copyright (c) 2015 Tim Crawford
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:
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
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
23 * PROJECT: ReactOS Ping Command
25 * FILE: base/applications/network/ping/ping.c
26 * PURPOSE: Network test utility
27 * PROGRAMMERS: Tim Crawford <crawfxrd@gmail.com>
32 #define WIN32_LEAN_AND_MEAN
34 #define WIN32_NO_STATUS
49 #define SIZEOF_ICMP_ERROR 8
50 #define SIZEOF_IO_STATUS_BLOCK 8
51 #define DEFAULT_TIMEOUT 1000
52 #define MAX_SEND_SIZE 65500
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
);
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
;
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;
79 static IP_OPTION_INFORMATION IpOptions
;
82 wmain(int argc
, WCHAR
*argv
[])
89 /* Initialize the Console Standard Streams */
94 if (!ParseCmdLine(argc
, argv
))
97 if (!SetConsoleCtrlHandler(ConsoleCtrlHandler
, TRUE
))
99 DPRINT("Failed to set control handler: %lu\n", GetLastError());
103 Status
= WSAStartup(MAKEWORD(2, 2), &wsaData
);
106 ConResPrintf(StdErr
, IDS_WINSOCK_FAIL
, Status
);
110 if (!ResolveTarget(TargetName
))
116 if (WSAAddressToStringW(Target
->ai_addr
, (DWORD
)Target
->ai_addrlen
, NULL
, Address
, &StrLen
) != 0)
118 DPRINT("WSAAddressToStringW failed: %d\n", WSAGetLastError());
119 FreeAddrInfoW(Target
);
124 if (Family
== AF_INET6
)
125 hIcmpFile
= Icmp6CreateFile();
127 hIcmpFile
= IcmpCreateFile();
130 if (hIcmpFile
== INVALID_HANDLE_VALUE
)
132 DPRINT("IcmpCreateFile failed: %lu\n", GetLastError());
133 FreeAddrInfoW(Target
);
139 ConResPrintf(StdOut
, IDS_PINGING_HOSTNAME
, CanonName
, Address
);
141 ConResPrintf(StdOut
, IDS_PINGING_ADDRESS
, Address
);
143 ConResPrintf(StdOut
, IDS_PING_SIZE
, RequestSize
);
148 while (i
< PingCount
)
159 IcmpCloseHandle(hIcmpFile
);
160 FreeAddrInfoW(Target
);
168 ParseCmdLine(int argc
, PWSTR argv
[])
174 ConResPrintf(StdOut
, IDS_USAGE
);
178 for (i
= 1; i
< argc
; i
++)
180 if (argv
[i
][0] == L
'-' || argv
[i
][0] == L
'/')
189 ResolveAddress
= TRUE
;
197 PingCount
= wcstoul(argv
[++i
], NULL
, 0);
200 ConResPrintf(StdErr
, IDS_BAD_VALUE
, argv
[i
- 1], 1, UINT_MAX
);
206 ConResPrintf(StdErr
, IDS_MISSING_VALUE
, argv
[i
]);
216 RequestSize
= wcstoul(argv
[++i
], NULL
, 0);
217 if (RequestSize
> MAX_SEND_SIZE
)
219 ConResPrintf(StdErr
, IDS_BAD_VALUE
, argv
[i
- 1], 0, MAX_SEND_SIZE
);
225 ConResPrintf(StdErr
, IDS_MISSING_VALUE
, argv
[i
]);
233 if (Family
== AF_INET6
)
235 ConResPrintf(StdErr
, IDS_WRONG_FAMILY
, argv
[i
], L
"IPv4");
240 IpOptions
.Flags
|= IP_FLAG_DF
;
248 ULONG Ttl
= wcstoul(argv
[++i
], NULL
, 0);
250 if ((Ttl
== 0) || (Ttl
> UCHAR_MAX
))
252 ConResPrintf(StdErr
, IDS_BAD_VALUE
, argv
[i
- 1], 1, UCHAR_MAX
);
256 IpOptions
.Ttl
= (UCHAR
)Ttl
;
260 ConResPrintf(StdErr
, IDS_MISSING_VALUE
, argv
[i
]);
268 if (Family
== AF_INET6
)
270 ConResPrintf(StdErr
, IDS_WRONG_FAMILY
, argv
[i
], L
"IPv4");
278 /* This option has been deprecated. Don't do anything. */
283 ConResPrintf(StdErr
, IDS_MISSING_VALUE
, argv
[i
]);
294 Timeout
= wcstoul(argv
[++i
], NULL
, 0);
295 if (Timeout
< DEFAULT_TIMEOUT
)
296 Timeout
= DEFAULT_TIMEOUT
;
300 ConResPrintf(StdErr
, IDS_MISSING_VALUE
, argv
[i
]);
308 if (Family
== AF_INET
)
310 ConResPrintf(StdErr
, IDS_WRONG_FAMILY
, argv
[i
], L
"IPv6");
316 /* This option has been deprecated. Don't do anything. */
322 if (Family
== AF_INET6
)
324 ConResPrintf(StdErr
, IDS_WRONG_FAMILY
, argv
[i
], L
"IPv4");
334 if (Family
== AF_INET
)
336 ConResPrintf(StdErr
, IDS_WRONG_FAMILY
, argv
[i
], L
"IPv6");
345 ConResPrintf(StdOut
, IDS_USAGE
);
349 ConResPrintf(StdErr
, IDS_BAD_OPTION
, argv
[i
]);
350 ConResPrintf(StdErr
, IDS_USAGE
);
356 if (TargetName
!= NULL
)
358 ConResPrintf(StdErr
, IDS_BAD_PARAMETER
, argv
[i
]);
362 TargetName
= argv
[i
];
366 if (TargetName
== NULL
)
368 ConResPrintf(StdErr
, IDS_MISSING_ADDRESS
);
377 ResolveTarget(PCWSTR target
)
382 ZeroMemory(&hints
, sizeof(hints
));
383 hints
.ai_family
= Family
;
384 hints
.ai_flags
= AI_NUMERICHOST
;
386 Status
= GetAddrInfoW(target
, NULL
, &hints
, &Target
);
389 hints
.ai_flags
= AI_CANONNAME
;
391 Status
= GetAddrInfoW(target
, NULL
, &hints
, &Target
);
394 ConResPrintf(StdOut
, IDS_UNKNOWN_HOST
, target
);
398 wcsncpy(CanonName
, Target
->ai_canonname
, wcslen(Target
->ai_canonname
));
400 else if (ResolveAddress
)
402 Status
= GetNameInfoW(Target
->ai_addr
, Target
->ai_addrlen
,
403 CanonName
, _countof(CanonName
),
408 DPRINT("GetNameInfoW failed: %d\n", WSAGetLastError());
412 Family
= Target
->ai_family
;
421 PVOID ReplyBuffer
= NULL
;
422 PVOID SendBuffer
= NULL
;
426 SendBuffer
= malloc(RequestSize
);
427 if (SendBuffer
== NULL
)
429 ConResPrintf(StdErr
, IDS_NO_RESOURCES
);
433 ZeroMemory(SendBuffer
, RequestSize
);
435 if (Family
== AF_INET6
)
436 ReplySize
+= sizeof(ICMPV6_ECHO_REPLY
);
438 ReplySize
+= sizeof(ICMP_ECHO_REPLY
);
440 ReplySize
+= RequestSize
+ SIZEOF_ICMP_ERROR
+ SIZEOF_IO_STATUS_BLOCK
;
442 ReplyBuffer
= malloc(ReplySize
);
443 if (ReplyBuffer
== NULL
)
445 ConResPrintf(StdErr
, IDS_NO_RESOURCES
);
450 ZeroMemory(ReplyBuffer
, ReplySize
);
454 if (Family
== AF_INET6
)
456 struct sockaddr_in6 Source
;
458 ZeroMemory(&Source
, sizeof(Source
));
459 Source
.sin6_family
= AF_INET6
;
461 Status
= Icmp6SendEcho2(hIcmpFile
, NULL
, NULL
, NULL
,
463 (struct sockaddr_in6
*)Target
->ai_addr
,
464 SendBuffer
, (USHORT
)RequestSize
, &IpOptions
,
465 ReplyBuffer
, ReplySize
, Timeout
);
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
);
479 Status
= GetLastError();
482 case IP_DEST_HOST_UNREACHABLE
:
483 ConResPrintf(StdOut
, IDS_DEST_HOST_UNREACHABLE
);
486 case IP_DEST_NET_UNREACHABLE
:
487 ConResPrintf(StdOut
, IDS_DEST_NET_UNREACHABLE
);
490 case IP_REQ_TIMED_OUT
:
491 ConResPrintf(StdOut
, IDS_REQUEST_TIMED_OUT
);
495 ConResPrintf(StdOut
, IDS_TRANSMIT_FAILED
, Status
);
503 ConResPrintf(StdOut
, IDS_REPLY_FROM
, Address
);
505 if (Family
== AF_INET6
)
507 PICMPV6_ECHO_REPLY pEchoReply
;
509 pEchoReply
= (PICMPV6_ECHO_REPLY
)ReplyBuffer
;
511 switch (pEchoReply
->Status
)
517 if (pEchoReply
->RoundTripTime
== 0)
518 ConResPrintf(StdOut
, IDS_REPLY_TIME_0MS
);
520 ConResPrintf(StdOut
, IDS_REPLY_TIME_MS
, pEchoReply
->RoundTripTime
);
522 if (pEchoReply
->RoundTripTime
< RTTMin
|| RTTMin
== 0)
523 RTTMin
= pEchoReply
->RoundTripTime
;
525 if (pEchoReply
->RoundTripTime
> RTTMax
|| RTTMax
== 0)
526 RTTMax
= pEchoReply
->RoundTripTime
;
528 ConPuts(StdOut
, L
"\n");
530 RTTTotal
+= pEchoReply
->RoundTripTime
;
534 case IP_TTL_EXPIRED_TRANSIT
:
535 ConResPrintf(StdOut
, IDS_TTL_EXPIRED
);
539 ConResPrintf(StdOut
, IDS_REPLY_STATUS
, pEchoReply
->Status
);
545 PICMP_ECHO_REPLY pEchoReply
;
547 pEchoReply
= (PICMP_ECHO_REPLY
)ReplyBuffer
;
549 switch (pEchoReply
->Status
)
555 ConResPrintf(StdOut
, IDS_REPLY_BYTES
, pEchoReply
->DataSize
);
557 if (pEchoReply
->RoundTripTime
== 0)
558 ConResPrintf(StdOut
, IDS_REPLY_TIME_0MS
);
560 ConResPrintf(StdOut
, IDS_REPLY_TIME_MS
, pEchoReply
->RoundTripTime
);
562 ConResPrintf(StdOut
, IDS_REPLY_TTL
, pEchoReply
->Options
.Ttl
);
564 if (pEchoReply
->RoundTripTime
< RTTMin
|| RTTMin
== 0)
565 RTTMin
= pEchoReply
->RoundTripTime
;
567 if (pEchoReply
->RoundTripTime
> RTTMax
|| RTTMax
== 0)
568 RTTMax
= pEchoReply
->RoundTripTime
;
570 RTTTotal
+= pEchoReply
->RoundTripTime
;
574 case IP_TTL_EXPIRED_TRANSIT
:
575 ConResPrintf(StdOut
, IDS_TTL_EXPIRED
);
579 ConResPrintf(StdOut
, IDS_REPLY_STATUS
, pEchoReply
->Status
);
592 ULONG EchosLost
= EchosSent
- EchosReceived
;
593 ULONG PercentLost
= (ULONG
)((EchosLost
/ (double)EchosSent
) * 100.0);
595 ConResPrintf(StdOut
, IDS_STATISTICS
, Address
, EchosSent
, EchosReceived
, EchosLost
, PercentLost
);
597 if (EchosSuccessful
> 0)
599 ULONG RTTAverage
= RTTTotal
/ EchosSuccessful
;
600 ConResPrintf(StdOut
, IDS_APPROXIMATE_RTT
, RTTMin
, RTTMax
, RTTAverage
);
607 ConsoleCtrlHandler(DWORD ControlType
)
613 ConResPrintf(StdOut
, IDS_CTRL_C
);
616 case CTRL_BREAK_EVENT
:
618 ConResPrintf(StdOut
, IDS_CTRL_BREAK
);
621 case CTRL_CLOSE_EVENT
: