现在的位置: 首页 > 综合 > 正文

TraceRoute小贴

2013年10月10日 ⁄ 综合 ⁄ 共 9875字 ⁄ 字号 评论关闭

TraceRoute程序:
原理:向目的地发送一个UDP数据包,并重复递增IP的"存活时间"(TTL)值.最开始的时候。TTL等于1,也就是说,一旦它抵达路途中的第一个路由器,TTL便会超时(变成0),这样就会造成路由器生成一个ICMP"超时"数据包。随后,最初的TTL值递增1,以便UDP包能继续传到下一个路由器,而生成的ICMP超时包会自第一个路由器返回。只需将返回的每一条ICMP消息都收集下来,便能为中途经过的路由器IP地址勾勒出一个清晰的轮廓,直到最终抵达目标主机。一旦TTL的值递增得足够大,可以实际抵达目标位置,通常便会返回一条ICMP"端口访问不到"消息,因为再接受端主机上,并没有进程在等待这条消息

有两个办法实现TraceRoute程序:
  1: 可以使用UDP包发送数据报,连续递增更改TTL的值。TTL每一次“超时”,都返回一条ICMP消息,这种方法要求使用安装了UDP协议的一个套接字来发送数据,同时用安装了ICMP协议的另一个套接字来接收数据(读取返回的消息),UDP套接字是普通的套接字,而ICMP套接字的类型比较特殊.是SOCK_RAW(原始套接字),并设定了IPPROTO_ICMP协议。UDP套接字的TTL值需要通过IP_TTL套接字选项加以控制.此外,也可以创建一个UDP套接字,并使用IP_HDRINCL选项,在IP头内对TTL进行人工设置
  2:将ICMP数据报简单发给目的地,同时连续递增更改TTL的值,在"超时"的时候,返回一条ICMP错误消息(它只要使用一个套接字(安装ICMP协议))
2的例子:
步骤:
 1 将RraceRoute的功能封装在一个类CTranceRoute中
 
#include <winsock2.h>
#include <ws2tcpip.h>

//定义ICMP消息类型
#define ICMP_ECHOREPLY   0 //目的地址返回的ICMP
#define ICMP_DESTUNREACH 3 //不能够到达目的地
#define ICMP_SRCQUENCH   4
#define ICMP_REDIRECT    5
#define ICMP_ECHO        8
#define ICMP_TIMEOUT     11 //沿着这条链路返回的路由
#define ICMP_PARMERR     12

//定义最大节点数
#define MAX_HOPS         30

#define ICMP_MIN         8 //每个ICMP数据包头部最少8字节
//IP 头结构
typedef struct iphdr
{
 unsigned int  h_len:4;      //头部长度
 unsigned int  version:4;    //IP版本
 unsigned char tos;          //服务类型
 unsigned short total_len;   //数据报总长度
 unsigned short ident;       //唯一的标志
 unsigned short frag_and_flags;//标志
 unsigned char  ttl;        //生命周期
 unsigned char  proto;      //协议类型
 unsigned short checksum;   //IP校验
 unsigned int   sourceIP;   //源地址
 unsigned int   destIP;     //目的地址
}IpHeader;

//ICMP头结构
typedef struct _ihdr
{
 BYTE   i_type;    //ICMP消息类型
 BYTE   i_code;    //子码
 USHORT i_cksum;   //校验和
 USHORT i_id;      //唯一的标识
 USHORT i_seq;     //序号
 ULONG  timestamp; //时间戳
}IcmpHeader;

#define DEF_PACKET_SIZE  32   //缺省数据报长度
#define MAX_PACKET       1024 //数据报最大长度

class CTraceRouteDlg ;

