blob: 1b0fa5d82fabd88bf17346ba3f5053b902e07da4 [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:261/*
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
henrike@webrtc.orgf0488722014-05-13 18:00:2613#include <Iphlpapi.h>
14
andresp@webrtc.orgff689be2015-02-12 11:54:2615#include <algorithm>
16
henrike@webrtc.orgf0488722014-05-13 18:00:2617#include "webrtc/base/byteorder.h"
kwiberg22487b22016-09-13 08:17:1018#include "webrtc/base/checks.h"
henrike@webrtc.orgf0488722014-05-13 18:00:2619#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
25namespace rtc {
26
27//////////////////////////////////////////////////////////////////////
28// Found in IPExport.h
29//////////////////////////////////////////////////////////////////////
30
31typedef 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
41typedef 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
122const char * const ICMP_DLL_NAME = "Iphlpapi.dll";
123const char * const ICMP_CREATE_FUNC = "IcmpCreateFile";
124const char * const ICMP_CLOSE_FUNC = "IcmpCloseHandle";
125const char * const ICMP_SEND_FUNC = "IcmpSendEcho";
126const char * const ICMP6_CREATE_FUNC = "Icmp6CreateFile";
henrike@webrtc.orgf0488722014-05-13 18:00:26127const char * const ICMP6_SEND_FUNC = "Icmp6SendEcho2";
128
Peter Boström0c4e06b2015-10-07 10:23:21129inline uint32_t ReplySize(uint32_t data_size, int family) {
henrike@webrtc.orgf0488722014-05-13 18:00:26130 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.
Peter Boström0c4e06b2015-10-07 10:23:21133 return sizeof(ICMP_ECHO_REPLY) + std::max<uint32_t>(8, data_size);
henrike@webrtc.orgf0488722014-05-13 18:00:26134 } 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
148WinPing::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
193WinPing::~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
Peter Boström0c4e06b2015-10-07 10:23:21211WinPing::PingResult WinPing::Ping(IPAddress ip,
212 uint32_t data_size,
213 uint32_t timeout,
214 uint8_t ttl,
215 bool allow_fragments) {
henrike@webrtc.orgf0488722014-05-13 18:00:26216 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
kwiberg22487b22016-09-13 08:17:10221 RTC_DCHECK(IsValid());
henrike@webrtc.orgf0488722014-05-13 18:00:26222
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
Peter Boström0c4e06b2015-10-07 10:23:21229 uint32_t reply_size = ReplySize(data_size, ip.family());
henrike@webrtc.orgf0488722014-05-13 18:00:26230
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) {
Peter Boström0c4e06b2015-10-07 10:23:21245 result = send_(hping_, ip.ipv4_address().S_un.S_addr, data_,
246 uint16_t(data_size), &ipopt, reply_, reply_size, timeout);
henrike@webrtc.orgf0488722014-05-13 18:00:26247 } 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();
Peter Boström0c4e06b2015-10-07 10:23:21253 result = send6_(hping6_, NULL, NULL, NULL, &src, &dst, data_,
254 int16_t(data_size), &ipopt, reply_, reply_size, timeout);
henrike@webrtc.orgf0488722014-05-13 18:00:26255 }
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