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/byteorder.h"
29 #include "talk/base/common.h"
30 #include "talk/base/socketaddress.h"
31 #include "talk/base/winping.h"
32 #include "talk/base/logging.h"
33 #include <cassert>
34 
35 namespace talk_base {
36 
37 //////////////////////////////////////////////////////////////////////
38 // Found in IPExport.h
39 //////////////////////////////////////////////////////////////////////
40 
41 typedef struct icmp_echo_reply {
42     ULONG   Address;            // Replying address
43     ULONG   Status;             // Reply IP_STATUS
44     ULONG   RoundTripTime;      // RTT in milliseconds
45     USHORT  DataSize;           // Reply data size in bytes
46     USHORT  Reserved;           // Reserved for system use
47     PVOID   Data;               // Pointer to the reply data
48     struct ip_option_information Options; // Reply options
49 } ICMP_ECHO_REPLY, * PICMP_ECHO_REPLY;
50 
51 //
52 // IP_STATUS codes returned from IP APIs
53 //
54 
55 #define IP_STATUS_BASE              11000
56 
57 #define IP_SUCCESS                  0
58 #define IP_BUF_TOO_SMALL            (IP_STATUS_BASE + 1)
59 #define IP_DEST_NET_UNREACHABLE     (IP_STATUS_BASE + 2)
60 #define IP_DEST_HOST_UNREACHABLE    (IP_STATUS_BASE + 3)
61 #define IP_DEST_PROT_UNREACHABLE    (IP_STATUS_BASE + 4)
62 #define IP_DEST_PORT_UNREACHABLE    (IP_STATUS_BASE + 5)
63 #define IP_NO_RESOURCES             (IP_STATUS_BASE + 6)
64 #define IP_BAD_OPTION               (IP_STATUS_BASE + 7)
65 #define IP_HW_ERROR                 (IP_STATUS_BASE + 8)
66 #define IP_PACKET_TOO_BIG           (IP_STATUS_BASE + 9)
67 #define IP_REQ_TIMED_OUT            (IP_STATUS_BASE + 10)
68 #define IP_BAD_REQ                  (IP_STATUS_BASE + 11)
69 #define IP_BAD_ROUTE                (IP_STATUS_BASE + 12)
70 #define IP_TTL_EXPIRED_TRANSIT      (IP_STATUS_BASE + 13)
71 #define IP_TTL_EXPIRED_REASSEM      (IP_STATUS_BASE + 14)
72 #define IP_PARAM_PROBLEM            (IP_STATUS_BASE + 15)
73 #define IP_SOURCE_QUENCH            (IP_STATUS_BASE + 16)
74 #define IP_OPTION_TOO_BIG           (IP_STATUS_BASE + 17)
75 #define IP_BAD_DESTINATION          (IP_STATUS_BASE + 18)
76 
77 #define IP_ADDR_DELETED             (IP_STATUS_BASE + 19)
78 #define IP_SPEC_MTU_CHANGE          (IP_STATUS_BASE + 20)
79 #define IP_MTU_CHANGE               (IP_STATUS_BASE + 21)
80 #define IP_UNLOAD                   (IP_STATUS_BASE + 22)
81 #define IP_ADDR_ADDED               (IP_STATUS_BASE + 23)
82 #define IP_MEDIA_CONNECT            (IP_STATUS_BASE + 24)
83 #define IP_MEDIA_DISCONNECT         (IP_STATUS_BASE + 25)
84 #define IP_BIND_ADAPTER             (IP_STATUS_BASE + 26)
85 #define IP_UNBIND_ADAPTER           (IP_STATUS_BASE + 27)
86 #define IP_DEVICE_DOES_NOT_EXIST    (IP_STATUS_BASE + 28)
87 #define IP_DUPLICATE_ADDRESS        (IP_STATUS_BASE + 29)
88 #define IP_INTERFACE_METRIC_CHANGE  (IP_STATUS_BASE + 30)
89 #define IP_RECONFIG_SECFLTR         (IP_STATUS_BASE + 31)
90 #define IP_NEGOTIATING_IPSEC        (IP_STATUS_BASE + 32)
91 #define IP_INTERFACE_WOL_CAPABILITY_CHANGE  (IP_STATUS_BASE + 33)
92 #define IP_DUPLICATE_IPADD          (IP_STATUS_BASE + 34)
93 
94 #define IP_GENERAL_FAILURE          (IP_STATUS_BASE + 50)
95 #define MAX_IP_STATUS               IP_GENERAL_FAILURE
96 #define IP_PENDING                  (IP_STATUS_BASE + 255)
97 
98 //
99 // Values used in the IP header Flags field.
100 //
101 #define IP_FLAG_DF      0x2         // Don't fragment this packet.
102 
103 //
104 // Supported IP Option Types.
105 //
106 // These types define the options which may be used in the OptionsData field
107 // of the ip_option_information structure.  See RFC 791 for a complete
108 // description of each.
109 //
110 #define IP_OPT_EOL      0          // End of list option
111 #define IP_OPT_NOP      1          // No operation
112 #define IP_OPT_SECURITY 0x82       // Security option
113 #define IP_OPT_LSRR     0x83       // Loose source route
114 #define IP_OPT_SSRR     0x89       // Strict source route
115 #define IP_OPT_RR       0x7        // Record route
116 #define IP_OPT_TS       0x44       // Timestamp
117 #define IP_OPT_SID      0x88       // Stream ID (obsolete)
118 #define IP_OPT_ROUTER_ALERT 0x94  // Router Alert Option
119 
120 #define MAX_OPT_SIZE    40         // Maximum length of IP options in bytes
121 
122 //////////////////////////////////////////////////////////////////////
123 // Global Constants and Types
124 //////////////////////////////////////////////////////////////////////
125 
126 const char * const ICMP_DLL_NAME = "icmp.dll";
127 const char * const ICMP_CREATE_FUNC = "IcmpCreateFile";
128 const char * const ICMP_CLOSE_FUNC = "IcmpCloseHandle";
129 const char * const ICMP_SEND_FUNC = "IcmpSendEcho";
130 
ReplySize(uint32 data_size)131 inline uint32 ReplySize(uint32 data_size) {
132   // A ping error message is 8 bytes long, so make sure we allow for at least
133   // 8 bytes of reply data.
134   return sizeof(ICMP_ECHO_REPLY) + talk_base::_max<uint32>(8, data_size);
135 }
136 
137 //////////////////////////////////////////////////////////////////////
138 // WinPing
139 //////////////////////////////////////////////////////////////////////
140 
WinPing()141 WinPing::WinPing()
142     : dll_(0), hping_(INVALID_HANDLE_VALUE), create_(0), close_(0), send_(0),
143       data_(0), dlen_(0), reply_(0), rlen_(0), valid_(false) {
144 
145   dll_ = LoadLibraryA(ICMP_DLL_NAME);
146   if (!dll_) {
147     LOG(LERROR) << "LoadLibrary: " << GetLastError();
148     return;
149   }
150 
151   create_ = (PIcmpCreateFile) GetProcAddress(dll_, ICMP_CREATE_FUNC);
152   close_ = (PIcmpCloseHandle) GetProcAddress(dll_, ICMP_CLOSE_FUNC);
153   send_ = (PIcmpSendEcho) GetProcAddress(dll_, ICMP_SEND_FUNC);
154   if (!create_ || !close_ || !send_) {
155     LOG(LERROR) << "GetProcAddress(ICMP_*): " << GetLastError();
156     return;
157   }
158 
159   hping_ = create_();
160   if (hping_ == INVALID_HANDLE_VALUE) {
161     LOG(LERROR) << "IcmpCreateFile: " << GetLastError();
162     return;
163   }
164 
165   dlen_ = 0;
166   rlen_ = ReplySize(dlen_);
167   data_ = new char[dlen_];
168   reply_ = new char[rlen_];
169 
170   valid_ = true;
171 }
172 
~WinPing()173 WinPing::~WinPing() {
174   if (dll_)
175     FreeLibrary(dll_);
176 
177   if ((hping_ != INVALID_HANDLE_VALUE) && close_) {
178     if (!close_(hping_))
179       LOG(WARNING) << "IcmpCloseHandle: " << GetLastError();
180   }
181 
182   delete[] data_;
183   delete[] reply_;
184 }
185 
Ping(uint32 ip,uint32 data_size,uint32 timeout,uint8 ttl,bool allow_fragments)186 WinPing::PingResult WinPing::Ping(
187     uint32 ip, uint32 data_size, uint32 timeout, uint8 ttl,
188     bool allow_fragments) {
189 
190   assert(IsValid());
191 
192   IP_OPTION_INFORMATION ipopt;
193   memset(&ipopt, 0, sizeof(ipopt));
194   if (!allow_fragments)
195     ipopt.Flags |= IP_FLAG_DF;
196   ipopt.Ttl = ttl;
197 
198   uint32 reply_size = ReplySize(data_size);
199 
200   if (data_size > dlen_) {
201     delete [] data_;
202     dlen_ = data_size;
203     data_ = new char[dlen_];
204     memset(data_, 'z', dlen_);
205   }
206 
207   if (reply_size > rlen_) {
208     delete [] reply_;
209     rlen_ = reply_size;
210     reply_ = new char[rlen_];
211   }
212 
213   DWORD result = send_(hping_, talk_base::HostToNetwork32(ip),
214                        data_, uint16(data_size), &ipopt,
215                        reply_, reply_size, timeout);
216   if (result == 0) {
217     long error = GetLastError();
218     if (error == IP_PACKET_TOO_BIG)
219       return PING_TOO_LARGE;
220     if (error == IP_REQ_TIMED_OUT)
221       return PING_TIMEOUT;
222     LOG(LERROR) << "IcmpSendEcho(" << talk_base::SocketAddress::IPToString(ip)
223                 << ", " << data_size << "): " << error;
224     return PING_FAIL;
225   }
226 
227   return PING_SUCCESS;
228 }
229 
230 //////////////////////////////////////////////////////////////////////
231 // Microsoft Documenation
232 //////////////////////////////////////////////////////////////////////
233 //
234 // Routine Name:
235 //
236 //     IcmpCreateFile
237 //
238 // Routine Description:
239 //
240 //     Opens a handle on which ICMP Echo Requests can be issued.
241 //
242 // Arguments:
243 //
244 //     None.
245 //
246 // Return Value:
247 //
248 //     An open file handle or INVALID_HANDLE_VALUE. Extended error information
249 //     is available by calling GetLastError().
250 //
251 //////////////////////////////////////////////////////////////////////
252 //
253 // Routine Name:
254 //
255 //     IcmpCloseHandle
256 //
257 // Routine Description:
258 //
259 //     Closes a handle opened by ICMPOpenFile.
260 //
261 // Arguments:
262 //
263 //     IcmpHandle  - The handle to close.
264 //
265 // Return Value:
266 //
267 //     TRUE if the handle was closed successfully, otherwise FALSE. Extended
268 //     error information is available by calling GetLastError().
269 //
270 //////////////////////////////////////////////////////////////////////
271 //
272 // Routine Name:
273 //
274 //     IcmpSendEcho
275 //
276 // Routine Description:
277 //
278 //     Sends an ICMP Echo request and returns any replies. The
279 //     call returns when the timeout has expired or the reply buffer
280 //     is filled.
281 //
282 // Arguments:
283 //
284 //     IcmpHandle           - An open handle returned by ICMPCreateFile.
285 //
286 //     DestinationAddress   - The destination of the echo request.
287 //
288 //     RequestData          - A buffer containing the data to send in the
289 //                            request.
290 //
291 //     RequestSize          - The number of bytes in the request data buffer.
292 //
293 //     RequestOptions       - Pointer to the IP header options for the request.
294 //                            May be NULL.
295 //
296 //     ReplyBuffer          - A buffer to hold any replies to the request.
297 //                            On return, the buffer will contain an array of
298 //                            ICMP_ECHO_REPLY structures followed by the
299 //                            options and data for the replies. The buffer
300 //                            should be large enough to hold at least one
301 //                            ICMP_ECHO_REPLY structure plus
302 //                            MAX(RequestSize, 8) bytes of data since an ICMP
303 //                            error message contains 8 bytes of data.
304 //
305 //     ReplySize            - The size in bytes of the reply buffer.
306 //
307 //     Timeout              - The time in milliseconds to wait for replies.
308 //
309 // Return Value:
310 //
311 //     Returns the number of ICMP_ECHO_REPLY structures stored in ReplyBuffer.
312 //     The status of each reply is contained in the structure. If the return
313 //     value is zero, extended error information is available via
314 //     GetLastError().
315 //
316 //////////////////////////////////////////////////////////////////////
317 
318 } // namespace talk_base
319