NAlek
int main() { return 0; }
« Работа с протоколом ICMP

Класс для работы с протоколом ICMP.



Для инициализации WinSock необходимо добавить директиву:


#define ICMPHLP_WSINIT

А так же подключить библиотеку ws2_32.lib:


#pragma comment(lib, "ws2_32.lib")


cpp,icmpICMPHelper.cpp


/*
 * Copyright (C) 2013 NAlek.
 * Web: http://www.nalek.org
 * E-Mail: nalek@nalek.org
 *
 * ICMPHelper version 1.1
 * Date: 31.03.2011
 *
 */

#include "stdafx.h"
#include "ICMPHelper.h"

//
// Query Perfprmance Timer
//

// получаем текущее значение высопоризвоительного счетчика
__int64 CQPTimer::Get()
{
	__int64 i64Ret =0;
	HANDLE hThr =::GetCurrentThread();

	DWORD_PTR dwMask = ::SetThreadAffinityMask( hThr, 0 );

	// сохраняем текущее значение счетчика
	::QueryPerformanceCounter( (LARGE_INTEGER *) &i64Ret );

	::SetThreadAffinityMask( hThr,  dwMask);

	return i64Ret;
}

// вычесляем прошедшее время
double CQPTimer::Elapsed( __int64 i64Begin, __int64 i64End )
{
	__int64 i64Freq =0;

	::QueryPerformanceFrequency( (LARGE_INTEGER *) &i64Freq );

	return i64Freq ==0 ? 0.0 : ((double)( i64End-i64Begin )/(double)i64Freq)*1000.0;
}


//
// ICMP helper
//

//
// подготавливаем имя хоста
//

#ifdef ICMPHLP_WSINIT
//
// инициализация WinSock если необходима
//
bool CICMPHelper::InitWinSock( WORD wVersion )
{
	bool bRet =true;

	WSADATA wsaData;
	if( WSAStartup( wVersion, &wsaData ) )
	{
		// ERROR
		// load winsock library
		bRet =false;   
    }
	else
		if( LOBYTE(wsaData.wVersion) !=LOBYTE(wVersion) || HIBYTE(wsaData.wVersion) !=HIBYTE(wVersion) )
		{
			// ERROR
			// wrong version
			WSACleanup();
			bRet =false;
		}

	return bRet;
}

bool CICMPHelper::CloseWinSock()
{
	return WSACleanup() ==0 ? true : false;
}
#endif

bool CICMPHelper::OpenHost( const char *pHostName )
{
	bool bRet =true;
	hostent *pHostEntry;

	memset( &saiDstHost, 0, sizeof(saiDstHost) );

	//
	// разрешаем имя
	//
	// проверяем корректный ли IPv4 адресс
	if( inet_addr ( pHostName ) !=INADDR_NONE )
	{
		// адрес корректен, дополнительных преобразований не требуется
		// сохраняем результат inet_addr
		saiDstHost.sin_addr.s_addr = inet_addr( pHostName );
		saiDstHost.sin_family =AF_INET;
	}
	else
		// указаный адрес не является IPv4 адресом, предполагаем, что это доменное имя
		// пытаемся разрешить его
		if( ( pHostEntry =gethostbyname( pHostName ) ) !=NULL )
		{
			// сохраняем полученный IPv4 адрес хоста
			//saiHost.sin_addr =*((LPIN_ADDR) *lpHostEntry->h_addr_list);
			memcpy( &saiDstHost.sin_addr, pHostEntry->h_addr, pHostEntry->h_length );
			saiDstHost.sin_family =pHostEntry->h_addrtype;
		}
		else
		{
			// ERROR
			// неудалось получить адрес хоста
			//::WSASetLastError( WSAHOST_NOT_FOUND );
			bRet =false;
		}

	return bRet;
}

//
// подготовка сокета
//
bool CICMPHelper::OpenICMPSession( int iRecvTimeout, int iSendTimeout, int iBufferSize, bool bBind )
{
	bool bRet =true;

	_iRecvTimeout =iRecvTimeout;
	
	// create socket
	if( ( sSock =socket( AF_INET, SOCK_RAW, IPPROTO_ICMP ) ) ==SOCKET_ERROR )
	{
		// ERROR
		// create raw socket
		bRet =false;
	}
	else
	{
		// привязка сокета
		// тащемто не нужна
		if( bBind )
		{
			// trying to bind
			sockaddr_in saLocalHost ={NULL};
			saLocalHost.sin_family =AF_INET;
			saLocalHost.sin_port =0;
			saLocalHost.sin_addr.s_addr =INADDR_ANY;
			if( bind( sSock, (struct sockaddr*)&saLocalHost, sizeof(saLocalHost) ) ==SOCKET_ERROR )
			{
				// ERROR
				// bind error
				bRet =false;
			}

		}// if


		if( bRet )
		{
			// установка таймаутов
			if( ( setsockopt( sSock, SOL_SOCKET, SO_RCVTIMEO, (char*)&iRecvTimeout, sizeof(int) ) ==SOCKET_ERROR ) ||
				( setsockopt( sSock, SOL_SOCKET, SO_SNDTIMEO, (char*)&iSendTimeout, sizeof(int) ) ==SOCKET_ERROR ) )
			{
				// ERROR
				bRet =false;
			}
			else
			{
				// размер буффера не должен превышать MAX_ICMP_DATA_SIZE
				iICMPDataSize =iBufferSize >MAX_ICMP_DATA_SIZE ? MAX_ICMP_DATA_SIZE : iBufferSize ;
				iICMPPacketSize =ICMP_HEADER_LEN+iICMPDataSize;
			}
		}
	}

	return bRet;
}

//
// закрытие сокета
//
bool CICMPHelper::CloseICMPSession()
{
	bool bRet =ICMPHLP_ERR_SUCCESS;

	if( closesocket( sSock ) ==SOCKET_ERROR )
	{
		// ERROR
		// close socket
		bRet =false;
	}

	return bRet;
}

//
// вычисление контрольной суммы пакета
//
unsigned short CICMPHelper::IPChecksum( unsigned short *usBuffer, int iSize )
{
	unsigned long cksum = 0;

	// Sum all the words together, adding the final byte if size is odd
	while( iSize >1 )
	{
		cksum +=*usBuffer++;
		iSize -=sizeof( unsigned short );
	}

	if( iSize )
	{
		cksum += *(unsigned char*)usBuffer;
	}

	// Do a little shuffling
	cksum = (cksum >> 16) + (cksum & 0xffff);
	cksum += (cksum >> 16);

	// Return the bitwise complement of the resulting mishmash
	return (unsigned short)(~cksum);
}

//
// отправляем ICMP запрос
//
int CICMPHelper::SendEchoRequest( unsigned short usPacketID, unsigned short usSeq, int &iSendBytes )
{
	int iRet =ICMPHLP_ERR_SUCCESS;

	// размер ICMP пакета
	//int iPacketSize =ICMP_HEADER_LEN+iICMPDataSize;
	ICMPHeader *sICMPHdr =(ICMPHeader *)malloc( iICMPPacketSize );

	if( sICMPHdr ==NULL )
	{
		// ERROR
		// memory allocation
		//::WSASetLastError( WSA_NOT_ENOUGH_MEMORY );
		iRet =ICMPHLP_ERR_MEMALLOC;
	}
	else
	{
		// подготовка заголовка ICMP пакета
		sICMPHdr->ucType =ICMP_ECHO;
		sICMPHdr->ucCode =0;
		sICMPHdr->usChecksum =0;
		sICMPHdr->usID =usPacketID;
		sICMPHdr->usSeq =usSeq;
		sICMPHdr->ulIDEx =::GetCurrentThreadId();
		sICMPHdr->i64Timestamp =0;

		// заполняем ICMP пакет данными
		memset( (char *)sICMPHdr+ICMP_HEADER_LEN, 0xFF, iICMPDataSize );

		// устанавливаем таймштамп и считаем контрольную сумму пакета
		sICMPHdr->i64Timestamp =cTimer.Get();
		sICMPHdr->usChecksum =IPChecksum( (unsigned short *)sICMPHdr, iICMPPacketSize );
		
		iSendBytes =0;
		int iSent =0, iSendSize =iICMPPacketSize;
		while(1)
		{
			// отправляем ICMP запрос
			iSent =sendto( sSock, ((char *)sICMPHdr)+iSendBytes, iSendSize, 0, (sockaddr*)&saiDstHost, sizeof(saiDstHost));

			if( iSent ==SOCKET_ERROR )
			{
				if( ::WSAGetLastError() ==WSAETIMEDOUT )
				{
					// время ожидания истекло
					iRet =ICMPHLP_ERR_TIMEOUT;
					break;
				}
				else
				{
					// ERROR
					// ошибка WinSock
					iRet =ICMPHLP_ERR_WINSOCK;
					break;
				}
			}
			else
				if( iSent <iSendSize )
				{
					// пакет отправлен не полностью
					// продолжаем отправку
					iSendBytes +=iSent;
					iSendSize =iICMPPacketSize -iSendBytes;
				}
				else
				{
					// ICMP пакет успешно отправлен
					iSendBytes +=iSent+IPv4_HEADER_LEN;
					break;
				}
		}

		free(sICMPHdr);
	}
	return iRet;
}

