⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ping.cpp

📁 网络管理器源码
💻 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 + -