1 /* 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/base/winping.h" 12 13 #include <assert.h> 14 #include <Iphlpapi.h> 15 16 #include <algorithm> 17 18 #include "webrtc/base/byteorder.h" 19 #include "webrtc/base/common.h" 20 #include "webrtc/base/ipaddress.h" 21 #include "webrtc/base/logging.h" 22 #include "webrtc/base/nethelpers.h" 23 #include "webrtc/base/socketaddress.h" 24 25 namespace rtc { 26 27 ////////////////////////////////////////////////////////////////////// 28 // Found in IPExport.h 29 ////////////////////////////////////////////////////////////////////// 30 31 typedef struct icmp_echo_reply { 32 ULONG Address; // Replying address 33 ULONG Status; // Reply IP_STATUS 34 ULONG RoundTripTime; // RTT in milliseconds 35 USHORT DataSize; // Reply data size in bytes 36 USHORT Reserved; // Reserved for system use 37 PVOID Data; // Pointer to the reply data 38 struct ip_option_information Options; // Reply options 39 } ICMP_ECHO_REPLY, * PICMP_ECHO_REPLY; 40 41 typedef struct icmpv6_echo_reply_lh { 42 sockaddr_in6 Address; 43 ULONG Status; 44 unsigned int RoundTripTime; 45 } ICMPV6_ECHO_REPLY, *PICMPV6_ECHO_REPLY; 46 47 // 48 // IP_STATUS codes returned from IP APIs 49 // 50 51 #define IP_STATUS_BASE 11000 52 53 #define IP_SUCCESS 0 54 #define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1) 55 #define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2) 56 #define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3) 57 #define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4) 58 #define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5) 59 #define IP_NO_RESOURCES (IP_STATUS_BASE + 6) 60 #define IP_BAD_OPTION (IP_STATUS_BASE + 7) 61 #define IP_HW_ERROR (IP_STATUS_BASE + 8) 62 #define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9) 63 #define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10) 64 #define IP_BAD_REQ (IP_STATUS_BASE + 11) 65 #define IP_BAD_ROUTE (IP_STATUS_BASE + 12) 66 #define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13) 67 #define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14) 68 #define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15) 69 #define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16) 70 #define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17) 71 #define IP_BAD_DESTINATION (IP_STATUS_BASE + 18) 72 73 #define IP_ADDR_DELETED (IP_STATUS_BASE + 19) 74 #define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20) 75 #define IP_MTU_CHANGE (IP_STATUS_BASE + 21) 76 #define IP_UNLOAD (IP_STATUS_BASE + 22) 77 #define IP_ADDR_ADDED (IP_STATUS_BASE + 23) 78 #define IP_MEDIA_CONNECT (IP_STATUS_BASE + 24) 79 #define IP_MEDIA_DISCONNECT (IP_STATUS_BASE + 25) 80 #define IP_BIND_ADAPTER (IP_STATUS_BASE + 26) 81 #define IP_UNBIND_ADAPTER (IP_STATUS_BASE + 27) 82 #define IP_DEVICE_DOES_NOT_EXIST (IP_STATUS_BASE + 28) 83 #define IP_DUPLICATE_ADDRESS (IP_STATUS_BASE + 29) 84 #define IP_INTERFACE_METRIC_CHANGE (IP_STATUS_BASE + 30) 85 #define IP_RECONFIG_SECFLTR (IP_STATUS_BASE + 31) 86 #define IP_NEGOTIATING_IPSEC (IP_STATUS_BASE + 32) 87 #define IP_INTERFACE_WOL_CAPABILITY_CHANGE (IP_STATUS_BASE + 33) 88 #define IP_DUPLICATE_IPADD (IP_STATUS_BASE + 34) 89 90 #define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50) 91 #define MAX_IP_STATUS IP_GENERAL_FAILURE 92 #define IP_PENDING (IP_STATUS_BASE + 255) 93 94 // 95 // Values used in the IP header Flags field. 96 // 97 #define IP_FLAG_DF 0x2 // Don't fragment this packet. 98 99 // 100 // Supported IP Option Types. 101 // 102 // These types define the options which may be used in the OptionsData field 103 // of the ip_option_information structure. See RFC 791 for a complete 104 // description of each. 105 // 106 #define IP_OPT_EOL 0 // End of list option 107 #define IP_OPT_NOP 1 // No operation 108 #define IP_OPT_SECURITY 0x82 // Security option 109 #define IP_OPT_LSRR 0x83 // Loose source route 110 #define IP_OPT_SSRR 0x89 // Strict source route 111 #define IP_OPT_RR 0x7 // Record route 112 #define IP_OPT_TS 0x44 // Timestamp 113 #define IP_OPT_SID 0x88 // Stream ID (obsolete) 114 #define IP_OPT_ROUTER_ALERT 0x94 // Router Alert Option 115 116 #define MAX_OPT_SIZE 40 // Maximum length of IP options in bytes 117 118 ////////////////////////////////////////////////////////////////////// 119 // Global Constants and Types 120 ////////////////////////////////////////////////////////////////////// 121 122 const char * const ICMP_DLL_NAME = "Iphlpapi.dll"; 123 const char * const ICMP_CREATE_FUNC = "IcmpCreateFile"; 124 const char * const ICMP_CLOSE_FUNC = "IcmpCloseHandle"; 125 const char * const ICMP_SEND_FUNC = "IcmpSendEcho"; 126 const char * const ICMP6_CREATE_FUNC = "Icmp6CreateFile"; 127 const char * const ICMP6_SEND_FUNC = "Icmp6SendEcho2"; 128 129 inline uint32_t ReplySize(uint32_t data_size, int family) { 130 if (family == AF_INET) { 131 // A ping error message is 8 bytes long, so make sure we allow for at least 132 // 8 bytes of reply data. 133 return sizeof(ICMP_ECHO_REPLY) + std::max<uint32_t>(8, data_size); 134 } else if (family == AF_INET6) { 135 // Per MSDN, Send6IcmpEcho2 needs at least one ICMPV6_ECHO_REPLY, 136 // 8 bytes for ICMP header, _and_ an IO_BLOCK_STATUS (2 pointers), 137 // in addition to the data size. 138 return sizeof(ICMPV6_ECHO_REPLY) + data_size + 8 + (2 * sizeof(DWORD*)); 139 } else { 140 return 0; 141 } 142 } 143 144 ////////////////////////////////////////////////////////////////////// 145 // WinPing 146 ////////////////////////////////////////////////////////////////////// 147 148 WinPing::WinPing() 149 : dll_(0), hping_(INVALID_HANDLE_VALUE), create_(0), close_(0), send_(0), 150 create6_(0), send6_(0), data_(0), dlen_(0), reply_(0), 151 rlen_(0), valid_(false) { 152 153 dll_ = LoadLibraryA(ICMP_DLL_NAME); 154 if (!dll_) { 155 LOG(LERROR) << "LoadLibrary: " << GetLastError(); 156 return; 157 } 158 159 create_ = (PIcmpCreateFile) GetProcAddress(dll_, ICMP_CREATE_FUNC); 160 close_ = (PIcmpCloseHandle) GetProcAddress(dll_, ICMP_CLOSE_FUNC); 161 send_ = (PIcmpSendEcho) GetProcAddress(dll_, ICMP_SEND_FUNC); 162 if (!create_ || !close_ || !send_) { 163 LOG(LERROR) << "GetProcAddress(ICMP_*): " << GetLastError(); 164 return; 165 } 166 hping_ = create_(); 167 if (hping_ == INVALID_HANDLE_VALUE) { 168 LOG(LERROR) << "IcmpCreateFile: " << GetLastError(); 169 return; 170 } 171 172 if (HasIPv6Enabled()) { 173 create6_ = (PIcmp6CreateFile) GetProcAddress(dll_, ICMP6_CREATE_FUNC); 174 send6_ = (PIcmp6SendEcho2) GetProcAddress(dll_, ICMP6_SEND_FUNC); 175 if (!create6_ || !send6_) { 176 LOG(LERROR) << "GetProcAddress(ICMP6_*): " << GetLastError(); 177 return; 178 } 179 hping6_ = create6_(); 180 if (hping6_ == INVALID_HANDLE_VALUE) { 181 LOG(LERROR) << "Icmp6CreateFile: " << GetLastError(); 182 } 183 } 184 185 dlen_ = 0; 186 rlen_ = ReplySize(dlen_, AF_INET); 187 data_ = new char[dlen_]; 188 reply_ = new char[rlen_]; 189 190 valid_ = true; 191 } 192 193 WinPing::~WinPing() { 194 if ((hping_ != INVALID_HANDLE_VALUE) && close_) { 195 if (!close_(hping_)) 196 LOG(WARNING) << "IcmpCloseHandle: " << GetLastError(); 197 } 198 if ((hping6_ != INVALID_HANDLE_VALUE) && close_) { 199 if (!close_(hping6_)) { 200 LOG(WARNING) << "Icmp6CloseHandle: " << GetLastError(); 201 } 202 } 203 204 if (dll_) 205 FreeLibrary(dll_); 206 207 delete[] data_; 208 delete[] reply_; 209 } 210 211 WinPing::PingResult WinPing::Ping(IPAddress ip, 212 uint32_t data_size, 213 uint32_t timeout, 214 uint8_t ttl, 215 bool allow_fragments) { 216 if (data_size == 0 || timeout == 0 || ttl == 0) { 217 LOG(LERROR) << "IcmpSendEcho: data_size/timeout/ttl is 0."; 218 return PING_INVALID_PARAMS; 219 } 220 221 assert(IsValid()); 222 223 IP_OPTION_INFORMATION ipopt; 224 memset(&ipopt, 0, sizeof(ipopt)); 225 if (!allow_fragments) 226 ipopt.Flags |= IP_FLAG_DF; 227 ipopt.Ttl = ttl; 228 229 uint32_t reply_size = ReplySize(data_size, ip.family()); 230 231 if (data_size > dlen_) { 232 delete [] data_; 233 dlen_ = data_size; 234 data_ = new char[dlen_]; 235 memset(data_, 'z', dlen_); 236 } 237 238 if (reply_size > rlen_) { 239 delete [] reply_; 240 rlen_ = reply_size; 241 reply_ = new char[rlen_]; 242 } 243 DWORD result = 0; 244 if (ip.family() == AF_INET) { 245 result = send_(hping_, ip.ipv4_address().S_un.S_addr, data_, 246 uint16_t(data_size), &ipopt, reply_, reply_size, timeout); 247 } else if (ip.family() == AF_INET6) { 248 sockaddr_in6 src = {0}; 249 sockaddr_in6 dst = {0}; 250 src.sin6_family = AF_INET6; 251 dst.sin6_family = AF_INET6; 252 dst.sin6_addr = ip.ipv6_address(); 253 result = send6_(hping6_, NULL, NULL, NULL, &src, &dst, data_, 254 int16_t(data_size), &ipopt, reply_, reply_size, timeout); 255 } 256 if (result == 0) { 257 DWORD error = GetLastError(); 258 if (error == IP_PACKET_TOO_BIG) 259 return PING_TOO_LARGE; 260 if (error == IP_REQ_TIMED_OUT) 261 return PING_TIMEOUT; 262 LOG(LERROR) << "IcmpSendEcho(" << ip.ToSensitiveString() 263 << ", " << data_size << "): " << error; 264 return PING_FAIL; 265 } 266 267 return PING_SUCCESS; 268 } 269 270 ////////////////////////////////////////////////////////////////////// 271 // Microsoft Documenation 272 ////////////////////////////////////////////////////////////////////// 273 // 274 // Routine Name: 275 // 276 // IcmpCreateFile 277 // 278 // Routine Description: 279 // 280 // Opens a handle on which ICMP Echo Requests can be issued. 281 // 282 // Arguments: 283 // 284 // None. 285 // 286 // Return Value: 287 // 288 // An open file handle or INVALID_HANDLE_VALUE. Extended error information 289 // is available by calling GetLastError(). 290 // 291 ////////////////////////////////////////////////////////////////////// 292 // 293 // Routine Name: 294 // 295 // IcmpCloseHandle 296 // 297 // Routine Description: 298 // 299 // Closes a handle opened by ICMPOpenFile. 300 // 301 // Arguments: 302 // 303 // IcmpHandle - The handle to close. 304 // 305 // Return Value: 306 // 307 // TRUE if the handle was closed successfully, otherwise FALSE. Extended 308 // error information is available by calling GetLastError(). 309 // 310 ////////////////////////////////////////////////////////////////////// 311 // 312 // Routine Name: 313 // 314 // IcmpSendEcho 315 // 316 // Routine Description: 317 // 318 // Sends an ICMP Echo request and returns any replies. The 319 // call returns when the timeout has expired or the reply buffer 320 // is filled. 321 // 322 // Arguments: 323 // 324 // IcmpHandle - An open handle returned by ICMPCreateFile. 325 // 326 // DestinationAddress - The destination of the echo request. 327 // 328 // RequestData - A buffer containing the data to send in the 329 // request. 330 // 331 // RequestSize - The number of bytes in the request data buffer. 332 // 333 // RequestOptions - Pointer to the IP header options for the request. 334 // May be NULL. 335 // 336 // ReplyBuffer - A buffer to hold any replies to the request. 337 // On return, the buffer will contain an array of 338 // ICMP_ECHO_REPLY structures followed by the 339 // options and data for the replies. The buffer 340 // should be large enough to hold at least one 341 // ICMP_ECHO_REPLY structure plus 342 // MAX(RequestSize, 8) bytes of data since an ICMP 343 // error message contains 8 bytes of data. 344 // 345 // ReplySize - The size in bytes of the reply buffer. 346 // 347 // Timeout - The time in milliseconds to wait for replies. 348 // 349 // Return Value: 350 // 351 // Returns the number of ICMP_ECHO_REPLY structures stored in ReplyBuffer. 352 // The status of each reply is contained in the structure. If the return 353 // value is zero, extended error information is available via 354 // GetLastError(). 355 // 356 ////////////////////////////////////////////////////////////////////// 357 358 } // namespace rtc 359