Ping.cpp
上传用户:geanq888
上传日期:2007-01-03
资源大小:316k
文件大小:15k
源码类别:

Ftp客户端

开发平台:

Visual C++

  1. /*
  2. Module : PING.CPP
  3. Purpose: Implementation for an MFC wrapper class to encapsulate PING
  4. Created: PJN / 10-06-1998
  5. History: PJN / 23-06-1198 1) Now code can be compiled to use Winsock2 calls
  6.                           instead of using the ICMP.DLL. This gives another of
  7.                           advantages:
  8.                           i)  Your using a API that MS has promised to continue to support.
  9.                           ii) Internally the class calls QueryPerformanceCounter meaning that
  10.                               you will get the highest resolution RTT's possible.
  11.                           2) Also did a general tidy up of the code
  12.                           3) Changed default timeout to 1 second
  13. Copyright (c) 1998 by PJ Naughter.  
  14. All rights reserved.
  15. */
  16. /////////////////////////////////  Includes  //////////////////////////////////
  17. #include "stdafx.h"
  18. #include "ping.h"
  19. #undef CPING_USE_WINSOCK2
  20. #undef _WINSOCK2API_
  21. /////////////////////////////////  Definitions ////////////////////////////////
  22. //These defines & structure definitions are taken from the "ipexport.h" and
  23. //"icmpapi.h" header files as provided with the Platform SDK and
  24. //are used internally by the CPing class. Including them here allows
  25. //you to compile the CPing code without the need to have the full 
  26. //Platform SDK installed.
  27. typedef unsigned long IPAddr;     // An IP address.
  28. typedef struct tagIP_OPTION_INFORMATION 
  29. {
  30.   unsigned char      Ttl;              // Time To Live
  31.   unsigned char      Tos;              // Type Of Service
  32.   unsigned char      Flags;            // IP header flags
  33.   unsigned char      OptionsSize;      // Size in bytes of options data
  34.   unsigned char FAR *OptionsData;      // Pointer to options data
  35. } IP_OPTION_INFORMATION;
  36. typedef struct tagICMP_ECHO_REPLY 
  37. {
  38.   IPAddr                Address;       // Replying address
  39.   unsigned long         Status;        // Reply IP_STATUS
  40.   unsigned long         RoundTripTime; // RTT in milliseconds
  41.   unsigned short        DataSize;      // Reply data size in bytes
  42.   unsigned short        Reserved;      // Reserved for system use
  43.   void FAR              *Data;         // Pointer to the reply data
  44.   IP_OPTION_INFORMATION Options;       // Reply options
  45. } ICMP_ECHO_REPLY;
  46. typedef IP_OPTION_INFORMATION FAR* LPIP_OPTION_INFORMATION;
  47. typedef ICMP_ECHO_REPLY FAR* LPICMP_ECHO_REPLY;
  48. typedef HANDLE (WINAPI IcmpCreateFile)(VOID);
  49. typedef IcmpCreateFile* lpIcmpCreateFile;
  50. typedef BOOL (WINAPI IcmpCloseHandle)(HANDLE IcmpHandle);
  51. typedef IcmpCloseHandle* lpIcmpCloseHandle;
  52. typedef DWORD (WINAPI IcmpSendEcho)(HANDLE IcmpHandle, IPAddr DestinationAddress,
  53.                                     LPVOID RequestData, WORD RequestSize,
  54.                                     LPIP_OPTION_INFORMATION RequestOptions,
  55.                                     LPVOID ReplyBuffer, DWORD ReplySize, DWORD Timeout);
  56. typedef IcmpSendEcho* lpIcmpSendEcho;
  57. #define MIN_ICMP_PACKET_SIZE 8    //minimum 8 byte icmp packet (just header)
  58. #define MAX_ICMP_PACKET_SIZE 102400 //Maximum icmp packet size
  59. #ifdef CPING_USE_WINSOCK2
  60. #ifndef _WINSOCK2API_
  61. #pragma message("You need to include winsock2.h in your PCH")
  62. #endif
  63. // IP header
  64. typedef struct tagIP_HEADER 
  65. {
  66. unsigned int h_len:4;          // length of the header
  67. unsigned int version:4;        // Version of IP
  68. unsigned char tos;             // Type of service
  69. unsigned short total_len;      // total length of the packet
  70. unsigned short ident;          // unique identifier
  71. unsigned short frag_and_flags; // flags
  72. unsigned char  ttl; 
  73. unsigned char proto;           // protocol (TCP, UDP etc)
  74. unsigned short checksum;       // IP checksum
  75. unsigned int sourceIP;
  76. unsigned int destIP;
  77. } IP_HEADER;
  78. typedef IP_HEADER FAR* LPIP_HEADER;
  79. // ICMP header
  80. typedef struct tagICMP_HEADER 
  81. {
  82.   BYTE i_type;
  83.   BYTE i_code; /* type sub code */
  84.   USHORT i_cksum;
  85.   USHORT i_id;
  86.   USHORT i_seq;
  87.   /* This is not the std header, but we reserve space for time */
  88.   ULONG timestamp;
  89. } ICMP_HEADER;
  90. typedef ICMP_HEADER FAR* LPICMP_HEADER;
  91. void FillIcmpData(LPICMP_HEADER pIcmp, int nData);
  92. BOOL DecodeResponse(char* pBuf, int nBytes, sockaddr_in* from);
  93. USHORT GenerateIPChecksum(USHORT* pBuffer, int nSize);
  94. #endif //CPING_USE_WINSOCK2
  95. /////////////////////////////////  Macros & Statics ///////////////////////////
  96. #ifdef _DEBUG
  97. #define new DEBUG_NEW
  98. #undef THIS_FILE
  99. static char THIS_FILE[] = __FILE__;
  100. #endif
  101. lpIcmpCreateFile s_pIcmpCreateFile = NULL;
  102. lpIcmpSendEcho s_pIcmpSendEcho = NULL;
  103. lpIcmpCloseHandle s_pIcmpCloseHandle = NULL;
  104. HINSTANCE s_hIcmp = NULL;
  105. BOOL s_bAttemptedIcmpInitialise = FALSE;
  106. int CPing::sm_nCount = 0;
  107. BOOL CPing::sm_bWinsock2OK = FALSE;
  108. __int64 CPing::sm_TimerFrequency = 0;
  109. ///////////////////////////////// Implementation //////////////////////////////
  110. CPing::CPing()
  111. {
  112. ++sm_nCount; //Increment the reference count
  113. }
  114. CPing::~CPing()
  115. {
  116. --sm_nCount; //decrement the reference count
  117. //Free up our resources if we are the last one out
  118. if (sm_nCount == 0)
  119. {
  120. FreeLibrary(s_hIcmp);
  121. WSACleanup();
  122. }
  123. }
  124. #ifdef CPING_USE_WINSOCK2
  125. BOOL CPing::Initialise() const
  126. {
  127. if (!s_bAttemptedIcmpInitialise)
  128. {
  129. s_bAttemptedIcmpInitialise = TRUE;
  130. //Initialise the winsock 2 stack
  131. WSADATA wsa;
  132. sm_bWinsock2OK = (WSAStartup(MAKEWORD(2, 1), &wsa) == 0);
  133.     //Use the High performace counter to get an accurate RTT
  134.     LARGE_INTEGER Frequency;
  135.     sm_bWinsock2OK = sm_bWinsock2OK && QueryPerformanceFrequency(&Frequency);
  136.     if (sm_bWinsock2OK)
  137.       sm_TimerFrequency = Frequency.QuadPart;
  138. }
  139. return sm_bWinsock2OK;
  140. }
  141. #else ////CPING_USE_WINSOCK2
  142. BOOL CPing::Initialise() const
  143. {
  144. if (!s_bAttemptedIcmpInitialise)
  145. {
  146. s_bAttemptedIcmpInitialise = TRUE;
  147. //Initialise the winsock stack
  148. WSADATA wsa;
  149. if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0)
  150. {
  151. TRACE(_T("Could not negotiate a correct version of WinSockn"));
  152. return FALSE;
  153. }
  154. //Load up the ICMP library
  155. s_hIcmp = LoadLibrary(_T("ICMP.DLL"));
  156. if (s_hIcmp == NULL)
  157. {
  158. TRACE(_T("Could not load up the ICMP DLLn"));
  159. return FALSE;
  160. }
  161. //Retrieve pointers to the functions in the ICMP dll
  162. s_pIcmpCreateFile = (lpIcmpCreateFile) GetProcAddress(s_hIcmp,"IcmpCreateFile");
  163. s_pIcmpSendEcho = (lpIcmpSendEcho) GetProcAddress(s_hIcmp,"IcmpSendEcho" );
  164. s_pIcmpCloseHandle = (lpIcmpCloseHandle) GetProcAddress(s_hIcmp,"IcmpCloseHandle");
  165. if (s_pIcmpCreateFile == NULL || s_pIcmpSendEcho == NULL || s_pIcmpCloseHandle == NULL)
  166.   TRACE(_T("Could not find ICMP functions in the ICMP DLLn"));
  167. }
  168. return (s_pIcmpCreateFile != NULL && s_pIcmpSendEcho != NULL && s_pIcmpCloseHandle != NULL);
  169. }
  170. #endif //CPING_USE_WINSOCK2
  171. #ifdef CPING_USE_WINSOCK2
  172. BOOL CPing::Ping(LPCTSTR pszHostName, CPingReply& pr, UCHAR /*nTTL*/, DWORD dwTimeout, UCHAR nPacketSize) const
  173. {
  174.   //Parameter validation
  175.   if (nPacketSize > MAX_ICMP_PACKET_SIZE || nPacketSize < MIN_ICMP_PACKET_SIZE)
  176.   {
  177.     ASSERT(FALSE);
  178.     SetLastError(WSAENOBUFS);
  179.     return FALSE;
  180.   }
  181. //For correct operation of the T2A macro, see TN059
  182. USES_CONVERSION;
  183. //Make sure everything is initialised
  184. if (!Initialise())
  185.   return FALSE;
  186.   //Resolve the address of the host to connect to
  187.   sockaddr_in dest;
  188.   memset(&dest,0,sizeof(dest));
  189. LPSTR lpszAscii = T2A((LPTSTR) pszHostName);
  190.   unsigned long addr = inet_addr(lpszAscii);
  191. if (addr == INADDR_NONE)
  192. {
  193. //Not a dotted address, then do a lookup of the name
  194. hostent* hp = gethostbyname(lpszAscii);
  195. if (hp)
  196.     {
  197.       memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);
  198.      dest.sin_family = hp->h_addrtype;
  199.     }
  200.     else
  201.   {
  202.   TRACE(_T("Could not resolve the host name %sn"), pszHostName);
  203.   return FALSE;
  204.     }
  205.   }
  206.   else
  207.   {
  208.     dest.sin_addr.s_addr = addr;
  209.     dest.sin_family = AF_INET;
  210.   }
  211.   //Create the raw socket
  212.   SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
  213.   if (sockRaw == INVALID_SOCKET) 
  214. {
  215.   TRACE(_T("Failed to create a raw socketn"));
  216.   return FALSE;
  217.   }
  218.   //Send the receive timeout
  219.   int nTimeout = dwTimeout;
  220.   int nError = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTimeout, sizeof(nTimeout));
  221.   if (nError == SOCKET_ERROR) 
  222. {
  223.    TRACE(_T("Failed to set recv timeoutn"));
  224.   return FALSE;
  225.   }
  226.   //Set the send timeout (always set to 1 second)
  227.   nTimeout = 1000;
  228.   nError = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&nTimeout, sizeof(nTimeout));
  229.   if (nError == SOCKET_ERROR) 
  230. {  
  231.   TRACE(_T("Failed to set send timeoutn"));
  232.   return FALSE;
  233.   }
  234.   //Allocate the ICMP packet
  235.   int nBufSize = nPacketSize + sizeof(ICMP_HEADER);
  236.   char* pICMP = new char[nBufSize];
  237.   FillIcmpData((LPICMP_HEADER) pICMP, nBufSize);
  238.   //Get the tick count prior to sending the packet
  239.   LARGE_INTEGER TimerTick;
  240.   VERIFY(QueryPerformanceCounter(&TimerTick));
  241.   __int64 nStartTick = TimerTick.QuadPart;
  242.   //Send of the packet
  243. int nWrote = sendto(sockRaw, pICMP, nBufSize, 0, (sockaddr*)&dest, sizeof(dest));
  244. if (nWrote == SOCKET_ERROR)
  245. {
  246. TRACE(_T("sendto failedn"));
  247.     delete [] pICMP;
  248. return FALSE;
  249. }
  250.   //allocate the recv buffer
  251.   char* pRecvBuf = new char[MAX_ICMP_PACKET_SIZE];
  252.   //Receive the response
  253.   sockaddr_in from;
  254.   int nFromlen = sizeof(from);
  255. int nRead = recvfrom(sockRaw, pRecvBuf, MAX_ICMP_PACKET_SIZE, 0, (sockaddr*)&from, &nFromlen);
  256.   //Get the current tick count
  257.   VERIFY(QueryPerformanceCounter(&TimerTick));
  258.   //Now check the return response from recvfrom
  259. if (nRead == SOCKET_ERROR)
  260. {
  261.     DWORD dwError = GetLastError();
  262. TRACE(_T("recvfrom failedn"));
  263.     delete [] pICMP;
  264.     delete [] pRecvBuf;
  265. return FALSE;
  266. }
  267.   //Decode the response we got back
  268.   BOOL bSuccess = DecodeResponse(pRecvBuf, nRead, &from);
  269.   //If we successfully decoded the response, then return the
  270.   //values in the CPingReply instance
  271.   if (bSuccess)
  272.   {
  273.     pr.Address = from.sin_addr;
  274.     pr.RTT = (ULONG) ((TimerTick.QuadPart - nStartTick) * 1000 / sm_TimerFrequency);
  275.   }
  276.   //Free up the memory we allocated
  277.   delete [] pICMP;
  278.   delete [] pRecvBuf;
  279.   //return the status
  280. return bSuccess;
  281. }
  282. #else ////CPING_USE_WINSOCK2
  283. BOOL CPing::Ping(LPCTSTR pszHostName, CPingReply& pr, UCHAR nTTL, DWORD dwTimeout, UCHAR nPacketSize) const
  284. {
  285. //For correct operation of the T2A macro, see TN059
  286. USES_CONVERSION;
  287. //Make sure everything is initialised
  288. if (!Initialise())
  289.   return FALSE;
  290. LPSTR lpszAscii = T2A((LPTSTR) pszHostName);
  291. //Convert from dotted notation if required
  292. unsigned long addr = inet_addr(lpszAscii);
  293. if (addr == INADDR_NONE)
  294. {
  295. //Not a dotted address, then do a lookup of the name
  296. hostent* hp = gethostbyname(lpszAscii);
  297. if (hp)
  298. memcpy(&addr, hp->h_addr, hp->h_length);
  299. else
  300. {
  301. TRACE(_T("Could not resolve the host name %sn"), pszHostName);
  302. return FALSE;
  303. }
  304. }
  305.   //Create the ICMP handle
  306. HANDLE hIP = s_pIcmpCreateFile();
  307. if (hIP == INVALID_HANDLE_VALUE)
  308. {
  309. TRACE(_T("Could not get a valid ICMP handlen"));
  310. return FALSE;
  311. }
  312.   //Set up the option info structure
  313. IP_OPTION_INFORMATION OptionInfo;
  314. ZeroMemory(&OptionInfo, sizeof(IP_OPTION_INFORMATION));
  315. OptionInfo.Ttl = nTTL;
  316.   //Set up the data which will be sent
  317.   unsigned char* pBuf = new unsigned char[nPacketSize];
  318.   memset(pBuf, 'E', nPacketSize);
  319. //Do the actual Ping
  320.   int nReplySize = sizeof(ICMP_ECHO_REPLY) + max(MIN_ICMP_PACKET_SIZE, nPacketSize);
  321.   unsigned char* pReply = new unsigned char[nReplySize];
  322. ICMP_ECHO_REPLY* pEchoReply = (ICMP_ECHO_REPLY*) pReply;
  323.   DWORD nRecvPackets = s_pIcmpSendEcho(hIP, addr, pBuf, nPacketSize, &OptionInfo, pReply, nReplySize, dwTimeout);
  324.   //Check we got the packet back
  325.   BOOL bSuccess = (nRecvPackets == 1);
  326.   //Check the IP status is OK (O is IP Success)
  327.   if (bSuccess && (pEchoReply->Status != 0))
  328.   {
  329.     bSuccess = FALSE;
  330.     SetLastError(pEchoReply->Status);
  331.   }
  332.   //Check we got the same amount of data back as we sent
  333.   if (bSuccess)
  334.   {
  335.     bSuccess = (pEchoReply->DataSize == nPacketSize);
  336.     if (!bSuccess)
  337.       SetLastError(ERROR_UNEXP_NET_ERR);
  338.   }
  339.   //Check the data we got back is what was sent
  340.   if (bSuccess)
  341.   {
  342.     char* pReplyData = (char*) pEchoReply->Data;
  343.     for (int i=0; i<nPacketSize && bSuccess; i++)
  344.       bSuccess = (pReplyData[i] == 'E');
  345.     if (!bSuccess)
  346.       SetLastError(ERROR_UNEXP_NET_ERR);
  347.   }
  348.   //Close the ICMP handle
  349. s_pIcmpCloseHandle(hIP);
  350. if (bSuccess)
  351. {
  352.     //Ping was successful, copy over the pertinent info
  353.     //into the return structure
  354. pr.Address.S_un.S_addr = pEchoReply->Address;
  355.   pr.RTT = pEchoReply->RoundTripTime;
  356. }
  357.   //Free up the memory we allocated
  358.   delete [] pBuf;
  359.   delete [] pReply;
  360.   //return the status
  361. return bSuccess; 
  362. }
  363. #endif //CPING_USE_WINSOCK2
  364. #ifdef CPING_USE_WINSOCK2
  365. //Decode the raw Ip packet we get back
  366. BOOL DecodeResponse(char* pBuf, int nBytes, sockaddr_in* from) 
  367. {
  368.   //Get the current tick count
  369.   LARGE_INTEGER TimerTick;
  370.   VERIFY(QueryPerformanceCounter(&TimerTick));
  371. LPIP_HEADER pIpHdr = (LPIP_HEADER) pBuf;
  372. int nIpHdrlen = pIpHdr->h_len * 4; //Number of 32-bit words*4 = bytes
  373.   //Not enough data recieved
  374. if (nBytes < nIpHdrlen + MIN_ICMP_PACKET_SIZE) 
  375. {
  376. TRACE(_T("Received too few bytes from %sn"), inet_ntoa(from->sin_addr));
  377.     SetLastError(ERROR_UNEXP_NET_ERR);
  378.     return FALSE;
  379. }
  380.   //Check it is an ICMP_ECHOREPLY packet
  381. LPICMP_HEADER pIcmpHdr = (LPICMP_HEADER) (pBuf + nIpHdrlen);
  382. if (pIcmpHdr->i_type != 0) //type ICMP_ECHOREPLY is 0
  383. {
  384. TRACE(_T("non-echo type %d recvdn"), pIcmpHdr->i_type);
  385.     SetLastError(ERROR_UNEXP_NET_ERR);
  386. return FALSE;
  387. }
  388.   //Check it is the same id as we sent
  389. if (pIcmpHdr->i_id != (USHORT)GetCurrentProcessId()) 
  390. {
  391. TRACE(_T("Received someone else's packet!n"));
  392.     SetLastError(ERROR_UNEXP_NET_ERR);
  393. return FALSE;
  394. }
  395.   return TRUE;
  396. }
  397. //generate an IP checksum based on a given data buffer
  398. USHORT GenerateIPChecksum(USHORT* pBuffer, int nSize) 
  399. {
  400.   unsigned long cksum = 0;
  401.   while (nSize > 1) 
  402. {
  403.   cksum += *pBuffer++;
  404.   nSize -= sizeof(USHORT);
  405.   }
  406.   
  407.   if (nSize) 
  408.   cksum += *(UCHAR*)pBuffer;
  409.   cksum = (cksum >> 16) + (cksum & 0xffff);
  410.   cksum += (cksum >>16);
  411.   return (USHORT)(~cksum);
  412. }
  413. //Fill up the ICMP packet with defined values
  414. void FillIcmpData(LPICMP_HEADER pIcmp, int nData)
  415. {
  416.   pIcmp->i_type    = 8; //ICMP_ECHO type
  417.   pIcmp->i_code    = 0;
  418.   pIcmp->i_id      = (USHORT) GetCurrentProcessId();
  419.   pIcmp->i_seq     = 0;
  420.   pIcmp->i_cksum   = 0;
  421.   pIcmp->timestamp = GetTickCount();
  422.  
  423.   //Set up the data which will be sent
  424.   int nHdrSize = sizeof(ICMP_HEADER);
  425.   char* pData = (char*) (pIcmp + nHdrSize);
  426.   memset(pData, 'E', nData - nHdrSize);
  427.   //Generate the checksum
  428.   pIcmp->i_cksum = GenerateIPChecksum((USHORT*)pIcmp, nData);
  429. }
  430. #endif //CPING_USE_WINSOCK2