Home | History | Annotate | Download | only in base
      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 
     23 namespace rtc {
     24 
     25 //////////////////////////////////////////////////////////////////////
     26 // Found in IPExport.h
     27 //////////////////////////////////////////////////////////////////////
     28 
     29 typedef 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 
     39 typedef 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 
    120 const char * const ICMP_DLL_NAME = "Iphlpapi.dll";
    121 const char * const ICMP_CREATE_FUNC = "IcmpCreateFile";
    122 const char * const ICMP_CLOSE_FUNC = "IcmpCloseHandle";
    123 const char * const ICMP_SEND_FUNC = "IcmpSendEcho";
    124 const char * const ICMP6_CREATE_FUNC = "Icmp6CreateFile";
    125 const char * const ICMP6_CLOSE_FUNC = "Icmp6CloseHandle";
    126 const char * const ICMP6_SEND_FUNC = "Icmp6SendEcho2";
    127 
    128 inline 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 
    147 WinPing::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 
    192 WinPing::~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 
    210 WinPing::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