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