class CTranceRoute 
{
public:
 CTranceRoute(CTraceRouteDlg *dlg);
 virtual ~CTranceRoute();
public:
 //解析返回的数据包
 int decode_resp(char *buf,int bytes,SOCKADDR_IN*from,int ttl);
 //清除数据
 void Cleanup();
 //数据校验
 USHORT checksum(USHORT*buffer,int size);
 //设置TTL
 int set_ttl(SOCKET s,int nTimeToLive);
 //填充ICMP数据包
 void fill_icmp_data(char*icmp_data,int datasize);
 //连接到主机
 void ConnectToHost(char* strHost);
 CTraceRouteDlg *m_dlg;
 SOCKET        m_socketRaw;
 SOCKADDR_IN   m_addrDest;
 SOCKADDR_IN   m_addrFrom;
 int           m_nDatasize;
 int           m_nMaxhops;//最大节点数
 int           m_nTTL;
 char*         m_IcmpData;
 char*         m_RcvBuffer;
 int           m_nSeqno;
 int           m_nTimeout;
 BOOL          m_bDone;
};

2 初始化:
 
CTranceRoute::CTranceRoute(CTraceRouteDlg *dlg)
{
 m_dlg=dlg;
 m_nTTL=1;
 m_nMaxhops=MAX_HOPS;
 m_socketRaw=INVALID_SOCKET;
 m_RcvBuffer=NULL;
 m_IcmpData=NULL;
 m_nTimeout=1000;
 m_bDone=FALSE;
 m_nSeqno=0;

 //初始化socket
 WSADATA wsaData;
 if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
 {
  AfxMessageBox("socket cannot load dll");
 }

}
 3:功能函数:

//该函数负责连接到主机,它首先创建一个Socket,这个Socket的类型必须设成SOCK_RAW,即原始套接字,必须采用IPPORTO_ICMP
//协议,接下来,设置Socket的接收和发送的超时参数,如果用户输入的地址是主机名,则还需要将其解析成一个IP地址
//接着,为ICMP数据报分配空间,最后是创建和发送ICMP数据包,并解析返回的ICMP包
void CTranceRoute::ConnectToHost(char*strHost)
{
 //----------------------------------------------------------------------------------------------------------------------
 //创建socket 这个Socket的类型必须设成SOCK_RAW,即原始套接字,必须采用IPPORTO_ICMP协议
 m_socketRaw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED);
 if(m_socketRaw==INVALID_SOCKET)
 {
  AfxMessageBox("socket 创建失败");
  return ;
 }
 //-------------------------------------------------------------------------------------------------------------------------
 //设置发送和接收超时参数
 int ret=setsockopt(m_socketRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&m_nTimeout,sizeof(m_nTimeout));
 if(ret==SOCKET_ERROR)
 {
  AfxMessageBox("设置接收超时参数失败");
  return ;
 }

 ret=setsockopt(m_socketRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&m_nTimeout,sizeof(m_nTimeout));
 if(ret==SOCKET_ERROR)
 {
  AfxMessageBox("设置接收超时参数失败");
  return ;
 }
   //-------------------------------------------------------------------------------------------------------------------------
 //解析地址
 m_addrDest.sin_family=AF_INET;
 if((m_addrDest.sin_addr.S_un.S_addr=inet_addr(strHost))==INADDR_NONE)
 {
  HOSTENT* hp;
  hp=gethostbyname(strHost);
  if(hp)
   memcpy(&(m_addrDest.sin_addr.S_un.S_addr ),hp->h_addr_list[0],hp->h_length );
  else
  {
   AfxMessageBox("输入的主机不存在");
   return ;
  }
 }
 //-------------------------------------------------------------------------------------------------------------------------
 //设置路由选项
 int bOpt=TRUE;
 if(setsockopt(m_socketRaw,SOL_SOCKET,SO_DONTROUTE,(char *)&bOpt,sizeof(BOOL))==SOCKET_ERROR)
 {
  AfxMessageBox("设置socket参数失败");
  return ;
 }
    //-------------------------------------------------------------------------------------------------------------------------
 //为ICMP数据报分配空间

 m_nDatasize=DEF_PACKET_SIZE;
 m_nDatasize+=sizeof(IcmpHeader);

 //为ICMP数据包分配发送和接收缓冲区
 m_IcmpData=(char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET);
 m_RcvBuffer=(char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET);

 if((!m_IcmpData)|(!m_RcvBuffer))
 {
  AfxMessageBox("堆分配数据失败");
  return ;
 }
 //-----------------------------------------------------------------------------------------------------------------------------

 //-------------------------------------------------
 //创建ICMP数据包
 memset(m_IcmpData,0,MAX_PACKET);
 CString temp;
 temp.Format("Tracing route to %s over a maximum of %d hops: /r/n",strHost,m_nMaxhops);
 m_dlg->m_result+=temp;
 m_dlg->SetDlgItemText(IDC_RESULT,m_dlg->m_result );
 //------------------------------------------------
 fill_icmp_data(m_IcmpData,m_nDatasize);//填充ICMP数据
    //------------------------------------------------
 for(m_nTTL=1;((m_nTTL<m_nMaxhops)&&(!m_bDone));m_nTTL++)
 {
  int bwrote;
  //设置socket生命周期
  set_ttl(m_socketRaw,m_nTTL);

  //往ICMP头部添加信息
  ((IcmpHeader*)m_IcmpData)->i_cksum=0;
  ((IcmpHeader*)m_IcmpData)->timestamp=GetTickCount();
  ((IcmpHeader*)m_IcmpData)->i_seq=m_nSeqno++;
  ((IcmpHeader*)m_IcmpData)->i_cksum=checksum((USHORT*)m_IcmpData,m_nDatasize);

  //发送ICMP包到目的端
  bwrote=sendto(m_socketRaw,m_IcmpData,m_nDatasize,0,(SOCKADDR*)&m_addrDest,sizeof(m_addrDest));
  if(bwrote==SOCKET_ERROR)
  {
   if(WSAGetLastError()==WSAETIMEDOUT)
   {
    temp.Format("%2d Send request timed out ./t/n",m_nTTL);
    m_dlg->m_result+=temp;
    m_dlg->SetDlgItemText(IDC_RESULT,m_dlg->m_result );
    continue ;
   }
   temp.Format("发送数据函数出错!/r/n");
   m_dlg->m_result+=temp;
   m_dlg->SetDlgItemText(IDC_RESULT,m_dlg->m_result);
   return ;
  }

  //从目的端读取数据包
  int fromlen;
  fromlen=sizeof(SOCKADDR);
  ret=recvfrom(m_socketRaw,m_RcvBuffer,MAX_PACKET,0,(struct sockaddr*)&m_addrFrom,&fromlen);
  if(ret==SOCKET_ERROR)
  {
   if(WSAGetLastError()==WSAETIMEDOUT)
   {
    temp.Format("%2d Recvive Request timed out ./r/n",m_nTTL);
    m_dlg->m_result+=temp;
    m_dlg->SetDlgItemText(IDC_RESULT,m_dlg->m_result );
    continue;
   }
   temp.Format("recvfrom 函数出错!/n");
   m_dlg->m_result+=temp;
   m_dlg->SetDlgItemText(IDC_RESULT,m_dlg->m_result );
   return ;
  }

  //解析返回的ICMP数据报信息
  m_bDone=decode_resp(m_RcvBuffer,ret,&m_addrFrom,m_nTTL);
  Sleep(1000);
 }
}

其它代码:

void CTranceRoute::Cleanup()
{
    HeapFree(GetProcessHeap(), 0, m_RcvBuffer);
    HeapFree(GetProcessHeap(), 0, m_IcmpData);
 if (m_socketRaw != NULL)
  closesocket(m_socketRaw);
// WSACleanup();
}

