1 /* 2 * libjingle 3 * Copyright 2004--2005, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 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. 15 * 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. 26 */ 27 28 #include "talk/base/winping.h" 29 30 #include <Iphlpapi.h> 31 #include <cassert> 32 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" 39 40 namespace talk_base { 41 42 ////////////////////////////////////////////////////////////////////// 43 // Found in IPExport.h 44 ////////////////////////////////////////////////////////////////////// 45 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; 55 56 typedef struct icmpv6_echo_reply_lh { 57 sockaddr_in6 Address; 58 ULONG Status; 59 unsigned int RoundTripTime; 60 } ICMPV6_ECHO_REPLY, *PICMPV6_ECHO_REPLY; 61 62 // 63 // IP_STATUS codes returned from IP APIs 64 // 65 66 #define IP_STATUS_BASE 11000 67 68 #define IP_SUCCESS 0 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) 87 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) 104 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) 108 109 // 110 // Values used in the IP header Flags field. 111 // 112 #define IP_FLAG_DF 0x2 // Don't fragment this packet. 113 114 // 115 // Supported IP Option Types. 116 // 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. 120 // 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 130 131 #define MAX_OPT_SIZE 40 // Maximum length of IP options in bytes 132 133 ////////////////////////////////////////////////////////////////////// 134 // Global Constants and Types 135 ////////////////////////////////////////////////////////////////////// 136 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"; 144 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*)); 155 } else { 156 return 0; 157 } 158 } 159 160 ////////////////////////////////////////////////////////////////////// 161 // WinPing 162 ////////////////////////////////////////////////////////////////////// 163 164 WinPing::WinPing() 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) { 168 169 dll_ = LoadLibraryA(ICMP_DLL_NAME); 170 if (!dll_) { 171 LOG(LERROR) << "LoadLibrary: " << GetLastError(); 172 return; 173 } 174 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(); 180 return; 181 } 182 hping_ = create_(); 183 if (hping_ == INVALID_HANDLE_VALUE) { 184 LOG(LERROR) << "IcmpCreateFile: " << GetLastError(); 185 return; 186 } 187 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(); 193 return; 194 } 195 hping6_ = create6_(); 196 if (hping6_ == INVALID_HANDLE_VALUE) { 197 LOG(LERROR) << "Icmp6CreateFile: " << GetLastError(); 198 } 199 } 200 201 dlen_ = 0; 202 rlen_ = ReplySize(dlen_, AF_INET); 203 data_ = new char[dlen_]; 204 reply_ = new char[rlen_]; 205 206 valid_ = true; 207 } 208 209 WinPing::~WinPing() { 210 if ((hping_ != INVALID_HANDLE_VALUE) && close_) { 211 if (!close_(hping_)) 212 LOG(WARNING) << "IcmpCloseHandle: " << GetLastError(); 213 } 214 if ((hping6_ != INVALID_HANDLE_VALUE) && close_) { 215 if (!close_(hping6_)) { 216 LOG(WARNING) << "Icmp6CloseHandle: " << GetLastError(); 217 } 218 } 219 220 if (dll_) 221 FreeLibrary(dll_); 222 223 delete[] data_; 224 delete[] reply_; 225 } 226 227 WinPing::PingResult WinPing::Ping( 228 IPAddress ip, uint32 data_size, uint32 timeout, uint8 ttl, 229 bool allow_fragments) { 230 231 if (data_size == 0 || timeout == 0 || ttl == 0) { 232 LOG(LERROR) << "IcmpSendEcho: data_size/timeout/ttl is 0."; 233 return PING_INVALID_PARAMS; 234 } 235 236 assert(IsValid()); 237 238 IP_OPTION_INFORMATION ipopt; 239 memset(&ipopt, 0, sizeof(ipopt)); 240 if (!allow_fragments) 241 ipopt.Flags |= IP_FLAG_DF; 242 ipopt.Ttl = ttl; 243 244 uint32 reply_size = ReplySize(data_size, ip.family()); 245 246 if (data_size > dlen_) { 247 delete [] data_; 248 dlen_ = data_size; 249 data_ = new char[dlen_]; 250 memset(data_, 'z', dlen_); 251 } 252 253 if (reply_size > rlen_) { 254 delete [] reply_; 255 rlen_ = reply_size; 256 reply_ = new char[rlen_]; 257 } 258 DWORD result = 0; 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, 270 &src, &dst, 271 data_, int16(data_size), &ipopt, 272 reply_, reply_size, timeout); 273 } 274 if (result == 0) { 275 DWORD error = GetLastError(); 276 if (error == IP_PACKET_TOO_BIG) 277 return PING_TOO_LARGE; 278 if (error == IP_REQ_TIMED_OUT) 279 return PING_TIMEOUT; 280 LOG(LERROR) << "IcmpSendEcho(" << ip.ToSensitiveString() 281 << ", " << data_size << "): " << error; 282 return PING_FAIL; 283 } 284 285 return PING_SUCCESS; 286 } 287 288 ////////////////////////////////////////////////////////////////////// 289 // Microsoft Documenation 290 ////////////////////////////////////////////////////////////////////// 291 // 292 // Routine Name: 293 // 294 // IcmpCreateFile 295 // 296 // Routine Description: 297 // 298 // Opens a handle on which ICMP Echo Requests can be issued. 299 // 300 // Arguments: 301 // 302 // None. 303 // 304 // Return Value: 305 // 306 // An open file handle or INVALID_HANDLE_VALUE. Extended error information 307 // is available by calling GetLastError(). 308 // 309 ////////////////////////////////////////////////////////////////////// 310 // 311 // Routine Name: 312 // 313 // IcmpCloseHandle 314 // 315 // Routine Description: 316 // 317 // Closes a handle opened by ICMPOpenFile. 318 // 319 // Arguments: 320 // 321 // IcmpHandle - The handle to close. 322 // 323 // Return Value: 324 // 325 // TRUE if the handle was closed successfully, otherwise FALSE. Extended 326 // error information is available by calling GetLastError(). 327 // 328 ////////////////////////////////////////////////////////////////////// 329 // 330 // Routine Name: 331 // 332 // IcmpSendEcho 333 // 334 // Routine Description: 335 // 336 // Sends an ICMP Echo request and returns any replies. The 337 // call returns when the timeout has expired or the reply buffer 338 // is filled. 339 // 340 // Arguments: 341 // 342 // IcmpHandle - An open handle returned by ICMPCreateFile. 343 // 344 // DestinationAddress - The destination of the echo request. 345 // 346 // RequestData - A buffer containing the data to send in the 347 // request. 348 // 349 // RequestSize - The number of bytes in the request data buffer. 350 // 351 // RequestOptions - Pointer to the IP header options for the request. 352 // May be NULL. 353 // 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. 362 // 363 // ReplySize - The size in bytes of the reply buffer. 364 // 365 // Timeout - The time in milliseconds to wait for replies. 366 // 367 // Return Value: 368 // 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 372 // GetLastError(). 373 // 374 ////////////////////////////////////////////////////////////////////// 375 376 } // namespace talk_base 377