• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ReplySize(uint32 data_size,int family)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 
WinPing()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 
~WinPing()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 
Ping(IPAddress ip,uint32 data_size,uint32 timeout,uint8 ttl,bool allow_fragments)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