//负责对返回的ICMP数据进行解析
int CTranceRoute::decode_resp(char*buf,int bytes,SOCKADDR_IN*from,int ttl)
{
 IpHeader      *iphdr=NULL;
 IcmpHeader    *icmphdr=NULL;
 unsigned short iphdrlen;
 struct hostent*IpHostent=NULL;
 struct in_addr inaddr=from->sin_addr;

 iphdr=(IpHeader*)buf;
 //Number of 32-bit words*4=bytes
 iphdrlen=iphdr->h_len*4;

 CString temp;
 if(bytes<iphdrlen+ICMP_MIN)
 {
  temp.Format("Too few bytes from %s /r/n",inet_ntoa(from->sin_addr));
  m_dlg->m_result+=temp;
  m_dlg->SetDlgItemText(IDC_RESULT,m_dlg->m_result);

 }
 icmphdr=(IcmpHeader*)(buf+iphdrlen);

 switch(icmphdr->i_type )
 {
 case ICMP_ECHOREPLY: //目的地址返回的ICMP
  IpHostent=gethostbyaddr((const char*)&from->sin_addr,AF_INET,sizeof(struct in_addr));
  if(IpHostent!=NULL)
  {
   temp.Format("%2d  %s (%s) /r/n", ttl, IpHostent->h_name,inet_ntoa(inaddr));
   m_dlg->m_result+=temp;
   m_dlg->SetDlgItemText(IDC_RESULT,m_dlg->m_result);
  }
  return 1;
  break;
 case ICMP_TIMEOUT: //沿着这条链路返回的路由
  temp.Format("%2d %s /r/n",ttl,inet_ntoa(inaddr));
  m_dlg->m_result+=temp;
  m_dlg->SetDlgItemText(IDC_RESULT,m_dlg->m_result );
  return 0;
  break;
 case ICMP_DESTUNREACH://不能够到达目的地
   temp.Format("%2d  %s  reports: Host is unreachable /r/n", ttl,inet_ntoa(inaddr));
   m_dlg->m_result+=temp;
   m_dlg->SetDlgItemText(IDC_RESULT,m_dlg->m_result);
            return 1;
            break;
     default:
   temp.Format("non-echo type %d recvd (找不到匹配)/n", icmphdr->i_type);
   m_dlg->m_result+=temp;
   m_dlg->SetDlgItemText(IDC_RESULT,m_dlg->m_result);
            return 1;
            break;

 }
 
 return 0;
}

//负责设置ICMP包的声明周期
int CTranceRoute::set_ttl(SOCKET s,int nTimeToLive)
{

 int nRet;
 nRet=setsockopt(s,IPPROTO_IP,IP_TTL,(LPSTR)&nTimeToLive,sizeof(int));
 if(nRet==SOCKET_ERROR)
 {
  AfxMessageBox("设置socket选项失败!");
  return 0;
 }
 return 1;
}

//负责填充ICMP数据
void CTranceRoute::fill_icmp_data(char* icmp_data,int datasize)
{
 IcmpHeader *icmp_hdr;
 //char       *datapart;

 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=0;
 
 //datapart=icmp_data+sizeof(IcmpHeader);
 //memset(datapart,'E',datasize-sizeof(IcmpHeader));
}

USHORT CTranceRoute::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);

}
 4 按钮
void CTraceRouteDlg::OnTracert()
{
 CTranceRoute route(this);
 char host[100];
 GetDlgItemText(IDC_HOST,host,100);
  route.m_nMaxhops=GetDlgItemInt(IDC_ROUTE_COUNT);
 route.ConnectToHost(host);
 m_result+="Trace 结束!/r/n";
 SetDlgItemText(IDC_RESULT,m_result);
 route.Cleanup();
 
}

void CTraceRouteDlg::OnDeltaposSpin(NMHDR* pNMHDR, LRESULT* pResult)
{
 NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;

 int max=GetDlgItemInt(IDC_ROUTE_COUNT);
 if(max>=MAX_HOPS&&pNMUpDown->iDelta<0)
  return ;
 if(max<2&&pNMUpDown->iDelta>0)
  return ;

 max-=pNMUpDown->iDelta;
 SetDlgItemInt(IDC_ROUTE_COUNT,max);
 
 *pResult = 0;
}

 

 

 

 

 

 

 

 

抱歉!评论已关闭.