// ping.cpp : 定义控制台应用程序的入口点。
//
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#define ICMP_ECHO 8
#define ICMP_ECHOREPLY 0
/* The IP header */
typedef struct iphdr
{
unsigned char h_len:4; // length of the header
unsigned char 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;
}IpHeader;
/* ICMP header */
typedef struct _ihdr
{
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;
}IcmpHeader;
#define STATUS_FAILED 0xFFFF
#define DEF_PACKET_SIZE 32
#define MAX_PACKET 1024
/* The response is an IP packet. We must decode the IP header to locate the ICMP data */
void decode_resp(char *buf, int bytes,struct sockaddr_in *from)
{
IpHeader *iphdr;
IcmpHeader *icmphdr;
unsigned short iphdrlen;
iphdr=(IpHeader*)buf;
iphdrlen=sizeof(IpHeader)+sizeof(IcmpHeader);
if(bytes<iphdrlen)
{
printf("Too few bytes from %s\n",inet_ntoa(from->sin_addr));
}
icmphdr=(IcmpHeader*)(buf+sizeof(IpHeader));
if(icmphdr->i_type!=ICMP_ECHOREPLY)
{
fprintf(stderr,"non-echo type %d recvd\n",icmphdr->i_type); return;
}
if(icmphdr->i_id!=(USHORT)GetCurrentProcessId())
{
fprintf(stderr,"someone else's packet!\n"); return ;
}
int correct=0;
for(int k=0;k<bytes-iphdrlen;k++)
{
if(*(buf+iphdrlen+k)=='E')
{
correct++;
}
}
printf("来自 %s 的回复: 字节=%d 时间=%dms TTL=%d\n",\
inet_ntoa(from->sin_addr),correct,GetTickCount()-icmphdr->timestamp,iphdr->ttl);
}
USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum=0;
while(size>1)
{
cksum+=*buffer++;
size-=sizeof(USHORT);
}
if(size)
{
cksum+=*(UCHAR*)buffer;
}
cksum=(cksum>>16)+(cksum&0xffff);
cksum+=(cksum>>16);
return (USHORT)(~cksum);
}
/* Helper function to fill in various stuff in our ICMP request. */
void fill_icmp_data(char *icmp_data, int datasize)
{
char *datapart=NULL;
IcmpHeader *icmp_hdr=NULL;
icmp_hdr=(IcmpHeader*)icmp_data;
icmp_hdr->i_type=ICMP_ECHO;
icmp_hdr->i_code=0;
icmp_hdr->i_id=(USHORT)GetCurrentProcessId();
icmp_hdr->i_cksum=0;
icmp_hdr->i_seq++;
icmp_hdr->timestamp=GetTickCount();
datapart=icmp_data+sizeof(IcmpHeader); // Place some junk in the buffer.
memset(datapart,'E',datasize-sizeof(IcmpHeader));
icmp_hdr->i_cksum=checksum((USHORT*)icmp_data,datasize);
}
int main(int argc, char *argv[])
{
int count=1;
int error=-1;
int datasize=0;
int timeout=3000;
struct sockaddr_in addrServer;
struct hostent *phostent=NULL;
int destlen=sizeof(addrServer);
char *dest_ip=NULL;
char icmp_data[MAX_PACKET];
char recvbuf[MAX_PACKET];
unsigned int addr=0;
char srcIP[MAX_PATH]="0.0.0.0";
char dstIP[MAX_PATH]="127.0.0.1";
for(int n=1;n<argc;)
{
if(!strcmp(argv[n],"-S"))
{
n++;strcpy_s(srcIP,argv[n++]);
}
else if(!strcmp(argv[n],"-n"))
{
n++;count=atoi(argv[n++]);
}
else
{
strcpy_s(dstIP,argv[n++]);
}
}
WSADATA wsaData={0};
if(WSAStartup(MAKEWORD(1,1),&wsaData)!=0)
{
printf("WSA startup error\n");
ExitProcess(STATUS_FAILED);
}
SOCKET sockClient=WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0,0);
if(sockClient==INVALID_SOCKET)
{
printf("WSA socket error\n");
ExitProcess(STATUS_FAILED);
}
sockaddr_in addrClient;
addrClient.sin_addr.S_un.S_addr=inet_addr(srcIP);
addrClient.sin_family=AF_INET;
addrClient.sin_port=htons(0);
error=bind(sockClient,(SOCKADDR *)&addrClient,sizeof(addrClient));
if(error)
{
closesocket(sockClient);
printf("bind client error\n");
return WSACleanup();
}
unsigned long ul=true;
error=ioctlsocket(sockClient,FIONBIO,(unsigned long*)&ul);
if(error)
{
closesocket(sockClient);
printf("set ioctlsocket error\n");
return WSACleanup();
}
error=setsockopt(sockClient,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout, sizeof(timeout));
if(error==SOCKET_ERROR)
{
fprintf(stderr,"failed to set recv timeout: %d\n",WSAGetLastError());
ExitProcess(STATUS_FAILED);
}
error=setsockopt(sockClient,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout, sizeof(timeout));
if(error==SOCKET_ERROR)
{
fprintf(stderr,"failed to set send timeout: %d\n",WSAGetLastError());
ExitProcess(STATUS_FAILED);
}
memset(&addrServer,0,sizeof(addrServer));
phostent=gethostbyname(dstIP);
if(!phostent)
{
addr=inet_addr(dstIP);
}
if((!phostent)&&(addr==INADDR_NONE))
{
fprintf(stderr,"Unable to resolve host %s\n",dstIP);
ExitProcess(STATUS_FAILED);
}
if(phostent!=NULL)
{
memcpy(&(addrServer.sin_addr),phostent->h_addr,phostent->h_length);
}
else
{
addrServer.sin_addr.s_addr=addr;
}
if(phostent)
{
addrServer.sin_family=phostent->h_addrtype;
}
else
{
addrServer.sin_family=AF_INET;
dest_ip=inet_ntoa(addrServer.sin_addr);
}
datasize=DEF_PACKET_SIZE;
datasize+=sizeof(IcmpHeader);
memset(icmp_data,0,MAX_PACKET);
for(int i=0;i<count;i++)
{
fill_icmp_data(icmp_data,datasize);
int num=sendto(sockClient,icmp_data,datasize,0,(struct sockaddr*)&addrServer, sizeof(addrServer));
if(num==SOCKET_ERROR)
{
error=WSAGetLastError();
if(error==WSAETIMEDOUT)
{
fprintf(stderr,"errorcode=%d\n",error);
continue;
}
}
if(num<datasize)
{
fprintf(stdout,"Write %d bytes\n",num);
}
timeval tm;
int len=sizeof(int);
fd_set set;
for(int k=0;k<3;k++)
{
num=recvfrom(sockClient,recvbuf,MAX_PACKET,0,(struct sockaddr*)&addrServer, &destlen);
if(num!=SOCKET_ERROR)
{
break;
}
if(num==SOCKET_ERROR)
{
Sleep(1);
tm.tv_sec=0;
tm.tv_usec=1;
FD_ZERO(&set);
FD_SET(sockClient,&set);
if(select(sockClient,NULL,&set,NULL,&tm)>0)
{
getsockopt(sockClient,SOL_SOCKET,SO_ERROR,(char*)&error,&len);
}
}
}
if(num!=SOCKET_ERROR)
{
decode_resp(recvbuf,num,&addrServer);
}
else
{
printf("Can not find host %s\n",dstIP);
}
if(i>0)
{
Sleep(1000);
}
}
closesocket(sockClient);
WSACleanup();
return 0;
}
编译命令如下:
g++ -std=c++2a -o ping.exe ping.cpp -lws2_32