Ping.cpp
资源名称:Netmanag.zip [点击查看]
上传用户:geanq888
上传日期:2007-01-03
资源大小:316k
文件大小:15k
源码类别:
Ftp客户端
开发平台:
Visual C++
- /*
- 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 WinSockn"));
- 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 DLLn"));
- 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 DLLn"));
- }
- 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 %sn"), 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 socketn"));
- 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 timeoutn"));
- 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 timeoutn"));
- 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 failedn"));
- 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 failedn"));
- 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 %sn"), pszHostName);
- return FALSE;
- }
- }
- //Create the ICMP handle
- HANDLE hIP = s_pIcmpCreateFile();
- if (hIP == INVALID_HANDLE_VALUE)
- {
- TRACE(_T("Could not get a valid ICMP handlen"));
- 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 %sn"), 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 recvdn"), 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