- //
- // Ping.h
- //
- #pragma pack(push)
- #pragma pack(1)
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <winsock.h>
- class CPing
- {
- #define ICMP_ECHOREPLY 0
- #define ICMP_ECHOREQ 8
- // IP Header -- RFC 791
- typedef struct tagIPHDR
- {
- u_char VIHL; // Version and IHL
- u_char TOS; // Type Of Service
- short TotLen; // Total Length
- short ID; // Identification
- short FlagOff; // Flags and Fragment Offset
- u_char TTL; // Time To Live
- u_char Protocol; // Protocol
- u_short Checksum; // Checksum
- struct in_addr iaSrc; // Internet Address - Source
- struct in_addr iaDst; // Internet Address - Destination
- }IPHDR, *PIPHDR;
- // ICMP Header - RFC 792
- typedef struct tagICMPHDR
- {
- u_char Type; // Type
- u_char Code; // Code
- u_short Checksum; // Checksum
- u_short ID; // Identification
- u_short Seq; // Sequence
- char Data; // Data
- }ICMPHDR, *PICMPHDR;
- // ICMP Echo Request
- #define REQ_DATASIZE 32 // Echo Request Data size
- typedef struct tagECHOREQUEST
- {
- ICMPHDR icmpHdr;
- DWORD dwTime;
- char cData[REQ_DATASIZE];
- }ECHOREQUEST, *PECHOREQUEST;
- // ICMP Echo Reply
- typedef struct tagECHOREPLY
- {
- IPHDR ipHdr;
- ECHOREQUEST echoRequest;
- char cFiller[256];
- }ECHOREPLY, *PECHOREPLY;
- private:
- bool bInitWinSockOK;
- u_short in_cksum(u_short *addr, int len);
- int SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr);
- DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL);
- int WaitForEchoReply(SOCKET s);
- void Report(LPCTSTR format, ...);
- public:
- CPing();
- virtual ~CPing();
- void ping(LPCSTR pstrHost);
- };
- #pragma pack(pop)
- //
- // PING.CPP -- Ping program using ICMP and RAW Sockets
- //
- #include "ping.h"
- #pragma comment(lib, "ws2_32.lib")
- CPing::CPing()
- {
- bInitWinSockOK = false;
- // Init WinSock
- WSADATA wsaData;
- WORD wVersionRequested = MAKEWORD(1,1);
- if ( WSAStartup(wVersionRequested, &wsaData) )
- {
- Report("/nError initializing WinSock/n");
- }
- else if (wsaData.wVersion != wVersionRequested)// Check version
- {
- Report("/nWinSock version not supported/n");
- }
- else
- {
- bInitWinSockOK = true;
- }
- }
- CPing::~CPing()
- {
- if(bInitWinSockOK)
- WSACleanup();// Free WinSock
- }
- // ping()
- // Calls SendEchoRequest() and
- // RecvEchoReply() and retport results
- void CPing::ping(LPCSTR pstrHost)
- {
- SOCKET rawSocket;
- LPHOSTENT lpHost;
- struct sockaddr_in saDest;
- struct sockaddr_in saSrc;
- DWORD dwTimeSent;
- DWORD dwElapsed;
- u_char cTTL;
- int nLoop;
- int nRet;
- if(!bInitWinSockOK)
- {
- Report("/nWinSock must be initializing/n");
- return;
- }
- // Create a Raw socket
- rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
- if (rawSocket == SOCKET_ERROR)
- {
- Report("socket() error: %d/n", WSAGetLastError());
- return;
- }
- // Lookup host
- lpHost = gethostbyname(pstrHost);
- if (lpHost == NULL)
- {
- Report("/nHost not found: %s/n", pstrHost);
- return;
- }
- // Setup destination socket address
- saDest.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr));
- saDest.sin_family = AF_INET;
- saDest.sin_port = 0;
- // Tell the user what we're doing
- Report("/nPinging %s [%s] with %d bytes of data:/n",
- pstrHost,
- inet_ntoa(saDest.sin_addr),
- REQ_DATASIZE);
- // Ping multiple times
- for (nLoop = 0; nLoop < 4; nLoop++)
- {
- // Send ICMP echo request
- SendEchoRequest(rawSocket, &saDest);
- // Use select() to wait for data to be received
- nRet = WaitForEchoReply(rawSocket);
- if (nRet == SOCKET_ERROR)
- {
- Report("select() error: %d/n", WSAGetLastError());
- break;
- }
- if (!nRet)
- {
- Report("/nTimeOut");
- break;
- }
- // Receive reply
- dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);
- // Calculate elapsed time
- dwElapsed = GetTickCount() - dwTimeSent;
- Report("/nReply from: %s: bytes=%d time=%ldms TTL=%d",
- inet_ntoa(saSrc.sin_addr),
- REQ_DATASIZE,
- dwElapsed,
- cTTL);
- }
- Report("/n");
- nRet = closesocket(rawSocket);
- if (nRet == SOCKET_ERROR)
- Report("closesocket() error: %d/n", WSAGetLastError());
- }
- void CPing::Report(LPCSTR format, ...)
- {
- char _Buff[8192];
- memset(_Buff, 0, sizeof(_Buff));
- va_list arg;
- va_start(arg, format);
- int charSize = _vsnprintf(_Buff, sizeof(_Buff), format, arg);
- va_end(arg);
- OutputDebugStringA(_Buff);
- }
- //
- // Checksum routine for Internet Protocol family headers (C Version)
- u_short CPing::in_cksum(u_short *addr, int len)
- {
- register int nleft = len;
- register u_short *w = addr;
- register u_short answer;
- register int sum = 0;
- /*
- * Our algorithm is simple, using a 32 bit accumulator (sum),
- * we add sequential 16 bit words to it, and at the end, fold
- * back all the carry bits from the top 16 bits into the lower
- * 16 bits.
- */
- while( nleft > 1 )
- {
- sum += *w++;
- nleft -= 2;
- }
- /* mop up an odd byte, if necessary */
- if( nleft == 1 )
- {
- u_short u = 0;
- *(u_char *)(&u) = *(u_char *)w ;
- sum += u;
- }
- /*
- * add back carry outs from top 16 bits to low 16 bits
- */
- sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
- sum += (sum >> 16); /* add carry */
- answer = ~sum; /* truncate to 16 bits */
- return (answer);
- }
- // SendEchoRequest()
- // Fill in echo request header
- // and send to destination
- int CPing::SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr)
- {
- static ECHOREQUEST echoReq;
- static u_short nId = 1;
- static u_short nSeq = 1;
- int nRet;
- // Fill in echo request
- echoReq.icmpHdr.Type = ICMP_ECHOREQ;
- echoReq.icmpHdr.Code = 0;
- echoReq.icmpHdr.Checksum = 0;
- echoReq.icmpHdr.ID = nId++;
- echoReq.icmpHdr.Seq = nSeq++;
- // Fill in some data to send
- for (nRet = 0; nRet < REQ_DATASIZE; nRet++)
- echoReq.cData[nRet] = ' '+nRet;
- // Save tick count when sent
- echoReq.dwTime = GetTickCount();
- // Put data in packet and compute checksum
- echoReq.icmpHdr.Checksum = in_cksum((u_short *)&echoReq, sizeof(ECHOREQUEST));
- // Send the echo request
- nRet = sendto(s, /* socket */
- (LPSTR)&echoReq, /* buffer */
- sizeof(ECHOREQUEST),
- 0, /* flags */
- (LPSOCKADDR)lpstToAddr, /* destination */
- sizeof(SOCKADDR_IN)); /* address length */
- if (nRet == SOCKET_ERROR)
- Report("sendto() error: %d/n", WSAGetLastError());
- return (nRet);
- }
- // RecvEchoReply()
- // Receive incoming data
- // and parse out fields
- DWORD CPing::RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL)
- {
- ECHOREPLY echoReply;
- int nRet;
- int nAddrLen = sizeof(struct sockaddr_in);
- // Receive the echo reply
- nRet = recvfrom(s, // socket
- (LPSTR)&echoReply, // buffer
- sizeof(ECHOREPLY), // size of buffer
- 0, // flags
- (LPSOCKADDR)lpsaFrom, // From address
- &nAddrLen); // pointer to address len
- // Check return value
- if (nRet == SOCKET_ERROR)
- Report("recvfrom() error: %d/n", WSAGetLastError());
- // return time sent and IP TTL
- *pTTL = echoReply.ipHdr.TTL;
- return(echoReply.echoRequest.dwTime);
- }
- // WaitForEchoReply()
- // Use select() to determine when
- // data is waiting to be read
- int CPing::WaitForEchoReply(SOCKET s)
- {
- struct timeval Timeout;
- fd_set readfds;
- readfds.fd_count = 1;
- readfds.fd_array[0] = s;
- Timeout.tv_sec = 5;
- Timeout.tv_usec = 0;
- return(select(1, &readfds, NULL, NULL, &Timeout));
- }
- //test code
- CPing ping;
- ping.ping( "www.sina.com.cn" );
- //output
- Pinging www.sina.com.cn [61.172.201.194] with 32 bytes of data:
- Reply from: 61.172.201.194: bytes=32 time=15ms TTL=249
- Reply from: 61.172.201.194: bytes=32 time=16ms TTL=249
- Reply from: 61.172.201.194: bytes=32 time=0ms TTL=249
- Reply from: 61.172.201.194: bytes=32 time=15ms TTL=249