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
24 * PROJECT: ReactOS Ping Command
26 * FILE: base/applications/network/ping/ping.c
27 * PURPOSE: Network test utility
28 * PROGRAMMERS: Tim Crawford <crawfxrd@gmail.com>
31 #define WIN32_LEAN_AND_MEAN
45 #define SIZEOF_ICMP_ERROR 8
46 #define SIZEOF_IO_STATUS_BLOCK 8
47 #define DEFAULT_TIMEOUT 1000
48 #define MAX_SEND_SIZE 65500
50 static BOOL
ParseCmdLine(int argc
, PWSTR argv
[]);
51 static BOOL
ResolveTarget(PCWSTR target
);
52 static void Ping(void);
53 static void PrintStats(void);
54 static BOOL WINAPI
ConsoleCtrlHandler(DWORD ControlType
);
55 static void PrintString(UINT id
, ...);
57 static HANDLE hStdOut
;
58 static HANDLE hIcmpFile
= INVALID_HANDLE_VALUE
;
59 static ULONG Timeout
= 4000;
60 static int Family
= AF_UNSPEC
;
61 static ULONG RequestSize
= 32;
62 static ULONG PingCount
= 4;
63 static BOOL PingForever
= FALSE
;
64 static PADDRINFOW Target
= NULL
;
65 static PCWSTR TargetName
= NULL
;
66 static WCHAR Address
[46];
67 static WCHAR CanonName
[NI_MAXHOST
];
68 static BOOL ResolveAddress
= FALSE
;
70 static ULONG RTTMax
= 0;
71 static ULONG RTTMin
= 0;
72 static ULONG RTTTotal
= 0;
73 static ULONG EchosSent
= 0;
74 static ULONG EchosReceived
= 0;
75 static ULONG EchosSuccessful
= 0;
77 static IP_OPTION_INFORMATION IpOptions
;
80 wmain(int argc
, WCHAR
*argv
[])
89 hStdOut
= GetStdHandle(STD_OUTPUT_HANDLE
);
91 if (!ParseCmdLine(argc
, argv
))
96 if (!SetConsoleCtrlHandler(ConsoleCtrlHandler
, TRUE
))
98 DPRINT("Failed to set control handler: %lu\n", GetLastError());
103 Status
= WSAStartup(MAKEWORD(2, 2), &wsaData
);
106 PrintString(IDS_WINSOCK_FAIL
, Status
);
111 if (!ResolveTarget(TargetName
))
118 if (WSAAddressToStringW(Target
->ai_addr
, (DWORD
)Target
->ai_addrlen
, NULL
, Address
, &StrLen
) != 0)
120 DPRINT("WSAAddressToStringW failed: %d\n", WSAGetLastError());
121 FreeAddrInfoW(Target
);
127 if (Family
== AF_INET6
)
129 hIcmpFile
= Icmp6CreateFile();
133 hIcmpFile
= IcmpCreateFile();
137 if (hIcmpFile
== INVALID_HANDLE_VALUE
)
139 DPRINT("IcmpCreateFile failed: %lu\n", GetLastError());
140 FreeAddrInfoW(Target
);
148 PrintString(IDS_PINGING_HOSTNAME
, CanonName
, Address
);
152 PrintString(IDS_PINGING_ADDRESS
, Address
);
155 PrintString(IDS_PING_SIZE
, RequestSize
);
160 while (i
< PingCount
)
171 IcmpCloseHandle(hIcmpFile
);
172 FreeAddrInfoW(Target
);
180 PrintString(UINT id
, ...)
182 #define RC_STRING_MAX_SIZE 4096
184 WCHAR Format
[RC_STRING_MAX_SIZE
];
185 LPWSTR lpMsgBuf
= NULL
;
189 if (!LoadStringW(GetModuleHandleW(NULL
), id
, Format
, _countof(Format
)))
191 DPRINT("LoadStringW failed: %lu\n", GetLastError());
197 Len
= FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_STRING
,
198 Format
, 0, 0, (PWSTR
)&lpMsgBuf
, 0, &args
);
199 if (lpMsgBuf
/* && Len != 0 */)
201 // TODO: Handle writing to file. Well, use the ConUtils lib!
202 WriteConsoleW(hStdOut
, lpMsgBuf
, Len
, &Len
, NULL
);
207 DPRINT("FormatMessageW failed: %lu\n", GetLastError());
215 ParseCmdLine(int argc
, PWSTR argv
[])
221 PrintString(IDS_USAGE
);
225 for (i
= 1; i
< argc
; i
++)
227 if (argv
[i
][0] == L
'-' || argv
[i
][0] == L
'/')
236 ResolveAddress
= TRUE
;
243 PingCount
= wcstoul(argv
[++i
], NULL
, 0);
247 PrintString(IDS_BAD_VALUE
, argv
[i
- 1], 1, UINT_MAX
);
254 PrintString(IDS_MISSING_VALUE
, argv
[i
]);
264 RequestSize
= wcstoul(argv
[++i
], NULL
, 0);
266 if (RequestSize
> MAX_SEND_SIZE
)
268 PrintString(IDS_BAD_VALUE
, argv
[i
- 1], 0, MAX_SEND_SIZE
);
275 PrintString(IDS_MISSING_VALUE
, argv
[i
]);
283 if (Family
== AF_INET6
)
285 PrintString(IDS_WRONG_FAMILY
, argv
[i
], L
"IPv4");
291 IpOptions
.Flags
|= IP_FLAG_DF
;
297 ULONG Ttl
= wcstoul(argv
[++i
], NULL
, 0);
299 if ((Ttl
== 0) || (Ttl
> UCHAR_MAX
))
301 PrintString(IDS_BAD_VALUE
, argv
[i
- 1], 1, UCHAR_MAX
);
306 IpOptions
.Ttl
= (UCHAR
)Ttl
;
310 PrintString(IDS_MISSING_VALUE
, argv
[i
]);
318 if (Family
== AF_INET6
)
320 PrintString(IDS_WRONG_FAMILY
, argv
[i
], L
"IPv4");
329 /* This option has been deprecated. Don't do anything. */
334 PrintString(IDS_MISSING_VALUE
, argv
[i
]);
344 Timeout
= wcstoul(argv
[++i
], NULL
, 0);
346 if (Timeout
< DEFAULT_TIMEOUT
)
348 Timeout
= DEFAULT_TIMEOUT
;
353 PrintString(IDS_MISSING_VALUE
, argv
[i
]);
361 if (Family
== AF_INET
)
363 PrintString(IDS_WRONG_FAMILY
, argv
[i
], L
"IPv6");
370 /* This option has been deprecated. Don't do anything. */
374 if (Family
== AF_INET6
)
376 PrintString(IDS_WRONG_FAMILY
, argv
[i
], L
"IPv4");
385 if (Family
== AF_INET
)
387 PrintString(IDS_WRONG_FAMILY
, argv
[i
], L
"IPv6");
396 PrintString(IDS_USAGE
);
400 PrintString(IDS_BAD_OPTION
, argv
[i
]);
401 PrintString(IDS_USAGE
);
407 if (TargetName
!= NULL
)
409 PrintString(IDS_BAD_PARAMETER
, argv
[i
]);
414 TargetName
= argv
[i
];
418 if (TargetName
== NULL
)
420 PrintString(IDS_MISSING_ADDRESS
);
430 ResolveTarget(PCWSTR target
)
435 ZeroMemory(&hints
, sizeof(hints
));
436 hints
.ai_family
= Family
;
437 hints
.ai_flags
= AI_NUMERICHOST
;
439 Status
= GetAddrInfoW(target
, NULL
, &hints
, &Target
);
442 hints
.ai_flags
= AI_CANONNAME
;
444 Status
= GetAddrInfoW(target
, NULL
, &hints
, &Target
);
447 PrintString(IDS_UNKNOWN_HOST
, target
);
452 wcsncpy(CanonName
, Target
->ai_canonname
, wcslen(Target
->ai_canonname
));
454 else if (ResolveAddress
)
456 Status
= GetNameInfoW(
457 Target
->ai_addr
, Target
->ai_addrlen
,
458 CanonName
, _countof(CanonName
),
464 DPRINT("GetNameInfoW failed: %d\n", WSAGetLastError());
468 Family
= Target
->ai_family
;
477 PVOID ReplyBuffer
= NULL
;
478 PVOID SendBuffer
= NULL
;
482 SendBuffer
= malloc(RequestSize
);
483 if (SendBuffer
== NULL
)
485 PrintString(IDS_NO_RESOURCES
);
490 ZeroMemory(SendBuffer
, RequestSize
);
492 if (Family
== AF_INET6
)
494 ReplySize
+= sizeof(ICMPV6_ECHO_REPLY
);
498 ReplySize
+= sizeof(ICMP_ECHO_REPLY
);
501 ReplySize
+= RequestSize
+ SIZEOF_ICMP_ERROR
+ SIZEOF_IO_STATUS_BLOCK
;
503 ReplyBuffer
= malloc(ReplySize
);
504 if (ReplyBuffer
== NULL
)
506 PrintString(IDS_NO_RESOURCES
);
512 ZeroMemory(ReplyBuffer
, ReplySize
);
516 if (Family
== AF_INET6
)
518 struct sockaddr_in6 Source
;
520 ZeroMemory(&Source
, sizeof(Source
));
521 Source
.sin6_family
= AF_INET6
;
523 Status
= Icmp6SendEcho2(
524 hIcmpFile
, NULL
, NULL
, NULL
,
526 (struct sockaddr_in6
*)Target
->ai_addr
,
527 SendBuffer
, (USHORT
)RequestSize
, &IpOptions
,
528 ReplyBuffer
, ReplySize
, Timeout
);
532 Status
= IcmpSendEcho2(
533 hIcmpFile
, NULL
, NULL
, NULL
,
534 ((PSOCKADDR_IN
)Target
->ai_addr
)->sin_addr
.s_addr
,
535 SendBuffer
, (USHORT
)RequestSize
, &IpOptions
,
536 ReplyBuffer
, ReplySize
, Timeout
);
543 Status
= GetLastError();
546 case IP_DEST_HOST_UNREACHABLE
:
547 PrintString(IDS_DEST_HOST_UNREACHABLE
);
550 case IP_DEST_NET_UNREACHABLE
:
551 PrintString(IDS_DEST_NET_UNREACHABLE
);
554 case IP_REQ_TIMED_OUT
:
555 PrintString(IDS_REQUEST_TIMED_OUT
);
559 PrintString(IDS_TRANSMIT_FAILED
, Status
);
567 PrintString(IDS_REPLY_FROM
, Address
);
569 if (Family
== AF_INET6
)
571 PICMPV6_ECHO_REPLY pEchoReply
;
573 pEchoReply
= (PICMPV6_ECHO_REPLY
)ReplyBuffer
;
575 switch (pEchoReply
->Status
)
580 if (pEchoReply
->RoundTripTime
== 0)
582 PrintString(IDS_REPLY_TIME_0MS
);
586 PrintString(IDS_REPLY_TIME_MS
, pEchoReply
->RoundTripTime
);
589 if (pEchoReply
->RoundTripTime
< RTTMin
|| RTTMin
== 0)
591 RTTMin
= pEchoReply
->RoundTripTime
;
594 if (pEchoReply
->RoundTripTime
> RTTMax
|| RTTMax
== 0)
596 RTTMax
= pEchoReply
->RoundTripTime
;
601 RTTTotal
+= pEchoReply
->RoundTripTime
;
604 case IP_TTL_EXPIRED_TRANSIT
:
605 PrintString(IDS_TTL_EXPIRED
);
609 PrintString(IDS_REPLY_STATUS
, pEchoReply
->Status
);
615 PICMP_ECHO_REPLY pEchoReply
;
617 pEchoReply
= (PICMP_ECHO_REPLY
)ReplyBuffer
;
619 switch (pEchoReply
->Status
)
624 PrintString(IDS_REPLY_BYTES
, pEchoReply
->DataSize
);
626 if (pEchoReply
->RoundTripTime
== 0)
628 PrintString(IDS_REPLY_TIME_0MS
);
632 PrintString(IDS_REPLY_TIME_MS
, pEchoReply
->RoundTripTime
);
635 PrintString(IDS_REPLY_TTL
, pEchoReply
->Options
.Ttl
);
637 if (pEchoReply
->RoundTripTime
< RTTMin
|| RTTMin
== 0)
639 RTTMin
= pEchoReply
->RoundTripTime
;
642 if (pEchoReply
->RoundTripTime
> RTTMax
|| RTTMax
== 0)
644 RTTMax
= pEchoReply
->RoundTripTime
;
647 RTTTotal
+= pEchoReply
->RoundTripTime
;
650 case IP_TTL_EXPIRED_TRANSIT
:
651 PrintString(IDS_TTL_EXPIRED
);
655 PrintString(IDS_REPLY_STATUS
, pEchoReply
->Status
);
668 ULONG EchosLost
= EchosSent
- EchosReceived
;
669 ULONG PercentLost
= (ULONG
)((EchosLost
/ (double)EchosSent
) * 100.0);
671 PrintString(IDS_STATISTICS
, Address
, EchosSent
, EchosReceived
, EchosLost
, PercentLost
);
673 if (EchosSuccessful
> 0)
675 ULONG RTTAverage
= RTTTotal
/ EchosSuccessful
;
677 PrintString(IDS_APPROXIMATE_RTT
, RTTMin
, RTTMax
, RTTAverage
);
684 ConsoleCtrlHandler(DWORD ControlType
)
690 PrintString(IDS_CTRL_C
);
693 case CTRL_BREAK_EVENT
:
695 PrintString(IDS_CTRL_BREAK
);
698 case CTRL_CLOSE_EVENT
: