📄 ping.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 + -