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 <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