/*
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
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
Copyright (c) 1998 by PJ Naughter.
All rights reserved.
*/
///////////////////////////////// Includes //////////////////////////////////
#include "stdafx.h"
#include "ping.h"
///////////////////////////////// Definitions ////////////////////////////////
#define MIN_ICMP_PACKET_SIZE 8 //minimum 8 byte icmp packet (just header)
#define MAX_ICMP_PACKET_SIZE 1024 //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
#ifdef CPING_USE_WINSOCK2
BOOL CPing::sm_bWinsock2OK = FALSE;
BOOL CPing::sm_bAttemptedWinsock2Initialise = FALSE;
#endif
#ifdef CPING_USE_ICMP
BOOL CPing::sm_bAttemptedIcmpInitialise = FALSE;
lpIcmpCreateFile CPing::sm_pIcmpCreateFile = NULL;
lpIcmpSendEcho CPing::sm_pIcmpSendEcho = NULL;
lpIcmpCloseHandle CPing::sm_pIcmpCloseHandle = NULL;
#endif
__int64 CPing::sm_TimerFrequency = 0;
//Internal class which is used to ensure that the ICMP
//handle and winsock stack is closed upon exit
class _CPING
{
public:
_CPING();
~_CPING();
protected:
#ifdef CPING_USE_ICMP
HINSTANCE sm_hIcmp;
#endif
friend class CPing;
};
_CPING::_CPING()
{
#ifdef CPING_USE_ICMP
sm_hIcmp = NULL;
#endif
}
_CPING::~_CPING()
{
#ifdef CPING_USE_ICMP
if (sm_hIcmp)
{
FreeLibrary(sm_hIcmp);
sm_hIcmp = NULL;
}
#endif
WSACleanup();
}
static _CPING _cpingData;
///////////////////////////////// Implementation //////////////////////////////
#ifdef CPING_USE_WINSOCK2
BOOL CPing::Initialise2() const
{
if (!sm_bAttemptedWinsock2Initialise)
{
sm_bAttemptedWinsock2Initialise = 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;
Frequency.QuadPart = 0;
sm_bWinsock2OK = sm_bWinsock2OK && QueryPerformanceFrequency(&Frequency);
if (sm_bWinsock2OK)
sm_TimerFrequency = Frequency.QuadPart;
}
return sm_bWinsock2OK;
}
#endif //CPING_USE_WINSOCK2
#ifdef CPING_USE_ICMP
BOOL CPing::Initialise1() const
{
if (!sm_bAttemptedIcmpInitialise)
{
sm_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
_cpingData.sm_hIcmp = LoadLibrary(_T("ICMP.DLL"));
if (_cpingData.sm_hIcmp == NULL)
{
TRACE(_T("Could not load up the ICMP DLL\n"));
return FALSE;
}
//Retrieve pointers to the functions in the ICMP dll
sm_pIcmpCreateFile = (lpIcmpCreateFile) GetProcAddress(_cpingData.sm_hIcmp,"IcmpCreateFile");
sm_pIcmpSendEcho = (lpIcmpSendEcho) GetProcAddress(_cpingData.sm_hIcmp,"IcmpSendEcho" );
sm_pIcmpCloseHandle = (lpIcmpCloseHandle) GetProcAddress(_cpingData.sm_hIcmp,"IcmpCloseHandle");
if (sm_pIcmpCreateFile == NULL || sm_pIcmpSendEcho == NULL || sm_pIcmpCloseHandle == NULL)
TRACE(_T("Could not find ICMP functions in the ICMP DLL\n"));
}
return (sm_pIcmpCreateFile != NULL && sm_pIcmpSendEcho != NULL && sm_pIcmpCloseHandle != NULL);
}
#endif //CPING_USE_ICMP
#ifdef CPING_USE_WINSOCK2
BOOL CPing::Ping2(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 (!Initialise2())
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("CPing::Ping2, 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("CPing::Ping2, Failed to create a raw socket\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("CPing::Ping2, sendto failed\n"));
delete [] pICMP;
DWORD dwError = GetLastError();
closesocket(sockRaw);
SetLastError(dwError);
return FALSE;
}
//allocate the recv buffer
char* pRecvBuf = new char[MAX_ICMP_PACKET_SIZE];
BOOL bReadable;
sockaddr_in from;
int nFrom