码迷,mamicode.com
首页 > 其他好文 > 详细

Trace命令的实现

时间:2015-05-12 13:36:58      阅读:241      评论:0      收藏:0      [点我收藏+]

标签:网络   tracer   

Trace命令的实现

【trace原理】

traceroute是用来跟踪路由的命令,可以查看数据包从一端到另一端的路线。

当源执行traceroute的时候,第一个数据包的TTL设置为1,那么下一跳的路由器收到数据包之后会丢弃数据包,并且会向源发送一条错误信息,源通过阅读错误信息从而得知发送错误信息的路由器就是第一跳。源第二次发送数据包的时候把TTL的值设置为2,第二跳的路由器发送错误信息过来,源路由器就可以知道第二跳是谁。以此类推,直到发现目标为止。

【源码分析】

// trace.cpp : Defines the entry point forthe console application.

//

 

#include "stdafx.h"

#pragma pack(4)

/*加载头文件*/

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>

#include <ws2tcpip.h>

#include <stdio.h>

#include <stdlib.h>

 

/*定义常量*/

/*ICMP报文类型,回显请求*/ 

#define ICMP_ECHO 8

/*ICMP报文类型,回显应答*/

#define ICMP_ECHOREPLY 0

/*最大的ICMP数据报大小*/

#define MAX_PACKET 1024

/*最小的ICMP数据报大小*/

#define ICMP_MIN 8

/*默认数据报大小*/

#define DEF_PACKET_SIZE 32

 

#define STATUS_FAILED 0xFFFF

#define xmalloc(s)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))

#define xfree(p)   HeapFree (GetProcessHeap(),0,(p))

 

/*IP报头字段数据结构*/

typedef struct iphdr {

         unsignedint h_len:4;            /*IP报头长度(4位)*/

         unsignedint version:4;          /*IP的版本号(4位)*/

         unsignedchar tos;               /*服务的类型(8位)*/

         unsignedshort total_len;        /*数据报总长度(16位)*/

         unsignedshort ident;            /*惟一的标识符(16位)*/

         unsignedshort frag_and_flags;   /*分段标志(3位)+分片偏移(13位)*/

         unsignedchar  ttl;                             /*生存期(8位)*/

         unsignedchar proto;             /*协议类型(TCP、UDP等)(8位)*/

         unsignedshort checksum;                /*校验和(16位)*/

         unsignedint sourceIP;                        /*源IP地址(32位)*/

         unsignedint destIP;                            /*目的IP地址(32位)*/

 

}IpHeader;

 

/*ICMP报头字段数据结构*/

typedef struct _icmphdr

{

   BYTE   i_type;                 /*ICMP报文类型(8位)*/

   BYTE   i_code;                 /*该类型中的代码号(8位)*/

   USHORT i_cksum;                /*校验和(16位)*/

   USHORT i_id;                   /*惟一的标识符(16位)*/

   USHORT i_seq;                  /*序列号(16位)*/

    ULONG  timestamp;              /*时间戳(32位)*/ 

} IcmpHeader;

 

 

/*子函数声明*/

void fill_icmp_data(char *, int);

USHORT checksum(USHORT *, int);

void decode_resp(char *,int ,structsockaddr_in *);

 

void Usage(char *progname){

 fprintf(stderr,"Usage:\n");

 fprintf(stderr,"%s <host> [data_size]\n",progname);

 fprintf(stderr,"datasize can be up to 1Kb\n");

 ExitProcess(STATUS_FAILED);

 

}

 

