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