Home | History | Annotate | Download | only in base
      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