3 * Copyright 2004--2005, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "talk/base/winping.h"
33 #include "talk/base/byteorder.h"
34 #include "talk/base/common.h"
35 #include "talk/base/ipaddress.h"
36 #include "talk/base/logging.h"
37 #include "talk/base/nethelpers.h"
38 #include "talk/base/socketaddress.h"
42 //////////////////////////////////////////////////////////////////////
43 // Found in IPExport.h
44 //////////////////////////////////////////////////////////////////////
46 typedef struct icmp_echo_reply {
47 ULONG Address; // Replying address
48 ULONG Status; // Reply IP_STATUS
49 ULONG RoundTripTime; // RTT in milliseconds
50 USHORT DataSize; // Reply data size in bytes
51 USHORT Reserved; // Reserved for system use
52 PVOID Data; // Pointer to the reply data
53 struct ip_option_information Options; // Reply options
54 } ICMP_ECHO_REPLY, * PICMP_ECHO_REPLY;
56 typedef struct icmpv6_echo_reply_lh {
59 unsigned int RoundTripTime;
60 } ICMPV6_ECHO_REPLY, *PICMPV6_ECHO_REPLY;
63 // IP_STATUS codes returned from IP APIs
66 #define IP_STATUS_BASE 11000
69 #define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1)
70 #define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2)
71 #define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3)
72 #define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4)
73 #define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5)
74 #define IP_NO_RESOURCES (IP_STATUS_BASE + 6)
75 #define IP_BAD_OPTION (IP_STATUS_BASE + 7)
76 #define IP_HW_ERROR (IP_STATUS_BASE + 8)
77 #define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9)
78 #define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10)
79 #define IP_BAD_REQ (IP_STATUS_BASE + 11)
80 #define IP_BAD_ROUTE (IP_STATUS_BASE + 12)
81 #define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13)
82 #define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14)
83 #define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15)
84 #define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16)
85 #define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17)
86 #define IP_BAD_DESTINATION (IP_STATUS_BASE + 18)
88 #define IP_ADDR_DELETED (IP_STATUS_BASE + 19)
89 #define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20)
90 #define IP_MTU_CHANGE (IP_STATUS_BASE + 21)
91 #define IP_UNLOAD (IP_STATUS_BASE + 22)
92 #define IP_ADDR_ADDED (IP_STATUS_BASE + 23)
93 #define IP_MEDIA_CONNECT (IP_STATUS_BASE + 24)
94 #define IP_MEDIA_DISCONNECT (IP_STATUS_BASE + 25)
95 #define IP_BIND_ADAPTER (IP_STATUS_BASE + 26)
96 #define IP_UNBIND_ADAPTER (IP_STATUS_BASE + 27)
97 #define IP_DEVICE_DOES_NOT_EXIST (IP_STATUS_BASE + 28)
98 #define IP_DUPLICATE_ADDRESS (IP_STATUS_BASE + 29)
99 #define IP_INTERFACE_METRIC_CHANGE (IP_STATUS_BASE + 30)
100 #define IP_RECONFIG_SECFLTR (IP_STATUS_BASE + 31)
101 #define IP_NEGOTIATING_IPSEC (IP_STATUS_BASE + 32)
102 #define IP_INTERFACE_WOL_CAPABILITY_CHANGE (IP_STATUS_BASE + 33)
103 #define IP_DUPLICATE_IPADD (IP_STATUS_BASE + 34)
105 #define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50)
106 #define MAX_IP_STATUS IP_GENERAL_FAILURE
107 #define IP_PENDING (IP_STATUS_BASE + 255)
110 // Values used in the IP header Flags field.
112 #define IP_FLAG_DF 0x2 // Don't fragment this packet.
115 // Supported IP Option Types.
117 // These types define the options which may be used in the OptionsData field
118 // of the ip_option_information structure. See RFC 791 for a complete
119 // description of each.
121 #define IP_OPT_EOL 0 // End of list option
122 #define IP_OPT_NOP 1 // No operation
123 #define IP_OPT_SECURITY 0x82 // Security option
124 #define IP_OPT_LSRR 0x83 // Loose source route
125 #define IP_OPT_SSRR 0x89 // Strict source route
126 #define IP_OPT_RR 0x7 // Record route
127 #define IP_OPT_TS 0x44 // Timestamp
128 #define IP_OPT_SID 0x88 // Stream ID (obsolete)
129 #define IP_OPT_ROUTER_ALERT 0x94 // Router Alert Option
131 #define MAX_OPT_SIZE 40 // Maximum length of IP options in bytes
133 //////////////////////////////////////////////////////////////////////
134 // Global Constants and Types
135 //////////////////////////////////////////////////////////////////////
137 const char * const ICMP_DLL_NAME = "Iphlpapi.dll";
138 const char * const ICMP_CREATE_FUNC = "IcmpCreateFile";
139 const char * const ICMP_CLOSE_FUNC = "IcmpCloseHandle";
140 const char * const ICMP_SEND_FUNC = "IcmpSendEcho";
141 const char * const ICMP6_CREATE_FUNC = "Icmp6CreateFile";
142 const char * const ICMP6_CLOSE_FUNC = "Icmp6CloseHandle";
143 const char * const ICMP6_SEND_FUNC = "Icmp6SendEcho2";
145 inline uint32 ReplySize(uint32 data_size, int family) {
146 if (family == AF_INET) {
147 // A ping error message is 8 bytes long, so make sure we allow for at least
148 // 8 bytes of reply data.
149 return sizeof(ICMP_ECHO_REPLY) + talk_base::_max<uint32>(8, data_size);
150 } else if (family == AF_INET6) {
151 // Per MSDN, Send6IcmpEcho2 needs at least one ICMPV6_ECHO_REPLY,
152 // 8 bytes for ICMP header, _and_ an IO_BLOCK_STATUS (2 pointers),
153 // in addition to the data size.
154 return sizeof(ICMPV6_ECHO_REPLY) + data_size + 8 + (2 * sizeof(DWORD*));
160 //////////////////////////////////////////////////////////////////////
162 //////////////////////////////////////////////////////////////////////
165 : dll_(0), hping_(INVALID_HANDLE_VALUE), create_(0), close_(0), send_(0),
166 create6_(0), send6_(0), data_(0), dlen_(0), reply_(0),
167 rlen_(0), valid_(false) {
169 dll_ = LoadLibraryA(ICMP_DLL_NAME);
171 LOG(LERROR) << "LoadLibrary: " << GetLastError();
175 create_ = (PIcmpCreateFile) GetProcAddress(dll_, ICMP_CREATE_FUNC);
176 close_ = (PIcmpCloseHandle) GetProcAddress(dll_, ICMP_CLOSE_FUNC);
177 send_ = (PIcmpSendEcho) GetProcAddress(dll_, ICMP_SEND_FUNC);
178 if (!create_ || !close_ || !send_) {
179 LOG(LERROR) << "GetProcAddress(ICMP_*): " << GetLastError();
183 if (hping_ == INVALID_HANDLE_VALUE) {
184 LOG(LERROR) << "IcmpCreateFile: " << GetLastError();
188 if (HasIPv6Enabled()) {
189 create6_ = (PIcmp6CreateFile) GetProcAddress(dll_, ICMP6_CREATE_FUNC);
190 send6_ = (PIcmp6SendEcho2) GetProcAddress(dll_, ICMP6_SEND_FUNC);
191 if (!create6_ || !send6_) {
192 LOG(LERROR) << "GetProcAddress(ICMP6_*): " << GetLastError();
195 hping6_ = create6_();
196 if (hping6_ == INVALID_HANDLE_VALUE) {
197 LOG(LERROR) << "Icmp6CreateFile: " << GetLastError();
202 rlen_ = ReplySize(dlen_, AF_INET);
203 data_ = new char[dlen_];
204 reply_ = new char[rlen_];
209 WinPing::~WinPing() {
210 if ((hping_ != INVALID_HANDLE_VALUE) && close_) {
212 LOG(WARNING) << "IcmpCloseHandle: " << GetLastError();
214 if ((hping6_ != INVALID_HANDLE_VALUE) && close_) {
215 if (!close_(hping6_)) {
216 LOG(WARNING) << "Icmp6CloseHandle: " << GetLastError();
227 WinPing::PingResult WinPing::Ping(
228 IPAddress ip, uint32 data_size, uint32 timeout, uint8 ttl,
229 bool allow_fragments) {
231 if (data_size == 0 || timeout == 0 || ttl == 0) {
232 LOG(LERROR) << "IcmpSendEcho: data_size/timeout/ttl is 0.";
233 return PING_INVALID_PARAMS;
238 IP_OPTION_INFORMATION ipopt;
239 memset(&ipopt, 0, sizeof(ipopt));
240 if (!allow_fragments)
241 ipopt.Flags |= IP_FLAG_DF;
244 uint32 reply_size = ReplySize(data_size, ip.family());
246 if (data_size > dlen_) {
249 data_ = new char[dlen_];
250 memset(data_, 'z', dlen_);
253 if (reply_size > rlen_) {
256 reply_ = new char[rlen_];
259 if (ip.family() == AF_INET) {
260 result = send_(hping_, ip.ipv4_address().S_un.S_addr,
261 data_, uint16(data_size), &ipopt,
262 reply_, reply_size, timeout);
263 } else if (ip.family() == AF_INET6) {
264 sockaddr_in6 src = {0};
265 sockaddr_in6 dst = {0};
266 src.sin6_family = AF_INET6;
267 dst.sin6_family = AF_INET6;
268 dst.sin6_addr = ip.ipv6_address();
269 result = send6_(hping6_, NULL, NULL, NULL,
271 data_, int16(data_size), &ipopt,
272 reply_, reply_size, timeout);
275 DWORD error = GetLastError();
276 if (error == IP_PACKET_TOO_BIG)
277 return PING_TOO_LARGE;
278 if (error == IP_REQ_TIMED_OUT)
280 LOG(LERROR) << "IcmpSendEcho(" << ip.ToSensitiveString()
281 << ", " << data_size << "): " << error;
288 //////////////////////////////////////////////////////////////////////
289 // Microsoft Documenation
290 //////////////////////////////////////////////////////////////////////
296 // Routine Description:
298 // Opens a handle on which ICMP Echo Requests can be issued.
306 // An open file handle or INVALID_HANDLE_VALUE. Extended error information
307 // is available by calling GetLastError().
309 //////////////////////////////////////////////////////////////////////
315 // Routine Description:
317 // Closes a handle opened by ICMPOpenFile.
321 // IcmpHandle - The handle to close.
325 // TRUE if the handle was closed successfully, otherwise FALSE. Extended
326 // error information is available by calling GetLastError().
328 //////////////////////////////////////////////////////////////////////
334 // Routine Description:
336 // Sends an ICMP Echo request and returns any replies. The
337 // call returns when the timeout has expired or the reply buffer
342 // IcmpHandle - An open handle returned by ICMPCreateFile.
344 // DestinationAddress - The destination of the echo request.
346 // RequestData - A buffer containing the data to send in the
349 // RequestSize - The number of bytes in the request data buffer.
351 // RequestOptions - Pointer to the IP header options for the request.
354 // ReplyBuffer - A buffer to hold any replies to the request.
355 // On return, the buffer will contain an array of
356 // ICMP_ECHO_REPLY structures followed by the
357 // options and data for the replies. The buffer
358 // should be large enough to hold at least one
359 // ICMP_ECHO_REPLY structure plus
360 // MAX(RequestSize, 8) bytes of data since an ICMP
361 // error message contains 8 bytes of data.
363 // ReplySize - The size in bytes of the reply buffer.
365 // Timeout - The time in milliseconds to wait for replies.
369 // Returns the number of ICMP_ECHO_REPLY structures stored in ReplyBuffer.
370 // The status of each reply is contained in the structure. If the return
371 // value is zero, extended error information is available via
374 //////////////////////////////////////////////////////////////////////
376 } // namespace talk_base