Trace命令的实现

来源:互联网 发布:万户网络怎样 编辑:程序博客网 时间:2024/06/05 19:24

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

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

0 0
原创粉丝点击