//
// получаем ICMP ответ
//
int CICMPHelper::ReceiveEchoReply( unsigned short usPacketID, unsigned short usSeq, sockaddr_in &saiSrcHost, int &iRecvBytes, double &dRecvTime, int &iRecvTTL )
{
	int iRet =ICMPHLP_ERR_SUCCESS;

	// максимальный размер принимаемого пакета
	// размер принятого пакета = размер IP заголовка + размер ICMP заголовка + размер отправленных данных

	int iPacketSize =MAX_PACKET_SIZE;
	IPv4Header *sIPHdr =(IPv4Header *)malloc( iPacketSize );

	CQPTimer cAdvTimer;
	if( sIPHdr ==NULL )
	{
		// ERROR
		// memory allocation
		//::WSASetLastError( WSA_NOT_ENOUGH_MEMORY );
		iRet =ICMPHLP_ERR_MEMALLOC;
	}
	else
	{
		__int64 iBegin =cAdvTimer.Get();
		while( 1 )
		{
			int iSrcLen =sizeof(saiSrcHost);
			// принимаем ICMP пакет

			int iRecv =recvfrom( sSock, (char *)sIPHdr, iPacketSize, 0, (sockaddr *)&saiSrcHost, &iSrcLen );

			if( iRecv ==SOCKET_ERROR )
			{
				if( ::WSAGetLastError() ==WSAETIMEDOUT )
				{
					// время ожидания истекло
					iRet =ICMPHLP_ERR_TIMEOUT;
					break;
				}
				else
				{
					// ERROR
					// тошибка WinSock
					iRet =ICMPHLP_ERR_WINSOCK;
					break;
				}
			}
			else
			{
				// минимальный размер принятого ICMP ответа должен быть не меньше sizeof(IPv4Header)+sizeof(ICMPHeader)
				if( iRecv <IPv4_HEADER_LEN+ICMP_HEADER_LEN )
				{
					if( cAdvTimer.Elapsed( iBegin, cAdvTimer.Get() ) >=(double)_iRecvTimeout )
					{
						// время ожидания истекло
						::WSASetLastError( WSAETIMEDOUT );
						iRet =ICMPHLP_ERR_TIMEOUT;
						break;
					}
					// размер полученого пакета меньше минимальнодопустимого размера
					else
						continue;
				}
				else
				{
					// размер IP заголовка принятого пакета может варьироваться от 20 до 60 байт
					// перемещаем указатель до конца IP заголовка принятого пакета 
					ICMPHeader *sICMPHdr =(ICMPHeader *)( (char *)sIPHdr + (sIPHdr->ucHLen*4) );

					//char t[123];
					//sprintf( t, "%dn%d", sICMPHdr->usSeq, usSeq );
					//MessageBox( 0, t, 0, 0);

					if( sICMPHdr->usID !=usPacketID || sICMPHdr->ulIDEx !=::GetCurrentThreadId() || sICMPHdr->usSeq !=usSeq )
					{
						if( cAdvTimer.Elapsed( iBegin, cAdvTimer.Get() ) >=(double)_iRecvTimeout )
						{
							// время ожидания истекло
							::WSASetLastError( WSAETIMEDOUT );
							iRet =ICMPHLP_ERR_TIMEOUT;
							break;
						}
						// "чужой" ICMP пакет
						// продолжаем ждать
						else
							continue;
					}
					else
					{
						if( sICMPHdr->ucType ==ICMP_ECHOREPLY )
						{
							// получили ICMP ответ
							// вычисляем затраченое на отправку и прием время в мсек
							__int64 i64EndTm =cTimer.Get();
							dRecvTime =cTimer.Elapsed( sICMPHdr->i64Timestamp, i64EndTm );
							iRecvBytes =iRecv;
							iRecvTTL =sIPHdr->ucTTL;
							break;
						}
						else
						{
							// неизвестный ICMP ответ
							iRet =ICMPHLP_ERR_ICMPTYPE;
							break;
						}// else
					}// else
				}// else
			}// else
		}// while

		free(sIPHdr);
	}// else

	return iRet;
}


