-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathping.cpp
More file actions
420 lines (376 loc) · 21.7 KB
/
Copy pathping.cpp
File metadata and controls
420 lines (376 loc) · 21.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
/*
Module : ping.cpp
Purpose: Implementation for a C++ wrapper class to encapsulate "Ping" on Windows
Created: PJN / 10-06-1998
History: PJN / 23-06-1998 1. Now code can be compiled to use Winsock2 calls
instead of using the ICMP.DLL. This gives another of
advantages:
i. Your using a API that MS has promised to continue to support.
ii. Internally the class calls QueryPerformanceCounter meaning that
you will get the highest resolution RTT's possible.
2. Also did a general tidy up of the code
3. Changed default timeout to 1 second
PJN / 30-07-1998 1. Can now use both Winsock 2 calls and ICMP style
calls at the same time with the use of 2 preprocessor directives
2. sample program now use generic entry point _tmain
3. Got rid of a 2 level 4 compiler warning
4. Fixed a problem with the cleanup of ICMP dll usage
5. Tidied up and optimized the usage of static variables
PJN / 01-03-2000 1. Fixed a problem where I was incorrectly overwriting memory in
the function FillIcmpData
PJN / 15-07-2001 1. Updated copyright information
2. Code now automatically pulls in the Winsock 2 libraries as and when needed
PJN / 12-01-2002 1. Updated copyright information
2. Now initializes Winsock 2.0 instead of Winsock 2.1. Thanks to Lev Elbert
for spotting this problem.
PJN / 24-02-2002 1. Winsock 2 code path now allows you to bind the socket to a specific NIC.
This can prove useful when you want to run a targeted ping request on a
multihomed machine.
2. Tidied up the code in the sample application
3. Provide a complete set of command line options for the sample app. It
now provides almost a 1 - 1 implementation of the build in Ping options.
4. Completed reworked and recoded all the CPing code.
5. Client code is now responsible for initializing Winsock instead of
having it embedded in the CPing code
PJN / 05-05-2002 1. Now includes a EchoReplyStatus in CPingReply structure if CPING_NO_ICMP is not
defined
PJN / 21-11-2002 1. Fixed an issue in the calculation of the packet size when sending using
raw sockets.
2. Default sample now uses a default TTL of 30
3. Changes the nPacketSize parameter to the Ping method to be nDataSize which is
what it really is.
4. Reviewed all the TRACE messages for correctness
5. Made all the helper functions used by the code member functions of the class
6. General tidy up of the various defines
7. Winsock 2 version of ping now allows you to specify the sequence number of the
packet. This allows the ping to exclude packets from the same process id which
are being generated by for example another thread.
8. Fixed a bug in the call to recvfrom. The receive buffer size was not being
specified correctly.
9. ICMP response packets from other processes are now ignored when waiting for
an ICMP response
10. ICMP response packets from the same process but which have an incorrect
sequence number are now ignored when waiting for an ICMP response.
11. Fix a resource leak of a socket when unexpected failures occur in the
CPing::PingUsingWinsock function
PJN / 10-03-2003 1. Fixed a bug where using PingUsingWinsock and setting the data size greater
than 1004 appears to cause Winsock error 10040 - The datagram is too large to
fit into the buffer and is truncated. Thanks to Paul Golightly for reported this
and provided the fix.
PJN / 13-03-2003 1. Fixed a bug in CPing::IsSocketReadible in the setup of the timeval structure.
Thanks to "InBloom Support" for reporting this.
PJN / 23-11-2003 1. Fixed a level 4 warning in VC 6 in the function CPing::PingUsingWinsock.
PJN / 12-04-2004 1. Fixed a bug in the declaration of h_len and version in the IP_HEADER structure.
Also made the source and destination IP addresses longs instead of ints to avoid
any porting problems. Thanks to Yuh-Rong Leu for reporting this problem.
2. Fixed a bug in PingUsingWinsock when a timeout occurs and it returns TRUE.
Thanks to Yuh-Rong Leu for reporting this problem.
3. Updated the documentation to refer to using the value from GetCurrentThreadId()
for the nSequenceNumber parameter when calling the PingUsingWinsock function.
Again thanks to Yuh-Rong Leu for reporting this issue.
4. Fixed an issue in DecodeResponse where if bTryAgain was set to TRUE, the return
value was incorrectly also being set to TRUE. Thanks to Yuh-Rong Leu for reporting
this issue.
PJN / 11-06-2005 1. Reviewed all TRACE statements for correctness. Thanks to "Grenal" for reporting
this issue.
2. Checked all premature return points in the codebase to ensure the last error
value is preserved.
3. Fixed an unreferenced variable in DecodeResponse function when code is compiled
with Visual Studio .NET 2003
PJN / 31-07-2005 1. When sending a ping, the allowable size of the data in the packet
can now be anywhere from 0 to 65500. Previously sending a ping using Winsock was
limited to a data size of 1024 and when using ICMP.dll, the size was limited to 255.
Thanks to Leon Luu for reporting this issue.
PJN / 31-07-2005 1. Now the PingUsingICMP method using the versions provided in the IP Helper dll
namely "Iphlpapi.dll" before falling back to "ICMP.dll".
2. Tidied up the various ICMP defines by making them part of the _CPING_ICMP class
PJN / 10-07-2006 1. Updated copyright details
2. The code now requires the Platform SDK if compiled using VC 6.
3. Integrated the functionality of _CPING_ICMP class into CPing
4. Optimized CPingReply constructor code
5. Code now uses newer C++ style casts instead of C style casts.
6. Addition of CPING_EXT_CLASS macro to allow the code to be easily added to an
extension dll.
7. Code now uses newer C++ style casts instead of C style casts.
8. Updated the code to clean compile on VC 2005.
9. Updated documentation to use the same style as the web site.
PJN / 03-10-2007 1. Updated copyright details.
2. Fixed small layout problem in sample app when it displays it could not ping
the specified host.
3. Updated the sample app to compile cleanly in VC 2005
PJN / 07-06-2008 1. Updated copyright details
2. Updated the sample app to clean compile on VC 2008
3. Code now compiles cleanly using Code Analysis (/analyze)
4. The code has now been updated to support VC 2005 or later only
5. Replaced all calls to ZeroMemory with memset
6. Code now uses ATL::CHeapPtr for managed raw buffers
7. The "Bind", "FillIcmpData", "DecodeResponse" and "GenerateIPChecksum" methods
have been made virtual. This for example allows client code to decide what data
gets put into ICMP request packets. Thanks to Dinci Glas for prompting this
update.
PJN / 02-05-2016 1. Updated copyright details.
2. Updated the sample project settings to more modern default values.
3. Updated the code to compile cleanly on VC 2010 - 2015.
4. Replaced all calls to TRACE with ATLTRACE
5. Code no longer uses LoadLibrary without an absolute path when loading Iphlpapi / ICMP
dlls. This avoids DLL planting security issues.
6. Removed code from CPing::DecodeResponse which checks i_type & i_id header values.
7. Made class MFC independent. Now the code only depends on ATL.
8. Removed code from CPing::PingUsingICMP which parses the response.
9. Added support for IPv6 in addition to IPv4. This means that the minimum supported
version of VC which supports CPing is now VC 2008. The sample now ships with a VC 2008
solution.
10. Code now supports IcmpSendEcho2Ex which supports IPv4 source address binding
11. Removed the PingUsingWinsock method from the class as all the ping functionality
can now be achieved via the Icmp* Windows API calls. This means that the CPING_NO_ICMP
and CPING_NO_WINSOCK2 preprocessor values are defunct and have been removed.
12. Added SAL annotations to all the code.
13. Added support for the IP_FLAG_REVERSE IP_OPTION_INFORMATION flag.
14. Addition of GetICMP_ECHO_REPLY and GetICMPV6_ECHO_REPLY methods to the CPingReply
class which allow access to the raw ICMP echo reply structure.
PJN / 06-05-2016 1. Split CPingReply class into two separate CPingReplyv4 and CPingReplyv6 classes.
PJN / 26-12-2017 1. Updated copyright details.
2. Replaced CString::operator LPC*STR() calls throughout the codebase with
CString::GetString calls
3. Replaced NULL throughout the codebase with nullptr. This means that the minimum
requirement for the code is now VC 2010.
4. Removed all the code which uses GetProcAddress to access the functions in
Iphlpapi.dll. Instead now the code implicitly links to Iphlpapi.dll.
PJN / 03-11-2018 1. Updated copyright details.
2. Fixed a number of C++ core guidelines compiler warnings. These changes mean that the
code will now only compile on VC 2017 or later.
3. Removed all code which used GetProcAddress in the sample app.
PJN / 03-05-2019 1. Updated copyright details.
2. Updated the code to clean compile on VC 2019
PJN / 10-11-2019 1. Updated initialization of various structs to use C++ 11 list initialization
2. Replaced BOOL with bool in a number of places throughout the codebase
PJN / 29-12-2019 1. Fixed various Clang-Tidy static code analysis warnings in the code.
PJN / 28-03-2020 1. Updated copyright details.
2. Fixed more Clang-Tidy static code analysis warnings in the code.
PJN / 30-03-2021 1. Updated copyright details.
2. Updated the PingUsingICMPv4 and PingUsingICMPv6 methods to preserve the last error
value across calls to IcmpCloseHandle. Thanks to Geoff Shapiro for reporting this issue.
PJN / 10-12-2021 1. Made a number of methods nodiscard.
2. Replaced ATL::CHeapPtr<BYTE> with std::vector<BYTE>
PJN / 18-03-2022 1. Updated copyright details.
2. Updated the code to use C++ uniform initialization for all variable declarations.
Copyright (c) 1998 - 2022 by PJ Naughter (Web: www.naughter.com, Email: pjna@naughter.com)
All rights reserved.
Copyright / Usage Details:
You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise)
when your product is released in binary form. You are allowed to modify the source code in any way you want
except you cannot modify the copyright details at the top of each module. If you want to distribute source
code with your application, then you are only allowed to distribute versions released by the author. This is
to maintain a single distribution point for the source code.
*/
///////////////////////////////// Includes ////////////////////////////////////
#include "pch.h"
#include "ping.h"
///////////////////////////////// Macros / Defines ////////////////////////////
#pragma comment(lib, "Iphlpapi.lib")
///////////////////////////////// Implementation //////////////////////////////
CPingReplyv4::CPingReplyv4() noexcept : Address{},
RTT{ 0 },
EchoReplyStatus{ 0 }
{
}
const ICMP_ECHO_REPLY* CPingReplyv4::GetICMP_ECHO_REPLY() noexcept
{
#pragma warning(suppress: 26490)
auto pEchoReply{ reinterpret_cast<const ICMP_ECHO_REPLY*>(Reply.data()) };
return pEchoReply;
}
CPingReplyv6::CPingReplyv6() noexcept : Address{},
RTT{ 0 },
EchoReplyStatus{ 0 }
{
}
const ICMPV6_ECHO_REPLY* CPingReplyv6::GetICMPV6_ECHO_REPLY() noexcept
{
#pragma warning(suppress: 26490)
auto pEchoReply{ reinterpret_cast<const ICMPV6_ECHO_REPLY*>(Reply.data()) };
return pEchoReply;
}
//Fill up the ICMP packet with defined values
#pragma warning(suppress: 26440)
void CPing::FillIcmpData(_Out_writes_bytes_(dwRequestSize) BYTE* pRequestData, _In_ DWORD dwRequestSize) const
{
memset(pRequestData, 'E', dwRequestSize);
}
bool CPing::PingUsingICMPv4(_In_z_ LPCTSTR pszHostName, _Inout_ CPingReplyv4& pr, _In_ UCHAR nTTL, _In_ DWORD dwTimeout, _In_ WORD wDataSize, _In_ UCHAR nTOS, _In_ bool bDontFragment, _In_ bool bFlagReverse, _In_opt_z_ LPCTSTR pszLocalBoundAddress) const
{
//Do the address lookup
ATL::CSocketAddr lookup;
int nError{ lookup.FindAddr(pszHostName, 0, 0, AF_INET, 0, 0) };
if (nError != 0)
{
SetLastError(nError);
return false;
}
//Note we always use the first address returned from ATL::CSocketAddr::FindAddr. If you want to use a different
//Address then do the lookup yourself and pass in the correct value in the "pszHostName" parameter
const ADDRINFOT* pAddress{ lookup.GetAddrInfoList() };
#pragma warning(suppress: 26477)
ATLASSUME(pAddress != nullptr);
#pragma warning(suppress: 26477)
ATLASSERT(pAddress->ai_family == AF_INET);
#pragma warning(suppress: 26489 26490)
auto pDestAddress{ reinterpret_cast<const sockaddr_in*>(pAddress->ai_addr) };
//Create the ICMP handle
HANDLE hIP{ IcmpCreateFile() };
if (hIP == INVALID_HANDLE_VALUE)
return false;
//Set up the option info structure
IP_OPTION_INFORMATION OptionInfo{};
OptionInfo.Ttl = nTTL;
OptionInfo.Tos = nTOS;
if (bDontFragment)
OptionInfo.Flags = IP_FLAG_DF;
if (bFlagReverse)
OptionInfo.Flags |= IP_FLAG_REVERSE;
//Set up the data which will be sent
std::vector<BYTE> sendBuf{ wDataSize, std::allocator<BYTE>{} };
FillIcmpData(sendBuf.data(), wDataSize);
//Do the actual Ping
#pragma warning(suppress: 26472)
const DWORD dwReplySize{ static_cast<DWORD>(sizeof(ICMP_ECHO_REPLY) + wDataSize + 8) };
pr.Reply.resize(dwReplySize);
//Bind to the local address if need be
IPAddr srcAddress{ 0 };
bool bBindSourceIPAddress{ false };
if ((pszLocalBoundAddress != nullptr) && _tcslen(pszLocalBoundAddress))
{
bBindSourceIPAddress = true;
nError = lookup.FindAddr(pszLocalBoundAddress, 0, AI_PASSIVE, AF_INET, 0, 0);
if (nError != 0)
{
IcmpCloseHandle(hIP);
SetLastError(nError);
return false;
}
pAddress = lookup.GetAddrInfoList();
#pragma warning(suppress: 26477)
ATLASSUME(pAddress != nullptr);
#pragma warning(suppress: 26477)
ATLASSERT(pAddress->ai_family == AF_INET);
#pragma warning(suppress: 26489 26490)
auto pLookupAddress{ reinterpret_cast<const sockaddr_in*>(pAddress->ai_addr) };
#pragma warning(suppress: 26489)
srcAddress = pLookupAddress->sin_addr.S_un.S_addr;
}
#pragma warning(suppress: 26486 26489)
const DWORD dwRecvPackets{ bBindSourceIPAddress ? IcmpSendEcho2Ex(hIP, nullptr, nullptr, nullptr, srcAddress, pDestAddress->sin_addr.S_un.S_addr, sendBuf.data(), wDataSize, &OptionInfo, pr.Reply.data(), dwReplySize, dwTimeout) :
IcmpSendEcho(hIP, pDestAddress->sin_addr.S_un.S_addr, sendBuf.data(), wDataSize, &OptionInfo, pr.Reply.data(), dwReplySize, dwTimeout) };
//Close the ICMP handle
IcmpCloseHandle(hIP);
//Check we got the packet back
const bool bSuccess{ dwRecvPackets >= 1 };
if (bSuccess)
{
//Ping was successful, copy over the pertinent info into the return structure
pr.Address = SOCKADDR_IN{};
pr.Address.sin_family = AF_INET;
const ICMP_ECHO_REPLY* pEchoReply{ pr.GetICMP_ECHO_REPLY() };
pr.Address.sin_addr.S_un.S_addr = pEchoReply->Address;
pr.RTT = pEchoReply->RoundTripTime;
pr.EchoReplyStatus = pEchoReply->Status;
SetLastError(ERROR_SUCCESS);
}
else
SetLastError(ERROR_TIMEOUT);
return bSuccess;
}
bool CPing::PingUsingICMPv6(_In_z_ LPCTSTR pszHostName, _Inout_ CPingReplyv6& pr, _In_ UCHAR nTTL, _In_ DWORD dwTimeout, _In_ WORD wDataSize, _In_ UCHAR nTOS, _In_ bool bDontFragment, _In_ bool bFlagReverse, _In_opt_z_ LPCTSTR pszLocalBoundAddress) const
{
//Do the address lookup
ATL::CSocketAddr lookup;
int nError{ lookup.FindAddr(pszHostName, 0, 0, AF_INET6, 0, 0) };
if (nError != 0) //NOLINT(clang-analyzer-optin.portability.UnixAPI)
{
SetLastError(nError);
return false;
}
//Note we always use the first address returned from ATL::CSocketAddr::FindAddr. If you want to use a different
//Address then do the lookup yourself and pass in the correct value in the "pszHostName" parameter
const ADDRINFOT* pAddress{ lookup.GetAddrInfoList() };
#pragma warning(suppress: 26477)
ATLASSUME(pAddress != nullptr);
#pragma warning(suppress: 26477)
ATLASSERT(pAddress->ai_family == AF_INET6);
#pragma warning(suppress: 26489 26490)
const auto pDestAddress{ reinterpret_cast<sockaddr_in6*>(pAddress->ai_addr) };
//Create the ICMP handle
HANDLE hIP{ Icmp6CreateFile() };
if (hIP == INVALID_HANDLE_VALUE)
return false;
//Set up the option info structure
IP_OPTION_INFORMATION OptionInfo{};
OptionInfo.Ttl = nTTL;
OptionInfo.Tos = nTOS;
if (bDontFragment)
OptionInfo.Flags = IP_FLAG_DF;
if (bFlagReverse)
OptionInfo.Flags |= IP_FLAG_REVERSE;
//Set up the data which will be sent
std::vector<BYTE> sendBuf{ wDataSize, std::allocator<BYTE>{} };
#pragma warning(suppress: 26486)
FillIcmpData(sendBuf.data(), wDataSize);
//Do the actual Ping
#pragma warning(suppress: 26472)
const DWORD dwReplySize{ static_cast<DWORD>(sizeof(ICMPV6_ECHO_REPLY) + wDataSize + 8) };
pr.Reply.resize(dwReplySize);
//Bind to the local address if need be
sockaddr_in6 srcAddress{};
srcAddress.sin6_family = AF_INET6;
if (pszLocalBoundAddress && _tcslen(pszLocalBoundAddress))
{
nError = lookup.FindAddr(pszLocalBoundAddress, 0, AI_PASSIVE, AF_INET6, 0, 0);
if (nError != 0)
{
IcmpCloseHandle(hIP);
SetLastError(nError);
return false;
}
pAddress = lookup.GetAddrInfoList();
#pragma warning(suppress: 26477)
ATLASSUME(pAddress != nullptr);
#pragma warning(suppress: 26477)
ATLASSERT(pAddress->ai_family == AF_INET6);
#pragma warning(suppress: 26489 26490)
auto pLookupAddress{ reinterpret_cast<const sockaddr_in6*>(pAddress->ai_addr) };
#pragma warning(suppress: 26489)
srcAddress.sin6_port = pLookupAddress->sin6_port;
#pragma warning(suppress: 26489)
srcAddress.sin6_flowinfo = pLookupAddress->sin6_flowinfo;
#pragma warning(suppress: 26489)
memcpy_s(&srcAddress.sin6_addr, sizeof(srcAddress.sin6_addr), &pLookupAddress->sin6_addr, sizeof(pLookupAddress->sin6_addr));
#pragma warning(suppress: 26489)
srcAddress.sin6_scope_id = pLookupAddress->sin6_scope_id;
}
else
srcAddress.sin6_addr = in6addr_any;
#pragma warning(suppress: 26486)
const DWORD dwRecvPackets{ Icmp6SendEcho2(hIP, nullptr, nullptr, nullptr, &srcAddress, pDestAddress, sendBuf.data(), wDataSize, &OptionInfo, pr.Reply.data(), dwReplySize, dwTimeout) };
//Close the ICMP handle
IcmpCloseHandle(hIP);
//Check we got the packet back
const bool bSuccess{ dwRecvPackets >= 1 };
if (bSuccess)
{
//Ping was successful, copy over the pertinent info into the return structure
pr.Address = SOCKADDR_IN6{};
pr.Address.sin6_family = AF_INET6;
const ICMPV6_ECHO_REPLY* pEchoReply{ pr.GetICMPV6_ECHO_REPLY() };
pr.Address.sin6_port = pEchoReply->Address.sin6_port;
pr.Address.sin6_flowinfo = pEchoReply->Address.sin6_flowinfo;
#pragma warning(suppress: 26485)
memcpy_s(&pr.Address.sin6_addr, sizeof(pr.Address.sin6_addr), pEchoReply->Address.sin6_addr, sizeof(pEchoReply->Address.sin6_addr));
pr.Address.sin6_scope_id = pEchoReply->Address.sin6_scope_id;
pr.RTT = pEchoReply->RoundTripTime;
pr.EchoReplyStatus = pEchoReply->Status;
SetLastError(ERROR_SUCCESS);
}
else
SetLastError(ERROR_TIMEOUT);
return bSuccess;
}