#define WIN32_LEAN_AND_MEAN
#include <winsock2.h> //使用原始套接字需要WinSock2的支持
#include <ws2tcpip.h> //进行IPPROTO_IP级别设置时用到
#include <stdio.h>
#include <stdlib.h>
#define ICMP_ECHO 8 //发送Ping请求时的ICMP报文类型
#define ICMP_ECHOREPLY 0 //接收Ping回复时的ICMP报文类型
#define ICMP_TIMEOUT 11 //ICMP超时报文类型
#define ICMP_MIN 8 //Minimum 8-byte ICMP packet (header)
#define MAX_PACKET 1024 //Max ICMP packet size
#define DEICMP_PACKSIZE 44 //Defaut ICMP PACKET SIZE
char lpdest[16]; //用来存放目的IP地址
DWORD cStartTickCount; //用来存放发送包的起始时间
typedef struct _icmphdr //ICMP头部定义,被封装在IP包中
{
BYTE i_type; //报文类型
BYTE i_code; //代码
USHORT i_cksum; //校验和
USHORT i_id; //标识符
USHORT i_seq; //序号
}IcmpHeader;
//初始化ICMP头部
void FillICMPData(char *icmp_data,int datasize)
{
IcmpHeader *icmp_hdr=NULL;
char *datapart=NULL;
icmp_hdr=(IcmpHeader *)icmp_data;
icmp_hdr->i_type=ICMP_ECHO; //request an ICMP echo
icmp_hdr->i_code=0;
icmp_hdr->i_id=(USHORT)GetCurrentProcessId();
icmp_hdr->i_cksum=0;
icmp_hdr->i_seq=0;
datapart=icmp_data+sizeof(IcmpHeader);
memset(datapart,'E',datasize-sizeof(IcmpHeader));
}
//定义IP选项中记录路由选项的结构
typedef struct _IpRouterOptions
{
BYTE code;//代码
BYTE len;//选项长度
BYTE ptr;//指针
BYTE ip_addr;//最多lO个IP地址(实际个)(每个字节) 9 4
}IpRouterOptions;
//定义IP探测信息结构(探测队列(等待回复包队列)中保存的信息)
typedef struct _IPProbelnfo
{
DWORD dwIP;//IP地址
BYTE byProbeTTL;//发送探测信号时所用的TTL值
BYTE byProbeStep;//探测所位于的阶段
BYTE byRetryTimes;//探测重试的次数
B00L bRouterRecord;//是否使用记录路由选项
DWORD dwlPGateway;//探测子网时子网的网关IP地址
DWORD dwIPLast;//探测子网时上一次探测成功的IP地址(属于当前的子网)
DWORD dwIPFirst;//探测子网时起始IP地址(当前子网发现的第一个IP地址)
WORD wProbeIdentify;//发送探测信号时所用的识别号
WORD wProbeSerial;//发送探测信号时所用的序号
unsigned nTimeCount;//超时计算中时间计数器(以0.1s为单位)
}IPProbelnfo;
//校验和函数
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);;
}
int DecodeIPHeader(char *buf,int bytes,struct sockaddr_in *from)
{
IcmpHeader *icmphdr=NULL;
DWORD tick;
static int icmpcount=1;
unsigned short iphdrlen;
//判断接收操作是否超时
if(!buf)
{
printf("%2d: ***.***.***.*** Request timed out.\n",icmpcount++);
return 1;
}
tick=GetTickCount();
iphdrlen=(buf[0] & 0x0f)*4;
icmphdr=(IcmpHeader *)(buf+iphdrlen);
if(bytes<iphdrlen+ICMP_MIN)
{
printf("Too few bytes from %s\n",inet_ntoa(from->sin_addr));
return 0;
}
//判断接收的ICMP报文是否为超时报文
if(icmphdr->i_type==ICMP_TIMEOUT&&icmphdr->i_code==0)
{
printf("%2d: %-15s %4dms\n",icmpcount++,inet_ntoa(from->sin_addr),tick-cStartTickCount);
return 0;
}
//判断接收的ICMP报文是否为回复报文
else if(icmphdr->i_type==ICMP_ECHOREPLY&&icmphdr->i_id==GetCurrentProcessId())
{
printf("%2d: %-15s %4dms\n",icmpcount++,inet_ntoa(from->sin_addr),tick-cStartTickCount);
printf("Trace complete!\n");
return 1;
}
//其他类型,表示不可达
else
{
printf("%2d: Destination host is unreachable!\n",icmpcount++);
return 1;
}
}
int main()
{
WSADATA wsaData;
SOCKET sockRaw=INVALID_SOCKET;
struct sockaddr_in dest,
from;
int i,bread,fromlen=sizeof(from),timeout=1000,ret;
struct hostent *hp=NULL;
char *icmp_data=NULL,*recvbuf=NULL;
USHORT seq_no=0;
printf("Destination Address(IP/Host name):");
scanf("%s",lpdest);
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
{
printf("WSAStartup() failed:%d\n",GetLastError());
return -1;
}
//创建套接字
sockRaw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED);
if(sockRaw==INVALID_SOCKET)
{
printf("WSASocket() failed:%d\n",WSAGetLastError());
return -1;
}
//对锁定套接字设置超时
bread=setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout));
if(bread==SOCKET_ERROR)
{
printf("setsockopt(SO_RCVTIMEO) failed:%d\n",WSAGetLastError());
return -1;
}
timeout=1000;
bread=setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(timeout));
if(bread==SOCKET_ERROR)
{
printf("setsockopt(SO_SNDTIMEO) failed:%d\n",WSAGetLastError());
return -1;
}
//解析目标地址,将主机名转化为IP地址
memset(&dest,0,sizeof(dest));
dest.sin_family=AF_INET;
if((dest.sin_addr.S_un.S_addr=inet_addr(lpdest))==INADDR_NONE)
{
if((hp=gethostbyname(lpdest))!=NULL)
{
memcpy(&(dest.sin_addr.S_un.S_addr),hp->h_addr_list[0],hp->h_length);
dest.sin_family=hp->h_addrtype;
printf("dest.sin_addr=%s\n",inet_ntoa(dest.sin_addr));
}
else
{
printf("gethostbyname() failed:%d\n",WSAGetLastError());
return -1;
}
}
//Create the ICMP pakcet
icmp_data=(char *)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET);
recvbuf =(char *)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET);
if(!icmp_data)
{
printf("HeapAlloc() failed: %d\n",GetLastError());
return -1;
}
memset(icmp_data,0,MAX_PACKET);
FillICMPData(icmp_data,DEICMP_PACKSIZE);
printf("Hop IP Address Time elapsed\n");
//开始发送/接收ICMP报文
for(i=1;i<=255;i++)
{
int bwrote;
//设置IP包的生存期
ret=setsockopt(sockRaw,IPPROTO_IP,IP_TTL,(char *)&i,sizeof(int));
if(ret==SOCKET_ERROR)
{
printf("setsockopt(IP_TTL) failed:%d\n",WSAGetLastError());
}
((IcmpHeader *)icmp_data)->i_cksum =0;
((IcmpHeader *)icmp_data)->i_seq=seq_no++; //Sequence number of ICMP packetsICMP数据包的序列号
((IcmpHeader *)icmp_data)->i_cksum=checksum((USHORT *)icmp_data,DEICMP_PACKSIZE);
//发送ICMP包请求查询
cStartTickCount=GetTickCount();
bwrote=sendto(sockRaw,icmp_data,DEICMP_PACKSIZE,0,(struct sockaddr *)&dest,sizeof(dest));
if(bwrote==SOCKET_ERROR)
{
if(WSAGetLastError()==WSAETIMEDOUT)
{
printf("timed out\n");
continue;
}
printf("sendto() failed:%d\n",WSAGetLastError());
return -1;
}
if(bwrote<DEICMP_PACKSIZE)
{
printf("Wrote %d bytes\n",bwrote);
}
//接收ICMP回复包
bread=recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr *)&from,&fromlen);
if(bread==SOCKET_ERROR)
{
if(WSAGetLastError()==WSAETIMEDOUT)
{
DecodeIPHeader(NULL,0,NULL);
continue;
}
printf("recvfrom() failed:%d\n",WSAGetLastError());
return -1;
}
if(DecodeIPHeader(recvbuf,bread,&from))
break;
Sleep(1000);
}
if(sockRaw!=INVALID_SOCKET)
closesocket(sockRaw);
HeapFree(GetProcessHeap(),0,recvbuf);
HeapFree(GetProcessHeap(),0,icmp_data);
WSACleanup();
return 0;
}