ICMPHelper.h


/*
 * Copyright (C) 2013 NAlek.
 * Web: http://www.nalek.org
 * E-Mail: nalek@nalek.org
 *
 * ICMPHelper version 1.1
 * Date: 31.03.2011
 *
 */
#pragma once

//
// Query Performance Timer
//
class CQPTimer
{
public:
	// получаем текущее значение счетчика
	__int64 Get();
	// вычисляем прошедшее время
	double Elapsed( __int64 i64Begin, __int64 i64End );
};




//
// ICMP
//

// отключаем выравниванеи стрктур
// т.к. нам нужен истенный размер структуры
#ifdef _MSC_VER
// The following two structures need to be packed tightly, but unlike
// Borland C++, Microsoft C++ does not do this by default.
#pragma pack(1)
#endif

// заголовок IP пакета 4-й версии
struct IPv4Header
{
	unsigned char	ucHLen:4;		// Length of the header in dwords
	unsigned char	ucVersion:4;	// Version of IP
	unsigned char	ucTOS;			// Type of service
	unsigned short	usTotalLen;		// Length of the packet in dwords
	unsigned short	usIdent;		// unique identifier
	unsigned short	usFlags;		// Flags
	unsigned char	ucTTL;			// Time to live
	unsigned char	ucProto;		// Protocol number (TCP, UDP etc)
	unsigned short	usChecksum;		// IP checksum
	unsigned long	ulSrcIP;
	unsigned long	ulDstIP;
};

// заголовок ICMP пакета
struct ICMPHeader
{
	unsigned char	ucType;		// ICMP packet type
	unsigned char	ucCode;		// Type sub code
	unsigned short	usChecksum;	// ICMP Checksum
	unsigned short	usID;
	unsigned short	usSeq;
	// не является частью ICMP заголовка, но понадобятся
	unsigned long	ulIDEx;			// 4 байта под уникальный идентификатор пакета
	__int64			i64Timestamp;	// 8 байт под таймштамп
};

#ifdef _MSC_VER
#pragma pack()
#endif

// размер ICMP заголовка
#define		ICMP_HEADER_LEN			sizeof(ICMPHeader)
// размер заголовка IP пакета
#define		IPv4_HEADER_LEN			sizeof(IPv4Header)
// максимальный размер данных ICMP пакета
#define		MAX_ICMP_DATA_SIZE		1024
// максимальный размер ICMP пакета
#define		MAX_ICMP_PACKET_SIZE	MAX_ICMP_DATA_SIZE+ICMP_HEADER_LEN
// максимальный размер принятого пакета
// data saize + icmp header + IP header (20 bytes) + 40 (max ip header =60 bytes)
#define		MAX_PACKET_SIZE	MAX_ICMP_DATA_SIZE+ICMP_HEADER_LEN+IPv4_HEADER_LEN+40
// "стандартный" идентификатор ICMP пакета
//#define		ICMPHLP_PACKET_ID			USER_ID

//
// коды ошибок
//
#define		ICMPHLP_ERR_SUCCESS			0	// success
#define		ICMPHLP_ERR_WINSOCK			1	// winsock error ( WSAGetLastError() )
//#define		ICMPHLP_ERR_HOSTNOTFOUND	2	// host not found
#define		ICMPHLP_ERR_MEMALLOC		3	// memory allocation error
//#define		ICMPHLP_ERR_PACKETSIZE		4	// wrong packet size
#define		ICMPHLP_ERR_TIMEOUT			5	// receive timeout
//#define		ICMPHLP_ERR_DSTUNREACH		6	// destanation unreacheable
//#define		ICMPHLP_ERR_TTL				7	// ttl expire
#define		ICMPHLP_ERR_ICMPTYPE		8	// uncknown icmp type


//
// Definition of type and code field values.
//
#define ICMP_ECHOREPLY            0  /* echo reply */
#define ICMP_UNREACH              3  /* dest unreachable, codes: */
#define ICMP_UNREACH_NET                0  /* bad net */
#define ICMP_UNREACH_HOST               1  /* bad host */
#define ICMP_UNREACH_PROTOCOL           2  /* bad protocol */
#define ICMP_UNREACH_PORT               3  /* bad port */
#define ICMP_UNREACH_NEEDFRAG           4  /* IP_DF caused drop */
#define ICMP_UNREACH_SRCFAIL            5  /* src route failed */
#define ICMP_UNREACH_NET_UNKNOWN        6  /* unknown net */
#define ICMP_UNREACH_HOST_UNKNOWN       7  /* unknown host */
#define ICMP_UNREACH_ISOLATED           8  /* src host isolated */
#define ICMP_UNREACH_NET_PROHIB         9  /* prohibited access */
#define ICMP_UNREACH_HOST_PROHIB       10  /* ditto */
#define ICMP_UNREACH_TOSNET            11  /* bad tos for net */
#define ICMP_UNREACH_TOSHOST           12  /* bad tos for host */
#define ICMP_SOURCEQUENCH         4  /* packet lost, slow down */
#define ICMP_REDIRECT             5  /* shorter route, codes: */
#define ICMP_REDIRECT_NET               0  /* for network */
#define ICMP_REDIRECT_HOST              1  /* for host */
#define ICMP_REDIRECT_TOSNET            2  /* for tos and net */
#define ICMP_REDIRECT_TOSHOST           3  /* for tos and host */
#define ICMP_ECHO                 8  /* echo service */
#define ICMP_ROUTERADVERT         9  /* router advertisement */
#define ICMP_ROUTERSOLICIT       10  /* router solicitation */
#define ICMP_TIMXCEED            11  /* time exceeded, codes: */
#define ICMP_TIMXCEED_INTRANS           0  /* ttl==0 in transit */
#define ICMP_TIMXCEED_REASS             1  /* ttl==0 in reass */
#define ICMP_PARAMPROB           12  /* ip header bad */
#define ICMP_PARAMPROB_OPTABSENT        1  /* req. opt. absent */
#define ICMP_TSTAMP              13  /* timestamp request */
#define ICMP_TSTAMPREPLY         14  /* timestamp reply */
#define ICMP_IREQ                15  /* information request */
#define ICMP_IREQREPLY           16  /* information reply */
#define ICMP_MASKREQ             17  /* address mask request */
#define ICMP_MASKREPLY           18  /* address mask reply */



//
// ICMP helper
//
class CICMPHelper
{
private:
	// высокопроихводительный счетчик
	CQPTimer cTimer;
	SOCKET sSock;

	int _iRecvTimeout;

	// вычисление контрольной суммы пакета
	unsigned short IPChecksum( unsigned short *usBuffer, int iSize );

public:


	// адрес назначения
	sockaddr_in saiDstHost;
	// итоговый размер данных пакета
	int iICMPDataSize;
	// итоговый размер ICMP пакета
	int iICMPPacketSize;

#ifdef ICMPHLP_WSINIT
	//
	// инициализация WinSock если необходима
	//
	bool InitWinSock( WORD wVersion =MAKEWORD(2, 2) );
	bool CloseWinSock();
#endif

	// разрешаем имя хоста
	bool OpenHost( const char *pHostName );
	// содаем новую сессию
	bool OpenICMPSession( int iRecvTimeout, int iSendTimeout, int iBufferSize =12, bool bBind =false );
	// закрываем сессию
	bool CloseICMPSession();
	// отправляем ICMP запрос
	int SendEchoRequest( unsigned short usPacketID, unsigned short usSeq, int &iSendBytes  );
	// получаем ICMP ответ
	int ReceiveEchoReply( unsigned short usPacketID, unsigned short usSeq, sockaddr_in &saiSrcHost, int &iRecvBytes, double &dRecvTime, int &iRecvTTL );
};

Пример (MS Visual Studio 2010): Демонстрация | Исходный код

Яндекс.Метрика