int main(int argc, char **argv){

 

 WSADATA wsaData;

 SOCKET sockRaw;

 struct sockaddr_in dest,from;

 struct sockaddr_in *dest_tmp,*from_tmp;

 struct hostent * hp;

  intbread,datasize;

  intfromlen = sizeof(from);

  inttimeout = 1000;

  intTTL=0;

 char *dest_ip;

 char *icmp_data;

 char *recvbuf;

 unsigned int addr=0;

 USHORT seq_no = 0;

 char temp1[100];

 char temp2[100];

  if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){

         fprintf(stderr,"套接字初始化失败:%d\n",GetLastError());

         ExitProcess(STATUS_FAILED);

  }

 

  if(argc <2 ) {

         Usage(argv[0]);

  }

 sockRaw = WSASocket (AF_INET,

                                                  SOCK_RAW,

                                                  IPPROTO_ICMP,

                                                NULL, 0,0);

 

  if(sockRaw == INVALID_SOCKET) {

         fprintf(stderr,"申请套接字失败:%d\n",WSAGetLastError());

         ExitProcess(STATUS_FAILED);

  }

 TTL=1;//初始化TTL

 bread =setsockopt(sockRaw,IPPROTO_IP,IP_TTL,(LPSTR)&TTL,sizeof(int));//设置原始套接字

 if(bread == SOCKET_ERROR){

           fprintf(stderr,"TTL设置:%d,错误:%d",bread,WSAGetLastError());

 ExitProcess(STATUS_FAILED);

  }

 bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,

                                            sizeof(timeout));

 if(bread == SOCKET_ERROR) {

       fprintf(stderr,"接收超时设置失败:%d\n",WSAGetLastError());

         ExitProcess(STATUS_FAILED);

  }

 timeout = 1000;

 bread = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,

                                            sizeof(timeout));

 if(bread == SOCKET_ERROR) {

       fprintf(stderr,"发送超时设置失败:%d\n",WSAGetLastError());

         ExitProcess(STATUS_FAILED);

  }

 memset(&dest,0,sizeof(dest));//复制目标地址

 

  hp= gethostbyname(argv[1]);

 

  if(!hp){

         addr= inet_addr(argv[1]);

  }

  if((!hp)  && (addr == INADDR_NONE)) {

         fprintf(stderr,"无法解析%s\n",argv[1]);

         ExitProcess(STATUS_FAILED);

  }

 

  if(hp != NULL)

          memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);

 else

       dest.sin_addr.s_addr = addr;

 

  if(hp)

           dest.sin_family = hp->h_addrtype;

 else

           dest.sin_family = AF_INET;

 

 dest_ip = inet_ntoa(dest.sin_addr);

 

  if(argc >2) {

         datasize= atoi(argv[2]);

         if(datasize == 0)

           datasize = DEF_PACKET_SIZE;

 

  }

 else

           datasize = DEF_PACKET_SIZE;

        

 datasize += sizeof(IcmpHeader); 

 

 icmp_data = xmalloc(MAX_PACKET);

 recvbuf = xmalloc(MAX_PACKET);

 

  if(!icmp_data) {

         fprintf(stderr,"HeapAllocfailed %d\n",GetLastError());

         ExitProcess(STATUS_FAILED);

  }

 

 

 memset(icmp_data,0,MAX_PACKET);

 fill_icmp_data(icmp_data,datasize);

  /*发送和接收数据*/

while(1) {

         intbwrote;

         bread= setsockopt(sockRaw,IPPROTO_IP,IP_TTL,(LPSTR)&TTL,sizeof(int));

        

         ((IcmpHeader*)icmp_data)->i_cksum= 0;

         ((IcmpHeader*)icmp_data)->timestamp= GetTickCount();

 

         ((IcmpHeader*)icmp_data)->i_seq= seq_no++;

         ((IcmpHeader*)icmp_data)->i_cksum= checksum((USHORT*)icmp_data,

                                                                                                       datasize);

 

         bwrote= sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,

                                               sizeof(dest));

         if(bwrote == SOCKET_ERROR){

           if (WSAGetLastError() == WSAETIMEDOUT) {

                printf("超时\n");

                   continue;

           }

           fprintf(stderr,"发送失败:%d\n",WSAGetLastError());

           ExitProcess(STATUS_FAILED);

         }

         if(bwrote < datasize ) {

           fprintf(stdout,"Wrote %dbytes\n",bwrote);

         }

         bread= recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from,

                                      &fromlen);

         if(bread == SOCKET_ERROR){

           if (WSAGetLastError() == WSAETIMEDOUT) {

                printf("超时\n");

                   continue;

           }

           fprintf(stderr,"接收失败:%d\n",WSAGetLastError());

           ExitProcess(STATUS_FAILED);

         }

/*TTL值加1*/

         fprintf(stderr,"%d:  ",TTL++);

         decode_resp(recvbuf,bread,&from);

        

 

         dest_tmp=&dest;

         from_tmp=&from;

        

         //fprintf(stderr,"dest.sin_addr.s_un:%s\n",inet_ntoa(dest_tmp->sin_addr));

         //fprintf(stderr,"from.sin_addr.s_un:%s\n",inet_ntoa(from_tmp->sin_addr));

        

         strcpy(temp1, inet_ntoa(dest_tmp->sin_addr)) ;

         strcpy(temp2, inet_ntoa(from_tmp->sin_addr)) ;

         //fprintf(stderr,"ddd:%d\n",(strcmp(inet_ntoa(from_tmp->sin_addr),inet_ntoa(dest_tmp->sin_addr))));

   /*下一地址和目的地址*/

         if(strcmp(temp1,temp2)==0)

         {fprintf(stderr,"Trace命令完成!\n");

                   return1;}

         Sleep(1000);

 

  }

 return 0;

 

}

