Класс для работы с протоколом ICMP. Для инициализации WinSock необходимо объявить директиву ICMPHLP_WSINIT и подключить библиотеку ws2_32.lib: #define ICMPHLP_WSINIT #pragma comment(lib, "ws2_32.lib") ICMPHelper.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 ); };