OLD | NEW |
| (Empty) |
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 <Iphlpapi.h> | |
14 | |
15 #include <algorithm> | |
16 | |
17 #include "webrtc/base/byteorder.h" | |
18 #include "webrtc/base/checks.h" | |
19 #include "webrtc/base/ipaddress.h" | |
20 #include "webrtc/base/logging.h" | |
21 #include "webrtc/base/nethelpers.h" | |
22 #include "webrtc/base/socketaddress.h" | |
23 | |
24 namespace rtc { | |
25 | |
26 ////////////////////////////////////////////////////////////////////// | |
27 // Found in IPExport.h | |
28 ////////////////////////////////////////////////////////////////////// | |
29 | |
30 typedef struct icmp_echo_reply { | |
31 ULONG Address; // Replying address | |
32 ULONG Status; // Reply IP_STATUS | |
33 ULONG RoundTripTime; // RTT in milliseconds | |
34 USHORT DataSize; // Reply data size in bytes | |
35 USHORT Reserved; // Reserved for system use | |
36 PVOID Data; // Pointer to the reply data | |
37 struct ip_option_information Options; // Reply options | |
38 } ICMP_ECHO_REPLY, * PICMP_ECHO_REPLY; | |
39 | |
40 typedef struct icmpv6_echo_reply_lh { | |
41 sockaddr_in6 Address; | |
42 ULONG Status; | |
43 unsigned int RoundTripTime; | |
44 } ICMPV6_ECHO_REPLY, *PICMPV6_ECHO_REPLY; | |
45 | |
46 // | |
47 // IP_STATUS codes returned from IP APIs | |
48 // | |
49 | |
50 #define IP_STATUS_BASE 11000 | |
51 | |
52 #define IP_SUCCESS 0 | |
53 #define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1) | |
54 #define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2) | |
55 #define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3) | |
56 #define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4) | |
57 #define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5) | |
58 #define IP_NO_RESOURCES (IP_STATUS_BASE + 6) | |
59 #define IP_BAD_OPTION (IP_STATUS_BASE + 7) | |
60 #define IP_HW_ERROR (IP_STATUS_BASE + 8) | |
61 #define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9) | |
62 #define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10) | |
63 #define IP_BAD_REQ (IP_STATUS_BASE + 11) | |
64 #define IP_BAD_ROUTE (IP_STATUS_BASE + 12) | |
65 #define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13) | |
66 #define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14) | |
67 #define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15) | |
68 #define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16) | |
69 #define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17) | |
70 #define IP_BAD_DESTINATION (IP_STATUS_BASE + 18) | |
71 | |
72 #define IP_ADDR_DELETED (IP_STATUS_BASE + 19) | |
73 #define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20) | |
74 #define IP_MTU_CHANGE (IP_STATUS_BASE + 21) | |
75 #define IP_UNLOAD (IP_STATUS_BASE + 22) | |
76 #define IP_ADDR_ADDED (IP_STATUS_BASE + 23) | |
77 #define IP_MEDIA_CONNECT (IP_STATUS_BASE + 24) | |
78 #define IP_MEDIA_DISCONNECT (IP_STATUS_BASE + 25) | |
79 #define IP_BIND_ADAPTER (IP_STATUS_BASE + 26) | |
80 #define IP_UNBIND_ADAPTER (IP_STATUS_BASE + 27) | |
81 #define IP_DEVICE_DOES_NOT_EXIST (IP_STATUS_BASE + 28) | |
82 #define IP_DUPLICATE_ADDRESS (IP_STATUS_BASE + 29) | |
83 #define IP_INTERFACE_METRIC_CHANGE (IP_STATUS_BASE + 30) | |
84 #define IP_RECONFIG_SECFLTR (IP_STATUS_BASE + 31) | |
85 #define IP_NEGOTIATING_IPSEC (IP_STATUS_BASE + 32) | |
86 #define IP_INTERFACE_WOL_CAPABILITY_CHANGE (IP_STATUS_BASE + 33) | |
87 #define IP_DUPLICATE_IPADD (IP_STATUS_BASE + 34) | |
88 | |
89 #define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50) | |
90 #define MAX_IP_STATUS IP_GENERAL_FAILURE | |
91 #define IP_PENDING (IP_STATUS_BASE + 255) | |
92 | |
93 // | |
94 // Values used in the IP header Flags field. | |
95 // | |
96 #define IP_FLAG_DF 0x2 // Don't fragment this packet. | |
97 | |
98 // | |
99 // Supported IP Option Types. | |
100 // | |
101 // These types define the options which may be used in the OptionsData field | |
102 // of the ip_option_information structure. See RFC 791 for a complete | |
103 // description of each. | |
104 // | |
105 #define IP_OPT_EOL 0 // End of list option | |
106 #define IP_OPT_NOP 1 // No operation | |
107 #define IP_OPT_SECURITY 0x82 // Security option | |
108 #define IP_OPT_LSRR 0x83 // Loose source route | |
109 #define IP_OPT_SSRR 0x89 // Strict source route | |
110 #define IP_OPT_RR 0x7 // Record route | |
111 #define IP_OPT_TS 0x44 // Timestamp | |
112 #define IP_OPT_SID 0x88 // Stream ID (obsolete) | |
113 #define IP_OPT_ROUTER_ALERT 0x94 // Router Alert Option | |
114 | |
115 #define MAX_OPT_SIZE 40 // Maximum length of IP options in bytes | |
116 | |
117 ////////////////////////////////////////////////////////////////////// | |
118 // Global Constants and Types | |
119 ////////////////////////////////////////////////////////////////////// | |
120 | |
121 const char * const ICMP_DLL_NAME = "Iphlpapi.dll"; | |
122 const char * const ICMP_CREATE_FUNC = "IcmpCreateFile"; | |
123 const char * const ICMP_CLOSE_FUNC = "IcmpCloseHandle"; | |
124 const char * const ICMP_SEND_FUNC = "IcmpSendEcho"; | |
125 const char * const ICMP6_CREATE_FUNC = "Icmp6CreateFile"; | |
126 const char * const ICMP6_SEND_FUNC = "Icmp6SendEcho2"; | |
127 | |
128 inline uint32_t ReplySize(uint32_t 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) + std::max<uint32_t>(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(IPAddress ip, | |
211 uint32_t data_size, | |
212 uint32_t timeout, | |
213 uint8_t ttl, | |
214 bool allow_fragments) { | |
215 if (data_size == 0 || timeout == 0 || ttl == 0) { | |
216 LOG(LERROR) << "IcmpSendEcho: data_size/timeout/ttl is 0."; | |
217 return PING_INVALID_PARAMS; | |
218 } | |
219 | |
220 RTC_DCHECK(IsValid()); | |
221 | |
222 IP_OPTION_INFORMATION ipopt; | |
223 memset(&ipopt, 0, sizeof(ipopt)); | |
224 if (!allow_fragments) | |
225 ipopt.Flags |= IP_FLAG_DF; | |
226 ipopt.Ttl = ttl; | |
227 | |
228 uint32_t reply_size = ReplySize(data_size, ip.family()); | |
229 | |
230 if (data_size > dlen_) { | |
231 delete [] data_; | |
232 dlen_ = data_size; | |
233 data_ = new char[dlen_]; | |
234 memset(data_, 'z', dlen_); | |
235 } | |
236 | |
237 if (reply_size > rlen_) { | |
238 delete [] reply_; | |
239 rlen_ = reply_size; | |
240 reply_ = new char[rlen_]; | |
241 } | |
242 DWORD result = 0; | |
243 if (ip.family() == AF_INET) { | |
244 result = send_(hping_, ip.ipv4_address().S_un.S_addr, data_, | |
245 uint16_t(data_size), &ipopt, 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_, nullptr, nullptr, nullptr, &src, &dst, data_, | |
253 int16_t(data_size), &ipopt, reply_, reply_size, timeout); | |
254 } | |
255 if (result == 0) { | |
256 DWORD error = GetLastError(); | |
257 if (error == IP_PACKET_TOO_BIG) | |
258 return PING_TOO_LARGE; | |
259 if (error == IP_REQ_TIMED_OUT) | |
260 return PING_TIMEOUT; | |
261 LOG(LERROR) << "IcmpSendEcho(" << ip.ToSensitiveString() | |
262 << ", " << data_size << "): " << error; | |
263 return PING_FAIL; | |
264 } | |
265 | |
266 return PING_SUCCESS; | |
267 } | |
268 | |
269 ////////////////////////////////////////////////////////////////////// | |
270 // Microsoft Documenation | |
271 ////////////////////////////////////////////////////////////////////// | |
272 // | |
273 // Routine Name: | |
274 // | |
275 // IcmpCreateFile | |
276 // | |
277 // Routine Description: | |
278 // | |
279 // Opens a handle on which ICMP Echo Requests can be issued. | |
280 // | |
281 // Arguments: | |
282 // | |
283 // None. | |
284 // | |
285 // Return Value: | |
286 // | |
287 // An open file handle or INVALID_HANDLE_VALUE. Extended error information | |
288 // is available by calling GetLastError(). | |
289 // | |
290 ////////////////////////////////////////////////////////////////////// | |
291 // | |
292 // Routine Name: | |
293 // | |
294 // IcmpCloseHandle | |
295 // | |
296 // Routine Description: | |
297 // | |
298 // Closes a handle opened by ICMPOpenFile. | |
299 // | |
300 // Arguments: | |
301 // | |
302 // IcmpHandle - The handle to close. | |
303 // | |
304 // Return Value: | |
305 // | |
306 // TRUE if the handle was closed successfully, otherwise FALSE. Extended | |
307 // error information is available by calling GetLastError(). | |
308 // | |
309 ////////////////////////////////////////////////////////////////////// | |
310 // | |
311 // Routine Name: | |
312 // | |
313 // IcmpSendEcho | |
314 // | |
315 // Routine Description: | |
316 // | |
317 // Sends an ICMP Echo request and returns any replies. The | |
318 // call returns when the timeout has expired or the reply buffer | |
319 // is filled. | |
320 // | |
321 // Arguments: | |
322 // | |
323 // IcmpHandle - An open handle returned by ICMPCreateFile. | |
324 // | |
325 // DestinationAddress - The destination of the echo request. | |
326 // | |
327 // RequestData - A buffer containing the data to send in the | |
328 // request. | |
329 // | |
330 // RequestSize - The number of bytes in the request data buffer. | |
331 // | |
332 // RequestOptions - Pointer to the IP header options for the request. | |
333 // May be null. | |
334 // | |
335 // ReplyBuffer - A buffer to hold any replies to the request. | |
336 // On return, the buffer will contain an array of | |
337 // ICMP_ECHO_REPLY structures followed by the | |
338 // options and data for the replies. The buffer | |
339 // should be large enough to hold at least one | |
340 // ICMP_ECHO_REPLY structure plus | |
341 // MAX(RequestSize, 8) bytes of data since an ICMP | |
342 // error message contains 8 bytes of data. | |
343 // | |
344 // ReplySize - The size in bytes of the reply buffer. | |
345 // | |
346 // Timeout - The time in milliseconds to wait for replies. | |
347 // | |
348 // Return Value: | |
349 // | |
350 // Returns the number of ICMP_ECHO_REPLY structures stored in ReplyBuffer. | |
351 // The status of each reply is contained in the structure. If the return | |
352 // value is zero, extended error information is available via | |
353 // GetLastError(). | |
354 // | |
355 ////////////////////////////////////////////////////////////////////// | |
356 | |
357 } // namespace rtc | |
OLD | NEW |