📄 ping.cpp
字号:
/*
Module : PING.CPP
Purpose: Implementation for an MFC wrapper class to encapsulate PING
Created: PJN / 10-06-1998
History: PJN / 23-06-1198 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
Copyright (c) 1998 by PJ Naughter.
All rights reserved.
*/
///////////////////////////////// Includes //////////////////////////////////
#include "stdafx.h"
#include "ping.h"
#undef CPING_USE_WINSOCK2
#undef _WINSOCK2API_
///////////////////////////////// Definitions ////////////////////////////////
//These defines & structure definitions are taken from the "ipexport.h" and
//"icmpapi.h" header files as provided with the Platform SDK and
//are used internally by the CPing class. Including them here allows
//you to compile the CPing code without the need to have the full
//Platform SDK installed.
typedef unsigned long IPAddr; // An IP address.
typedef struct tagIP_OPTION_INFORMATION
{
unsigned char Ttl; // Time To Live
unsigned char Tos; // Type Of Service
unsigned char Flags; // IP header flags
unsigned char OptionsSize; // Size in bytes of options data
unsigned char FAR *OptionsData; // Pointer to options data
} IP_OPTION_INFORMATION;
typedef struct tagICMP_ECHO_REPLY
{
IPAddr Address; // Replying address
unsigned long Status; // Reply IP_STATUS
unsigned long RoundTripTime; // RTT in milliseconds
unsigned short DataSize; // Reply data size in bytes
unsigned short Reserved; // Reserved for system use
void FAR *Data; // Pointer to the reply data
IP_OPTION_INFORMATION Options; // Reply options
} ICMP_ECHO_REPLY;
typedef IP_OPTION_INFORMATION FAR* LPIP_OPTION_INFORMATION;
typedef ICMP_ECHO_REPLY FAR* LPICMP_ECHO_REPLY;
typedef HANDLE (WINAPI IcmpCreateFile)(VOID);
typedef IcmpCreateFile* lpIcmpCreateFile;
typedef BOOL (WINAPI IcmpCloseHandle)(HANDLE IcmpHandle);
typedef IcmpCloseHandle* lpIcmpCloseHandle;
typedef DWORD (WINAPI IcmpSendEcho)(HANDLE IcmpHandle, IPAddr DestinationAddress,
LPVOID RequestData, WORD RequestSize,
LPIP_OPTION_INFORMATION RequestOptions,
LPVOID ReplyBuffer, DWORD ReplySize, DWORD Timeout);
typedef IcmpSendEcho* lpIcmpSendEcho;
#define MIN_ICMP_PACKET_SIZE 8 //minimum 8 byte icmp packet (just header)
#define MAX_ICMP_PACKET_SIZE 102400 //Maximum icmp packet size
#ifdef CPING_USE_WINSOCK2
#ifndef _WINSOCK2API_
#pragma message("You need to include winsock2.h in your PCH")
#endif
// IP header
typedef struct tagIP_HEADER
{
unsigned int h_len:4; // length of the header
unsigned int version:4; // Version of IP
unsigned char tos; // Type of service
unsigned short total_len; // total length of the packet
unsigned short ident; // unique identifier
unsigned short frag_and_flags; // flags
unsigned char ttl;
unsigned char proto; // protocol (TCP, UDP etc)
unsigned short checksum; // IP checksum
unsigned int sourceIP;
unsigned int destIP;
} IP_HEADER;
typedef IP_HEADER FAR* LPIP_HEADER;
// ICMP header
typedef struct tagICMP_HEADER
{
BYTE i_type;
BYTE i_code; /* type sub code */
USHORT i_cksum;
USHORT i_id;
USHORT i_seq;
/* This is not the std header, but we reserve space for time */
ULONG timestamp;
} ICMP_HEADER;
typedef ICMP_HEADER FAR* LPICMP_HEADER;
void FillIcmpData(LPICMP_HEADER pIcmp, int nData);
BOOL DecodeResponse(char* pBuf, int nBytes, sockaddr_in* from);
USHORT GenerateIPChecksum(USHORT* pBuffer, int nSize);
#endif //CPING_USE_WINSOCK2
///////////////////////////////// Macros & Statics ///////////////////////////
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
lpIcmpCreateFile s_pIcmpCreateFile = NULL;
lpIcmpSendEcho s_pIcmpSendEcho = NULL;
lpIcmpCloseHandle s_pIcmpCloseHandle = NULL;
HINSTANCE s_hIcmp = NULL;
BOOL s_bAttemptedIcmpInitialise = FALSE;
int CPing::sm_nCount = 0;
BOOL CPing::sm_bWinsock2OK = FALSE;
__int64 CPing::sm_TimerFrequency = 0;
///////////////////////////////// Implementation //////////////////////////////
CPing::CPing()
{
++sm_nCount; //Increment the reference count
}
CPing::~CPing()
{
--sm_nCount; //decrement the reference count
//Free up our resources if we are the last one out
if (sm_nCount == 0)
{
FreeLibrary(s_hIcmp);
WSACleanup();
}
}
#ifdef CPING_USE_WINSOCK2
BOOL CPing::Initialise() const
{
if (!s_bAttemptedIcmpInitialise)
{
s_bAttemptedIcmpInitialise = TRUE;
//Initialise the winsock 2 stack
WSADATA wsa;
sm_bWinsock2OK = (WSAStartup(MAKEWORD(2, 1), &wsa) == 0);
//Use the High performace counter to get an accurate RTT
LARGE_INTEGER Frequency;
sm_bWinsock2OK = sm_bWinsock2OK && QueryPerformanceFrequency(&Frequency);
if (sm_bWinsock2OK)
sm_TimerFrequency = Frequency.QuadPart;
}
return sm_bWinsock2OK;
}
#else ////CPING_USE_WINSOCK2
BOOL CPing::Initialise() const
{
if (!s_bAttemptedIcmpInitialise)
{
s_bAttemptedIcmpInitialise = TRUE;
//Initialise the winsock stack
WSADATA wsa;
if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0)
{
TRACE(_T("Could not negotiate a correct version of WinSock\n"));
return FALSE;
}
//Load up the ICMP library
s_hIcmp = LoadLibrary(_T("ICMP.DLL"));
if (s_hIcmp == NULL)
{
TRACE(_T("Could not load up the ICMP DLL\n"));
return FALSE;
}
//Retrieve pointers to the functions in the ICMP dll
s_pIcmpCreateFile = (lpIcmpCreateFile) GetProcAddress(s_hIcmp,"IcmpCreateFile");
s_pIcmpSendEcho = (lpIcmpSendEcho) GetProcAddress(s_hIcmp,"IcmpSendEcho" );
s_pIcmpCloseHandle = (lpIcmpCloseHandle) GetProcAddress(s_hIcmp,"IcmpCloseHandle");
if (s_pIcmpCreateFile == NULL || s_pIcmpSendEcho == NULL || s_pIcmpCloseHandle == NULL)
TRACE(_T("Could not find ICMP functions in the ICMP DLL\n"));
}
return (s_pIcmpCreateFile != NULL && s_pIcmpSendEcho != NULL && s_pIcmpCloseHandle != NULL);
}
#endif //CPING_USE_WINSOCK2
#ifdef CPING_USE_WINSOCK2
BOOL CPing::Ping(LPCTSTR pszHostName, CPingReply& pr, UCHAR /*nTTL*/, DWORD dwTimeout, UCHAR nPacketSize) const
{
//Parameter validation
if (nPacketSize > MAX_ICMP_PACKET_SIZE || nPacketSize < MIN_ICMP_PACKET_SIZE)
{
ASSERT(FALSE);
SetLastError(WSAENOBUFS);
return FALSE;
}
//For correct operation of the T2A macro, see TN059
USES_CONVERSION;
//Make sure everything is initialised
if (!Initialise())
return FALSE;
//Resolve the address of the host to connect to
sockaddr_in dest;
memset(&dest,0,sizeof(dest));
LPSTR lpszAscii = T2A((LPTSTR) pszHostName);
unsigned long addr = inet_addr(lpszAscii);
if (addr == INADDR_NONE)
{
//Not a dotted address, then do a lookup of the name
hostent* hp = gethostbyname(lpszAscii);
if (hp)
{
memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);
dest.sin_family = hp->h_addrtype;
}
else
{
TRACE(_T("Could not resolve the host name %s\n"), pszHostName);
return FALSE;
}
}
else
{
dest.sin_addr.s_addr = addr;
dest.sin_family = AF_INET;
}
//Create the raw socket
SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
if (sockRaw == INVALID_SOCKET)
{
TRACE(_T("Failed to create a raw socket\n"));
return FALSE;
}
//Send the receive timeout
int nTimeout = dwTimeout;
int nError = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTimeout, sizeof(nTimeout));
if (nError == SOCKET_ERROR)
{
TRACE(_T("Failed to set recv timeout\n"));
return FALSE;
}
//Set the send timeout (always set to 1 second)
nTimeout = 1000;
nError = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&nTimeout, sizeof(nTimeout));
if (nError == SOCKET_ERROR)
{
TRACE(_T("Failed to set send timeout\n"));
return FALSE;
}
//Allocate the ICMP packet
int nBufSize = nPacketSize + sizeof(ICMP_HEADER);
char* pICMP = new char[nBufSize];
FillIcmpData((LPICMP_HEADER) pICMP, nBufSize);
//Get the tick count prior to sending the packet
LARGE_INTEGER TimerTick;
VERIFY(QueryPerformanceCounter(&TimerTick));
__int64 nStartTick = TimerTick.QuadPart;
//Send of the packet
int nWrote = sendto(sockRaw, pICMP, nBufSize, 0, (sockaddr*)&dest, sizeof(dest));
if (nWrote == SOCKET_ERROR)
{
TRACE(_T("sendto failed\n"));
delete [] pICMP;
return FALSE;
}
//allocate the recv buffer
char* pRecvBuf = new char[MAX_ICMP_PACKET_SIZE];
//Receive the response
sockaddr_in from;
int nFromlen = sizeof(from);
int nRead = recvfrom(sockRaw, pRecvBuf, MAX_ICMP_PACKET_SIZE, 0, (sockaddr*)&from, &nFromlen);
//Get the current tick count
VERIFY(QueryPerformanceCounter(&TimerTick));
//Now check the return response from recvfrom
if (nRead == SOCKET_ERROR)
{
DWORD dwError = GetLastError();
TRACE(_T("recvfrom failed\n"));
delete [] pICMP;
delete [] pRecvBuf;
return FALSE;
}
//Decode the response we got back
BOOL bSuccess = DecodeResponse(pRecvBuf, nRead, &from);
//If we successfully decoded the response, then return the
//values in the CPingReply instance
if (bSuccess)
{
pr.Address = from.sin_addr;
pr.RTT = (ULONG) ((TimerTick.QuadPart - nStartTick) * 1000 / sm_TimerFrequency);
}
//Free up the memory we allocated
delete [] pICMP;
delete [] pRecvBuf;
//return the status
return bSuccess;
}
#else ////CPING_USE_WINSOCK2
BOOL CPing::Ping(LPCTSTR pszHostName, CPingReply& pr, UCHAR nTTL, DWORD dwTimeout, UCHAR nPacketSize) const
{
//For correct operation of the T2A macro, see TN059
USES_CONVERSION;
//Make sure everything is initialised
if (!Initialise())
return FALSE;
LPSTR lpszAscii = T2A((LPTSTR) pszHostName);
//Convert from dotted notation if required
unsigned long addr = inet_addr(lpszAscii);
if (addr == INADDR_NONE)
{
//Not a dotted address, then do a lookup of the name
hostent* hp = gethostbyname(lpszAscii);
if (hp)
memcpy(&addr, hp->h_addr, hp->h_length);
else
{
TRACE(_T("Could not resolve the host name %s\n"), pszHostName);
return FALSE;
}
}
//Create the ICMP handle
HANDLE hIP = s_pIcmpCreateFile();
if (hIP == INVALID_HANDLE_VALUE)
{
TRACE(_T("Could not get a valid ICMP handle\n"));
return FALSE;
}
//Set up the option info structure
IP_OPTION_INFORMATION OptionInfo;
ZeroMemory(&OptionInfo, sizeof(IP_OPTION_INFORMATION));
OptionInfo.Ttl = nTTL;
//Set up the data which will be sent
unsigned char* pBuf = new unsigned char[nPacketSize];
memset(pBuf, 'E', nPacketSize);
//Do the actual Ping
int nReplySize = sizeof(ICMP_ECHO_REPLY) + max(MIN_ICMP_PACKET_SIZE, nPacketSize);
unsigned char* pReply = new unsigned char[nReplySize];
ICMP_ECHO_REPLY* pEchoReply = (ICMP_ECHO_REPLY*) pReply;
DWORD nRecvPackets = s_pIcmpSendEcho(hIP, addr, pBuf, nPacketSize, &OptionInfo, pReply, nReplySize, dwTimeout);
//Check we got the packet back
BOOL bSuccess = (nRecvPackets == 1);
//Check the IP status is OK (O is IP Success)
if (bSuccess && (pEchoReply->Status != 0))
{
bSuccess = FALSE;
SetLastError(pEchoReply->Status);
}
//Check we got the same amount of data back as we sent
if (bSuccess)
{
bSuccess = (pEchoReply->DataSize == nPacketSize);
if (!bSuccess)
SetLastError(ERROR_UNEXP_NET_ERR);
}
//Check the data we got back is what was sent
if (bSuccess)
{
char* pReplyData = (char*) pEchoReply->Data;
for (int i=0; i<nPacketSize && bSuccess; i++)
bSuccess = (pReplyData[i] == 'E');
if (!bSuccess)
SetLastError(ERROR_UNEXP_NET_ERR);
}
//Close the ICMP handle
s_pIcmpCloseHandle(hIP);
if (bSuccess)
{
//Ping was successful, copy over the pertinent info
//into the return structure
pr.Address.S_un.S_addr = pEchoReply->Address;
pr.RTT = pEchoReply->RoundTripTime;
}
//Free up the memory we allocated
delete [] pBuf;
delete [] pReply;
//return the status
return bSuccess;
}
#endif //CPING_USE_WINSOCK2
#ifdef CPING_USE_WINSOCK2
//Decode the raw Ip packet we get back
BOOL DecodeResponse(char* pBuf, int nBytes, sockaddr_in* from)
{
//Get the current tick count
LARGE_INTEGER TimerTick;
VERIFY(QueryPerformanceCounter(&TimerTick));
LPIP_HEADER pIpHdr = (LPIP_HEADER) pBuf;
int nIpHdrlen = pIpHdr->h_len * 4; //Number of 32-bit words*4 = bytes
//Not enough data recieved
if (nBytes < nIpHdrlen + MIN_ICMP_PACKET_SIZE)
{
TRACE(_T("Received too few bytes from %s\n"), inet_ntoa(from->sin_addr));
SetLastError(ERROR_UNEXP_NET_ERR);
return FALSE;
}
//Check it is an ICMP_ECHOREPLY packet
LPICMP_HEADER pIcmpHdr = (LPICMP_HEADER) (pBuf + nIpHdrlen);
if (pIcmpHdr->i_type != 0) //type ICMP_ECHOREPLY is 0
{
TRACE(_T("non-echo type %d recvd\n"), pIcmpHdr->i_type);
SetLastError(ERROR_UNEXP_NET_ERR);
return FALSE;
}
//Check it is the same id as we sent
if (pIcmpHdr->i_id != (USHORT)GetCurrentProcessId())
{
TRACE(_T("Received someone else's packet!\n"));
SetLastError(ERROR_UNEXP_NET_ERR);
return FALSE;
}
return TRUE;
}
//generate an IP checksum based on a given data buffer
USHORT GenerateIPChecksum(USHORT* pBuffer, int nSize)
{
unsigned long cksum = 0;
while (nSize > 1)
{
cksum += *pBuffer++;
nSize -= sizeof(USHORT);
}
if (nSize)
cksum += *(UCHAR*)pBuffer;
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}
//Fill up the ICMP packet with defined values
void FillIcmpData(LPICMP_HEADER pIcmp, int nData)
{
pIcmp->i_type = 8; //ICMP_ECHO type
pIcmp->i_code = 0;
pIcmp->i_id = (USHORT) GetCurrentProcessId();
pIcmp->i_seq = 0;
pIcmp->i_cksum = 0;
pIcmp->timestamp = GetTickCount();
//Set up the data which will be sent
int nHdrSize = sizeof(ICMP_HEADER);
char* pData = (char*) (pIcmp + nHdrSize);
memset(pData, 'E', nData - nHdrSize);
//Generate the checksum
pIcmp->i_cksum = GenerateIPChecksum((USHORT*)pIcmp, nData);
}
#endif //CPING_USE_WINSOCK2
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -