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

📄 ping.cpp

📁 远程网络监视程序的源码
💻 CPP
字号:
#include "stdafx.h"
#include <afxpriv.h>
#include "ping.h"

#ifndef _WINSOCK2API_
#include <winsock2.h>
#include <ws2tcpip.h>
#include ".\ping.h"
#endif

// Automatically use the Winsock 2 library
#pragma comment(lib, "ws2_32.lib")


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

////////////////////////////////////////////////////////////////////////////////////

CPing::CPing(int nPacketSize)
	: m_pIcmpBuff(NULL)
	, m_pRecvBuff(NULL)
	, m_bInitSuccess(FALSE)
{
#ifdef PING_USE_WINSOCK2
	m_sockRaw = NULL;
#else
	m_hIcmpDll = NULL;
	m_pfnIcmpCreateFile = NULL;
	m_pfnIcmpSendEcho = NULL;
	m_pfnIcmpCloseHandle = NULL;
#endif

	if( !Initialize() )
	{
		AfxMessageBox( "ICMP初始化失败" );
		return;
	}

#ifdef PING_USE_WINSOCK2
	if( BindLocalHost() != 0 )
	{
		AfxMessageBox("绑定本地地址失败");
		return;
	}
#endif

	FillIcmpData(nPacketSize);
	m_bInitSuccess = TRUE;
}

CPing::~CPing()
{
	if( m_pIcmpBuff )
		delete []m_pIcmpBuff;

	if( m_pRecvBuff )
		delete []m_pRecvBuff;

#ifdef PING_USE_WINSOCK2
	if( m_sockRaw != INVALID_SOCKET )
	{
		closesocket(m_sockRaw);
	}
#else
	if( m_hIcmpDll )
	{
		FreeLibrary(m_hIcmpDll);
		m_hIcmpDll = NULL;
	}
#endif

	WSACleanup();
}

BOOL CPing::Initialize(void)
{
	WORD wSocketVer;
#ifdef PING_USE_WINSOCK2
	wSocketVer = MAKEWORD(2,2);
#else
	wSocketVer = MAKEWORD(1, 1);
#endif

	WSADATA wsaData;
	if ( WSAStartup(wSocketVer, &wsaData) != 0 )
	{
		return FALSE;
	}

#ifdef PING_USE_WINSOCK2
	// Create the raw socket
	m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
	if (m_sockRaw == INVALID_SOCKET) 
	{
		TRACE( _T("Failed to create a raw socket\n") );
		return FALSE;
	}

	return TRUE;
#else
	//Load up the ICMP library
	m_hIcmpDll = LoadLibrary(_T("ICMP.DLL"));
	if ( !m_hIcmpDll )
	{
		TRACE(_T("Could not load up the ICMP DLL\n"));
		return FALSE;
	}

	//Retrieve pointers to the functions in the ICMP dll
	m_pfnIcmpCreateFile = (PFN_ICMPCREATEFILE) GetProcAddress(m_hIcmpDll, "IcmpCreateFile");
	m_pfnIcmpSendEcho = (PFN_ICMPSENDECHO) GetProcAddress(m_hIcmpDll, "IcmpSendEcho" );
	m_pfnIcmpCloseHandle = (PFN_ICMPCLOSEHANDLE) GetProcAddress(m_hIcmpDll, "IcmpCloseHandle");

	return (m_pfnIcmpCreateFile && m_pfnIcmpSendEcho && m_pfnIcmpCloseHandle);
#endif
}

// Fill up the ICMP packet with defined values
void CPing::FillIcmpData(int nPacketSize)
{
#ifdef PING_USE_WINSOCK2
	m_nBufferSize = max(nPacketSize, MIN_ICMP_PACKET_SIZE) + sizeof(ICMP_HEADER);
	m_nRecvSize = MAX_ICMP_PACKET_SIZE;
#else
	m_nBufferSize = max(nPacketSize, MIN_ICMP_PACKET_SIZE);
	m_nRecvSize = sizeof(ICMP_ECHO_REPLY) + m_nBufferSize;
#endif

	if( m_pIcmpBuff ) // already allocated, maybe need a new size buffer
	{
		delete m_pIcmpBuff;
	}
	m_pIcmpBuff = new char[m_nBufferSize];

	if( m_pRecvBuff )
	{
		delete m_pRecvBuff;
	}
	m_pRecvBuff = new char[m_nRecvSize];

#ifdef PING_USE_WINSOCK2
	ICMP_HEADER *pIcmpHdr = (ICMP_HEADER *)m_pIcmpBuff;
	pIcmpHdr->i_type    = 8; //ICMP_ECHO type
	pIcmpHdr->i_code    = 0;
	pIcmpHdr->i_id      = (USHORT) GetCurrentProcessId();
	pIcmpHdr->i_seq     = 0;
	pIcmpHdr->i_cksum   = 0;
	pIcmpHdr->timestamp = GetTickCount();

	// Set up the data which will be sent
	char* pData = m_pIcmpBuff + sizeof(ICMP_HEADER);
	memset(pData, 'E', nPacketSize);

	// Generate the checksum
	pIcmpHdr->i_cksum = GenerateIPChecksum((USHORT*)m_pIcmpBuff, m_nBufferSize);
#else
	memset(m_pIcmpBuff, 'E', nPacketSize);
#endif
}

BOOL CPing::ParseHostName(LPCTSTR szHostName, sockaddr_in* psockAddr)
{
	// For correct operation of the T2A macro, see TN059
	USES_CONVERSION;

	if( !( szHostName && psockAddr ) )
	{
		return FALSE;
	}

	// Resolve the address of the host to connect to
	memset( psockAddr, 0, sizeof(sockaddr_in) );
	LPSTR lpszAscii = T2A((LPTSTR) szHostName);
	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( &(psockAddr->sin_addr), hp->h_addr, hp->h_length );
			psockAddr->sin_family = hp->h_addrtype;
		}
		else
		{
			TRACE(_T("CPing::ParseHostName, Could not resolve the host name %s\n"), szHostName);
			return FALSE;
		}
	}
	else
	{
		psockAddr->sin_addr.s_addr = addr;
		psockAddr->sin_family = AF_INET;
	}

	return TRUE;
}

#ifdef PING_USE_WINSOCK2
int CPing::Ping(LPCTSTR szHostName, PINGREPLY_S *pr, int nPings, UCHAR nTTL, DWORD dwTimeout, UCHAR nTOS, BOOL bDontFragment)
{
	pr->nError = -1;
	pr->minRTT = ULONG_MAX;
	pr->maxRTT = 0;
	pr->avgRTT = 0;

	if( !m_bInitSuccess )
	{
		return -1;
	}

	// Resolve the address of the host to connect to
	sockaddr_in dest;
	if( !ParseHostName(szHostName, &dest) )
	{
		TRACE(_T("CPing::Ping, Failed to parse host name\n"));
		return -1;
	}

	// Set the TTL on the socket  
	int nTempTTL = nTTL;
	if (setsockopt(m_sockRaw, IPPROTO_IP, IP_TTL, (char*) &nTempTTL, sizeof(nTempTTL)) == SOCKET_ERROR)
	{
		TRACE(_T("CPing::Ping, Failed to set the TTL value on the socket\n"));
		return -1;
	}

	// Set the TOS on the socket  
	int nTempTos = nTOS;
	if (setsockopt(m_sockRaw, IPPROTO_IP, IP_TOS, (char*) &nTempTos, sizeof(nTempTos)) == SOCKET_ERROR)
	{
		TRACE(_T("CPing::Ping, Failed to set the Tos value on the socket\n"));
		return -1;
	}

	// Set the Don't Fragment flag on the socket  
	if (bDontFragment)
	{
		if (setsockopt(m_sockRaw, IPPROTO_IP, IP_DONTFRAGMENT, (char*) &bDontFragment, sizeof(bDontFragment)) == SOCKET_ERROR)
		{
			TRACE(_T("CPing::Ping, Failed to set the Don't Fragment value on the socket\n"));
			return -1;
		}
	}

	for( int i = 0; i < nPings; i++ )
	{
		// Get the tick count prior to sending the packet
		m_obTimer.Start();

		// Send of the packet
		int nWrote = sendto( m_sockRaw, m_pIcmpBuff, m_nBufferSize, 0, (sockaddr *)&dest, sizeof(sockaddr_in) );
		if (nWrote == SOCKET_ERROR)
		{
			TRACE( _T("CPing::Ping, sendto failed\n") );

			DWORD dwError = GetLastError();
			SetLastError(dwError);

			return -1;
		}

		// zero the recv buffer
		memset(m_pRecvBuff, 0, m_nRecvSize);
		BOOL bReadable;
		sockaddr_in addrFrom;
		int nFromlen = sizeof(sockaddr_in);
		int nRead = 0;

		// Allow the specified timeout
		if ( IsSocketReadible(m_sockRaw, dwTimeout, bReadable) )
		{
			if (bReadable)
			{
				// Receive the response
				nRead = recvfrom(m_sockRaw, m_pRecvBuff, m_nRecvSize, 0, (sockaddr *)&addrFrom, &nFromlen);
			}
			else
			{
				TRACE(_T("CPing::Ping, timeout occured while awaiting recvfrom\n"));

				// set the error to timed out
				SetLastError(WSAETIMEDOUT);

				return -1;
			}
		}
		else
		{
			TRACE(_T("CPing::Ping, IsReadible call failed\n"));

			DWORD dwError = GetLastError();
			SetLastError(dwError);

			return -1;
		}

		// Get the current tick count
		ULONG rtt = (ULONG)m_obTimer.Stop();

		// Now check the return response from recvfrom
		if ( nRead == SOCKET_ERROR )
		{
			TRACE(_T("CPing::Ping, recvfrom call failed\n"));

			DWORD dwError = GetLastError();
			SetLastError(dwError);

			return -1;
		}

		// Decode the response we got back
		pr->nError = DecodeResponse(m_pRecvBuff, nRead, &addrFrom);

		pr->Address = addrFrom.sin_addr;
		pr->avgRTT += rtt;
		if (pr->maxRTT < rtt)
			pr->maxRTT = rtt;
		if (pr->minRTT > rtt)
			pr->minRTT = rtt;
	} // for looping
	pr->avgRTT /= nPings;

	// return the status
	return pr->nError;
}

BOOL CPing::IsSocketReadible(SOCKET socket, DWORD dwTimeout, BOOL &bReadible)
{
	timeval timeout = {dwTimeout/1000, dwTimeout % 1000};
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(socket, &fds);

	int nStatus = select(0, &fds, NULL, NULL, &timeout);
	if (nStatus == SOCKET_ERROR)
	{
		return FALSE;
	}
	else
	{
		bReadible = !(nStatus == 0);
		return TRUE;
	}
}

// Decode the raw IP packet we get back
int CPing::DecodeResponse(char *pBuf, int nBytes, sockaddr_in *paddrFrom) 
{
	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 %sin ICMP packet\n"), inet_ntoa(paddrFrom->sin_addr));
		SetLastError(ERROR_UNEXP_NET_ERR);
		return -1;
	}

	// Check it is an ICMP_ECHOREPLY packet
	LPICMP_HEADER pIcmpHdr = (LPICMP_HEADER) (pBuf + nIpHdrlen);
	if (pIcmpHdr->i_type != 0) //type ICMP_ECHOREPLY is 0
	{
		return ((int)pIcmpHdr->i_type);
	}

	// Check it is the same id as we sent
	if (pIcmpHdr->i_id != (USHORT)GetCurrentProcessId()) 
	{
		TRACE(_T("Received someone else's ICMP packet!\n"));
		SetLastError(ERROR_UNEXP_NET_ERR);
		return -1;
	}

	return pIcmpHdr->i_type;
}

// generate an IP checksum based on a given data buffer
USHORT CPing::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);
}

int CPing::BindLocalHost(void)
{
	sockaddr_in sockLocalAddress;
	memset(&sockLocalAddress, 0, sizeof(sockaddr_in));

	sockLocalAddress.sin_family = AF_INET;
	sockLocalAddress.sin_port = htons((u_short)0);

	LPHOSTENT lphost = gethostbyname(NULL);
	if ( !lphost )
	{
		return -1; 
	}
	sockLocalAddress.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr;

	if (bind(m_sockRaw, (sockaddr*) &sockLocalAddress, sizeof(sockLocalAddress)) == SOCKET_ERROR)
	{
		TRACE(_T("CPing::BindLocalHost, Failed to bind to specified address\n"));
		return -1;
	}

	return 0;
}

#else
////////////////////////////////////////////////////////////////////////////////
// using ICMP DLL

int CPing::Ping(LPCTSTR szHostName, PINGREPLY_S *pr, int nPings, UCHAR nTTL, DWORD dwTimeout, UCHAR nTOS, BOOL bDontFragment)
{
	pr->nError = -1;
	pr->minRTT = ULONG_MAX;
	pr->maxRTT = 0;
	pr->avgRTT = 0;

	//Make sure everything is initialised
	if ( !m_bInitSuccess )
	{
		return -1;
	}

	sockaddr_in dest;
	if( !ParseHostName( szHostName, &dest ) )
	{
		return -1;
	}

	//Create the ICMP handle
	HANDLE hIcmpFile = m_pfnIcmpCreateFile();
	if (hIcmpFile == INVALID_HANDLE_VALUE)
	{
		TRACE(_T("Could not get a valid ICMP handle\n"));
		return -1;
	}

	//Set up the option info structure
	IP_OPTION_INFORMATION stOptions;
	memset( &stOptions, 0, sizeof(IP_OPTION_INFORMATION) );
	stOptions.Ttl = nTTL;
	stOptions.Tos = nTOS;
	if( bDontFragment )
	{
		stOptions.Flags = IP_FLAG_DF;
	}

	BOOL bSuccess = TRUE;
	for( int i = 0; i < nPings; i++ )
	{
		//Do the actual Ping
		ICMP_ECHO_REPLY *pEchoReply = (ICMP_ECHO_REPLY *)m_pRecvBuff;
		memset( (char *)pEchoReply, 0, m_nRecvSize );

		DWORD nRecvPackets = m_pfnIcmpSendEcho(hIcmpFile, dest.sin_addr.S_un.S_addr,
			m_pIcmpBuff, m_nBufferSize, &stOptions, (void *)pEchoReply,
			m_nRecvSize, dwTimeout);

		//Check we got the packet back
		bSuccess = (nRecvPackets == 1);

		//Check the IP status is OK (O is IP Success)
		if (bSuccess && (pEchoReply->Status != 0))
		{
			bSuccess = FALSE;
			pr->nError = pEchoReply->Status; // received a overtime packet
			SetLastError(pEchoReply->Status);
		}

		//Check we got the same amount of data back as we sent
		if (bSuccess)
		{
			bSuccess = ( pEchoReply->DataSize == m_nBufferSize );
			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< m_nBufferSize && bSuccess; i++ )
			{
				bSuccess = (pReplyData[i] == 'E');
			}

			if (!bSuccess)
			{
				SetLastError(ERROR_UNEXP_NET_ERR);
			}
		}

		// TTL is enough now, do not try any more
		if( bSuccess )
		{
			pr->nError = 0;
		}

		// success or received an icmp error packet
		if( pr->nError != -1 )
		{
			pr->Address.S_un.S_addr = pEchoReply->Address;
			pr->avgRTT += pEchoReply->RoundTripTime;
			if( pr->minRTT > pEchoReply->RoundTripTime )
			{
				pr->minRTT = pEchoReply->RoundTripTime;
			}

			if( pr->maxRTT < pEchoReply->RoundTripTime )
			{
				pr->maxRTT = pEchoReply->RoundTripTime;
			}
		}
	}
	pr->avgRTT /= nPings;

	//Close the ICMP handle
	m_pfnIcmpCloseHandle(hIcmpFile);

	//return the status
	return (bSuccess? 0 : -1); 
}
#endif // PING_USE_WINSOCK2

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -