使用 ICMP 和 RAW Sockets实现 ping 类

来源:互联网 发布:广联达软件管家打不开 编辑:程序博客网 时间:2024/05/21 23:32

[cpp] view plaincopy
  1. //  
  2. // Ping.h  
  3. //  
  4.   
  5. #pragma pack(push)  
  6. #pragma pack(1)  
  7.   
  8. #include <stdio.h>  
  9. #include <stdlib.h>  
  10. #include <stdarg.h>  
  11. #include <winsock.h>  
  12.   
  13. class CPing  
  14. {  
  15.   #define ICMP_ECHOREPLY    0  
  16.   #define ICMP_ECHOREQ  8  
  17.   
  18.   // IP Header -- RFC 791  
  19.   typedef struct tagIPHDR  
  20.   {  
  21.     u_char  VIHL;           // Version and IHL  
  22.     u_char  TOS;            // Type Of Service  
  23.     short   TotLen;         // Total Length  
  24.     short   ID;             // Identification  
  25.     short   FlagOff;        // Flags and Fragment Offset  
  26.     u_char  TTL;            // Time To Live  
  27.     u_char  Protocol;       // Protocol  
  28.     u_short Checksum;       // Checksum  
  29.     struct  in_addr iaSrc;  // Internet Address - Source  
  30.     struct  in_addr iaDst;  // Internet Address - Destination  
  31.   }IPHDR, *PIPHDR;  
  32.   
  33.   // ICMP Header - RFC 792  
  34.   typedef struct tagICMPHDR  
  35.   {  
  36.     u_char  Type;           // Type  
  37.     u_char  Code;           // Code  
  38.     u_short Checksum;       // Checksum  
  39.     u_short ID;             // Identification  
  40.     u_short Seq;            // Sequence  
  41.     char    Data;           // Data  
  42.   }ICMPHDR, *PICMPHDR;  
  43.   
  44.   // ICMP Echo Request  
  45.   #define REQ_DATASIZE 32       // Echo Request Data size  
  46.   typedef struct tagECHOREQUEST  
  47.   {  
  48.     ICMPHDR icmpHdr;  
  49.     DWORD   dwTime;  
  50.     char    cData[REQ_DATASIZE];  
  51.   }ECHOREQUEST, *PECHOREQUEST;  
  52.   
  53.   // ICMP Echo Reply  
  54.   typedef struct tagECHOREPLY  
  55.   {  
  56.     IPHDR   ipHdr;  
  57.     ECHOREQUEST echoRequest;  
  58.     char    cFiller[256];  
  59.   }ECHOREPLY, *PECHOREPLY;  
  60.   
  61. private:  
  62.   bool bInitWinSockOK;  
  63.   u_short in_cksum(u_short *addr, int len);  
  64.   int SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr);  
  65.   DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL);  
  66.   int WaitForEchoReply(SOCKET s);  
  67.   void Report(LPCTSTR format, ...);  
  68.   
  69. public:  
  70.   CPing();  
  71.   virtual ~CPing();  
  72.   
  73.   void ping(LPCSTR pstrHost);  
  74.   
  75. };  
  76.   
  77. #pragma pack(pop)  

 

 

[cpp] view plaincopy
  1. //  
  2. // PING.CPP -- Ping program using ICMP and RAW Sockets  
  3. //  
  4.   
  5. #include "ping.h"  
  6. #pragma comment(lib, "ws2_32.lib")  
  7.   
  8. CPing::CPing()  
  9. {  
  10.   bInitWinSockOK = false;  
  11.   
  12.   // Init WinSock  
  13.   WSADATA wsaData;  
  14.   WORD wVersionRequested = MAKEWORD(1,1);  
  15.   if ( WSAStartup(wVersionRequested, &wsaData) )  
  16.   {  
  17.     Report("/nError initializing WinSock/n");  
  18.   }    
  19.   else if (wsaData.wVersion != wVersionRequested)// Check version  
  20.   {  
  21.     Report("/nWinSock version not supported/n");  
  22.   }  
  23.   else  
  24.   {  
  25.     bInitWinSockOK = true;  
  26.   }  
  27. }  
  28.   
  29. CPing::~CPing()  
  30. {  
  31.   if(bInitWinSockOK)  
  32.     WSACleanup();// Free WinSock  
  33. }  
  34.   
  35. // ping()  
  36. // Calls SendEchoRequest() and  
  37. // RecvEchoReply() and retport results  
  38. void CPing::ping(LPCSTR pstrHost)  
  39. {  
  40.   SOCKET      rawSocket;  
  41.   LPHOSTENT lpHost;  
  42.   struct    sockaddr_in saDest;  
  43.   struct    sockaddr_in saSrc;  
  44.   DWORD   dwTimeSent;  
  45.   DWORD   dwElapsed;  
  46.   u_char    cTTL;  
  47.   int       nLoop;  
  48.   int       nRet;  
  49.   
  50.   if(!bInitWinSockOK)  
  51.   {  
  52.     Report("/nWinSock must be initializing/n");  
  53.     return;  
  54.   }  
  55.   
  56.   // Create a Raw socket  
  57.   rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);  
  58.   if (rawSocket == SOCKET_ERROR)   
  59.   {  
  60.     Report("socket() error: %d/n", WSAGetLastError());  
  61.     return;  
  62.   }  
  63.   
  64.   // Lookup host  
  65.   lpHost = gethostbyname(pstrHost);  
  66.   if (lpHost == NULL)  
  67.   {  
  68.     Report("/nHost not found: %s/n", pstrHost);  
  69.     return;  
  70.   }  
  71.   
  72.   // Setup destination socket address  
  73.   saDest.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr));  
  74.   saDest.sin_family = AF_INET;  
  75.   saDest.sin_port = 0;  
  76.   
  77.   // Tell the user what we're doing  
  78.   Report("/nPinging %s [%s] with %d bytes of data:/n",  
  79.     pstrHost,  
  80.     inet_ntoa(saDest.sin_addr),  
  81.     REQ_DATASIZE);  
  82.   
  83.   // Ping multiple times  
  84.   for (nLoop = 0; nLoop < 4; nLoop++)  
  85.   {  
  86.     // Send ICMP echo request  
  87.     SendEchoRequest(rawSocket, &saDest);  
  88.   
  89.     // Use select() to wait for data to be received  
  90.     nRet = WaitForEchoReply(rawSocket);  
  91.     if (nRet == SOCKET_ERROR)  
  92.     {  
  93.       Report("select() error: %d/n", WSAGetLastError());  
  94.       break;  
  95.     }  
  96.     if (!nRet)  
  97.     {  
  98.       Report("/nTimeOut");  
  99.       break;  
  100.     }  
  101.   
  102.     // Receive reply  
  103.     dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);  
  104.   
  105.     // Calculate elapsed time  
  106.     dwElapsed = GetTickCount() - dwTimeSent;  
  107.     Report("/nReply from: %s: bytes=%d time=%ldms TTL=%d",   
  108.       inet_ntoa(saSrc.sin_addr),   
  109.       REQ_DATASIZE,  
  110.       dwElapsed,  
  111.       cTTL);  
  112.   }  
  113.   Report("/n");  
  114.   nRet = closesocket(rawSocket);  
  115.   if (nRet == SOCKET_ERROR)  
  116.     Report("closesocket() error: %d/n", WSAGetLastError());  
  117. }  
  118.   
  119. void CPing::Report(LPCSTR format, ...)  
  120. {  
  121.   char _Buff[8192];  
  122.   memset(_Buff, 0, sizeof(_Buff));  
  123.   
  124.     va_list arg;    
  125.     va_start(arg, format);  
  126.     int charSize = _vsnprintf(_Buff, sizeof(_Buff), format, arg);  
  127.     va_end(arg);  
  128.     
  129.   OutputDebugStringA(_Buff);  
  130. }  
  131.   
  132. //  
  133. //  Checksum routine for Internet Protocol family headers (C Version)  
  134. u_short CPing::in_cksum(u_short *addr, int len)  
  135. {  
  136.   register int nleft = len;  
  137.   register u_short *w = addr;  
  138.   register u_short answer;  
  139.   register int sum = 0;  
  140.   
  141.   /* 
  142.   *  Our algorithm is simple, using a 32 bit accumulator (sum), 
  143.   *  we add sequential 16 bit words to it, and at the end, fold 
  144.   *  back all the carry bits from the top 16 bits into the lower 
  145.   *  16 bits. 
  146.   */  
  147.   while( nleft > 1 )    
  148.   {  
  149.     sum += *w++;  
  150.     nleft -= 2;  
  151.   }  
  152.   
  153.   /* mop up an odd byte, if necessary */  
  154.   if( nleft == 1 )   
  155.   {  
  156.     u_short u = 0;  
  157.     *(u_char *)(&u) = *(u_char *)w ;  
  158.     sum += u;  
  159.   }  
  160.   
  161.   /* 
  162.   * add back carry outs from top 16 bits to low 16 bits 
  163.   */  
  164.   sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */  
  165.   sum += (sum >> 16);         /* add carry */  
  166.   answer = ~sum;                /* truncate to 16 bits */  
  167.   return (answer);  
  168. }  
  169.   
  170. // SendEchoRequest()  
  171. // Fill in echo request header  
  172. // and send to destination  
  173. int CPing::SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr)   
  174. {  
  175.   static ECHOREQUEST echoReq;  
  176.   static u_short nId = 1;  
  177.   static u_short nSeq = 1;  
  178.   int nRet;  
  179.   
  180.   // Fill in echo request  
  181.   echoReq.icmpHdr.Type      = ICMP_ECHOREQ;  
  182.   echoReq.icmpHdr.Code      = 0;  
  183.   echoReq.icmpHdr.Checksum  = 0;  
  184.   echoReq.icmpHdr.ID            = nId++;  
  185.   echoReq.icmpHdr.Seq           = nSeq++;  
  186.   
  187.   // Fill in some data to send  
  188.   for (nRet = 0; nRet < REQ_DATASIZE; nRet++)  
  189.     echoReq.cData[nRet] = ' '+nRet;  
  190.   
  191.   // Save tick count when sent  
  192.   echoReq.dwTime                = GetTickCount();  
  193.   
  194.   // Put data in packet and compute checksum  
  195.   echoReq.icmpHdr.Checksum = in_cksum((u_short *)&echoReq, sizeof(ECHOREQUEST));  
  196.   
  197.   // Send the echo request                                      
  198.   nRet = sendto(s,                      /* socket */  
  199.            (LPSTR)&echoReq,         /* buffer */  
  200.            sizeof(ECHOREQUEST),  
  201.            0,                           /* flags */  
  202.            (LPSOCKADDR)lpstToAddr, /* destination */  
  203.            sizeof(SOCKADDR_IN));   /* address length */  
  204.   
  205.   if (nRet == SOCKET_ERROR)   
  206.     Report("sendto() error: %d/n", WSAGetLastError());  
  207.   return (nRet);  
  208. }  
  209.   
  210. // RecvEchoReply()  
  211. // Receive incoming data  
  212. // and parse out fields  
  213. DWORD CPing::RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL)   
  214. {  
  215.   ECHOREPLY echoReply;  
  216.   int nRet;  
  217.   int nAddrLen = sizeof(struct sockaddr_in);  
  218.   
  219.   // Receive the echo reply   
  220.   nRet = recvfrom(s,                    // socket  
  221.               (LPSTR)&echoReply,    // buffer  
  222.               sizeof(ECHOREPLY),    // size of buffer  
  223.               0,                    // flags  
  224.               (LPSOCKADDR)lpsaFrom, // From address  
  225.               &nAddrLen);           // pointer to address len  
  226.   
  227.   // Check return value  
  228.   if (nRet == SOCKET_ERROR)   
  229.     Report("recvfrom() error: %d/n", WSAGetLastError());  
  230.   
  231.   // return time sent and IP TTL  
  232.   *pTTL = echoReply.ipHdr.TTL;  
  233.   return(echoReply.echoRequest.dwTime);           
  234. }  
  235.   
  236. // WaitForEchoReply()  
  237. // Use select() to determine when  
  238. // data is waiting to be read  
  239. int CPing::WaitForEchoReply(SOCKET s)  
  240. {  
  241.   struct timeval Timeout;  
  242.   fd_set readfds;  
  243.   
  244.   readfds.fd_count = 1;  
  245.   readfds.fd_array[0] = s;  
  246.   Timeout.tv_sec = 5;  
  247.   Timeout.tv_usec = 0;  
  248.   
  249.   return(select(1, &readfds, NULL, NULL, &Timeout));  
  250. }  

 

[cpp] view plaincopy
  1. //test code  
  2.   CPing ping;  
  3.   ping.ping( "www.sina.com.cn" );  
  4.   
  5. //output  
  6. Pinging www.sina.com.cn [61.172.201.194] with 32 bytes of data:  
  7.   
  8. Reply from: 61.172.201.194: bytes=32 time=15ms TTL=249  
  9. Reply from: 61.172.201.194: bytes=32 time=16ms TTL=249  
  10. Reply from: 61.172.201.194: bytes=32 time=0ms TTL=249  
  11. Reply from: 61.172.201.194: bytes=32 time=15ms TTL=249  

使用 ICMP 和 RAW Sockets实现 ping 类
0 0
原创粉丝点击