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)用来设置警告消息的等级
出现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
就不会出现上面的错误了。
原文地址:http://blog.csdn.net/liuruiqun/article/details/45668061