/*解读ICMP报头函数*/

void decode_res(char *buf, int bytes,structsockaddr_in *from) {

 

         IpHeader*iphdr;

         IcmpHeader*icmphdr;

         unsignedshort iphdrlen;

 

         iphdr= (IpHeader *)buf;

//      printf("RemoteTTL:%d\n",(int)iphdr->ttl);

         iphdrlen= iphdr->h_len * 4 ; // number of 32-bit words *4 = bytes

 

/*如果读取的数据太小*/

         if(bytes  < iphdrlen + ICMP_MIN) {

                   printf("Toofew bytes from %s\n",inet_ntoa(from->sin_addr));

         }

 

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

/*

/*如果收到的不是回显应答报文则报错*/

         if(icmphdr->i_type != ICMP_ECHOREPLY) {

                   fprintf(stderr,"non-echotype %d recvd\n",icmphdr->i_type);

                   //return;

         }

   /*核实收到的ID号和发送的是否一致*/

         if(icmphdr->i_id != (USHORT)GetCurrentProcessId()) {

                   fprintf(stderr,"someoneelse's packet!\n");

                   //return;

         }

*/

         printf("%dbytes     %s     ",bytes,inet_ntoa(from->sin_addr));

//      printf("icmp_seq = %d. ",icmphdr->i_seq);

         printf("time: %d ms ",GetTickCount()-icmphdr->timestamp);

         printf("\n");

                  

}

 

/*求校验和函数*/

USHORT checksum(USHORT *buffer, int size) {

 

 unsigned long cksum=0;

 

 while(size >1) {

         cksum+=*buffer++;

         size-=sizeof(USHORT);

  }

 

 if(size ) {

         cksum+= *(UCHAR*)buffer;

  }

/*对每个16bit进行二进制反码求和*/

 cksum = (cksum >> 16) + (cksum & 0xffff);

 cksum += (cksum >>16);

 return (USHORT)(~cksum);

}

/*填充ICMP数据报字段函数*/

void fill_icmp_data(char * icmp_data, intdatasize){

 

 IcmpHeader *icmp_hdr;

 char *datapart;

 

 icmp_hdr = (IcmpHeader*)icmp_data;

/*ICMP报文类型设置为回显请求*/

 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);

  /*以数字0填充剩余空间*/

 memset(datapart,'E', datasize - sizeof(IcmpHeader));

 

}


【注意事项】

【字节对齐】

#pragma pack(push) //保存对齐状态
#pragma pack(4)//设定为4字节对齐

#pragma pack(pop)//恢复对齐状态
 
struct是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如array、struct、union等)的数据
单元。对于结构体,编译器会自动进行成员变量的对齐,以提高运算效率。
 
缺省情况下,编译器为结构体的每个成员按其自然对界(natural alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和
整个结构的地址相同。自然对界是指按结构体的成员中 sizeof 最大的成员对齐。
 
#pragma pack规定的对齐长度,实际使用的规则是:
1)结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和结构体的自然对齐长度中比较小的那个进行。
 就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。
2) 结构体的对齐,按照结构体中size最大的数据成员和#pragma pack指定值之间,较小的那个进行。
 
#pragma pack(4)

  class TestC

  {

  public:

    char a;//第一个成员,放在[0]偏移的位置,

    short b;//第二个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置。

    char c;//第三个,自身长为1,放在[4]的位置。

  };


整个类的大小是5字节,按照min(sizeof(short),4)字节对齐,也就是2字节对齐,结果是6。所以sizeof(TestC)是6。

windows下面默认的是#pragma pack(8)。所以如果要紧凑内存存放的话 需要用 #pragma pack(1)来限制了。

因为编译器在编译时会对程序进行优化,以便加快访问速度,所以一般都会按照2的倍数进行字节对齐。用这个宏就是为了防止编译器对结构的定义进行对齐。

#pragma(push,n)用来设置警告消息的等级

【error LNK2001】

出现link2001的错误的解决办法:

在project-> setting ->link ->object/librarymodules中添加:

ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.libadvapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.libodbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.libadvapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.libodbccp32.lib

就不会出现上面的错误了。

Trace命令的实现

标签:网络   tracer   

原文地址:http://blog.csdn.net/liuruiqun/article/details/45668061

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!