基于icmp的tracert路由追踪程序
https://blog.csdn.net/u013271921/article/details/45488173
#include<winsock2.h> //#include<iphlpapi.h> #include <Ws2tcpip.h> #include<iostream> #include<conio.h> //#include<bits/stdc++.h> using namespace std; const BYTE ICMP_ECHO_REQUEST = 8;//请求回显 const BYTE ICMP_ECHO_REPLY = 0;//回显应答 const BYTE ICMP_TIMEOUT = 11;//传输超时 const int DEF_ICMP_DAtA_SIZE = 32;//ICMP报文默认数据字段长度 const int MAX_ICMP_PACKET_SIZE = 1024;//ICMP报文最大长度(包含报头) const DWORD DEF_ICMP_TIMEOUT = 3000;//回显应答超时时间,单位ms const int DEF_MAX_HOP = 30;//最大跳站数 typedef struct { BYTE hdr_len :4;//4位头部长度 BYTE version :4;//长度版本号 BYTE tos;//8位服务类型 USHORT total_len;//16位总长度 USHORT identifier;//16位标识符 USHORT frag_and_flags;//3位标志+13位片偏移 BYTE ttl;//8位生存时间 BYTE protocol;//8位上层协议号 USHORT checksum;//16位校验和 ULONG sourceIP;//32位源IP地址 ULONG destIP;//32位目的IP地址 } IP_HEADER; typedef struct { BYTE type;//8位类型字段 BYTE code;//8位代码字段 USHORT cksum;//16为校验和 USHORT id;//16位标识符 USHORT seq;//16位序列号 } ICMP_HEADER; typedef struct { // 序列号(输入参数) USHORT usSeqNo; // 往返时间(输入、输出) DWORD dwRoundTripTime; // 返回报文的IP地址(输出参数) in_addr dwIpAddr; } DECODE_RESULT; //计算网际校验和函数 USHORT checksum(USHORT* pBuf, int iSize) { ULONG cksum = 0; while(iSize > 1) { cksum += *pBuf++; iSize -= sizeof(USHORT); } // 如果 iSize 为正,即为奇数个字节 if(iSize) { // 则在末尾补上一个字节,使之有偶数个字节 cksum += *(UCHAR*)pBuf; } cksum = (cksum>>16) +(cksum & 0xffff); cksum += (cksum >> 16); return (USHORT)(~cksum); } bool DecodeIcmpResponse(char *pBuf, int iPacketSize, DECODE_RESULT& DecodeResult) { // 计算IP头部长度 int iIpHdrLen = ((IP_HEADER*)pBuf)->hdr_len * 4; // 根据ICMP报文类型提取ID字段和序列号字段 ICMP_HEADER *pIcmpHdr = (ICMP_HEADER *)(pBuf + iIpHdrLen); USHORT usID; USHORT usSquNo; // ICMP回显应答报文 if(pIcmpHdr->type == ICMP_ECHO_REPLY) { // 报文ID usID = pIcmpHdr->id; // 序列号 usSquNo = pIcmpHdr->seq; } // ICMP超时差错报文 else if(pIcmpHdr->type == ICMP_TIMEOUT) { // 载荷中的IP头 char *pInnerIpHdr = pBuf + iIpHdrLen + sizeof(ICMP_HEADER); // 载荷中的IP头长 int iInnerIpHdrLen = ((IP_HEADER*)pInnerIpHdr)->hdr_len * 4; // 载荷中的ICMP头 ICMP_HEADER *pInnerIcmpHdr = (ICMP_HEADER*)(pInnerIpHdr + iInnerIpHdrLen); // 报文ID usID = pInnerIcmpHdr->id; // 序列号 usSquNo = pInnerIcmpHdr->seq; } else { return false; } // printf("usID: %d == currentID: %d\n", usID, (USHORT)GetCurrentProcessId()); // printf("usSquNo: %d == DecodeResult: %d\n", usSquNo, DecodeResult.usSeqNo); // 检查ID和序列号以确定收到期待数据报 if(usID != (USHORT)GetCurrentProcessId() || usSquNo != DecodeResult.usSeqNo) { return false; } // 记录IP地址并计算往返时间 DecodeResult.dwIpAddr.s_addr = ((IP_HEADER*)pBuf)->sourceIP; DecodeResult.dwRoundTripTime = GetTickCount() - DecodeResult.dwRoundTripTime; // 打印往返时间信息 if(DecodeResult.dwRoundTripTime) { cout << " " << DecodeResult.dwRoundTripTime << "ms" << flush; } else { cout << " " << "<1" << "ms" << flush; } return true; } WSADATA wsa; char IcmpSendBuf[sizeof(ICMP_HEADER) + DEF_ICMP_DAtA_SIZE];//发送缓冲区 char IcmpRecvBuf[sizeof(ICMP_HEADER) + DEF_ICMP_DAtA_SIZE];//接收缓冲区 char argv[4][100]; int main() { WSAStartup(MAKEWORD(2, 2), &wsa); //将命令行参数转换成IP地址 scanf("%s", argv[1]); ULONG ulDestIP = inet_addr(argv[1]); if(ulDestIP == INADDR_NONE) { //转换不成功时按域名解析 hostent *pHostent = gethostbyname(argv[1]); if(pHostent) ulDestIP = (*(in_addr*) pHostent->h_addr).s_addr; else { WSACleanup(); return -1; } } //填充目的端socket地址。 sockaddr_in destSockAddr; ZeroMemory(&destSockAddr, sizeof(sockaddr_in)); destSockAddr.sin_family = AF_INET; destSockAddr.sin_addr.s_addr = ulDestIP; SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED); int iTimeout = 3000; //设置超时机制 setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*) &iTimeout, sizeof(iTimeout)); setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*) &iTimeout, sizeof(iTimeout)); ICMP_HEADER *pIcmpHeader = (ICMP_HEADER*) IcmpSendBuf; pIcmpHeader->type = ICMP_ECHO_REQUEST;//类型为请求回显 pIcmpHeader->code = 0;//代码字段为0 pIcmpHeader->id=(USHORT)GetCurrentProcessId();//ID字段为当前进程号 memset(IcmpSendBuf + sizeof(ICMP_HEADER), 'E', DEF_ICMP_DAtA_SIZE);//数据字段 USHORT usSeqNo = 0;//ICMP报文序列号 int iTTL = 1;//TTL初始值为1 bool bReachDestHost = false;//循环退出标志 int iMaxHop = DEF_MAX_HOP;//循环的最大次数 DECODE_RESULT DecodeResult;//传递给报文解码函数的结构化参数 while(!bReachDestHost && iMaxHop--) { //设置IP报头的TTL字段 setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char*)&iTTL, sizeof(iTTL)); cout << iTTL << flush;//输出当前序号 //填充ICMP报文中每次发送时需要变化的字段 ((ICMP_HEADER*)IcmpSendBuf)->cksum = 0;//校验和先置为0 ((ICMP_HEADER*)IcmpSendBuf)->seq = htons(usSeqNo++);//填充序列号 //计算校验和 ((ICMP_HEADER*)IcmpSendBuf)->cksum = checksum((USHORT*)IcmpSendBuf, sizeof(ICMP_HEADER) + DEF_ICMP_DAtA_SIZE); DecodeResult.usSeqNo = ((ICMP_HEADER*)IcmpSendBuf)->seq;//当前序号 DecodeResult.dwRoundTripTime = GetTickCount();//当前时间 //发送ICMP回显请求消息 sendto(sockRaw, IcmpSendBuf, sizeof(IcmpSendBuf), 0, (sockaddr*)&destSockAddr, sizeof(destSockAddr)); // 接收ICMP报文 // 对端Socket地址 sockaddr_in from; // 地址结构大小 int iFromLen = sizeof(from); // 接收数据长度 int iReadDataLen; // 循环接收直到收到所需数据或超时 while(1) { iReadDataLen = recvfrom(sockRaw, IcmpRecvBuf, MAX_ICMP_PACKET_SIZE, 0, (sockaddr*)&from, &iFromLen); // 有数据到达 if(iReadDataLen != SOCKET_ERROR) { // 对数据包进行解析 if(DecodeIcmpResponse(IcmpRecvBuf, iReadDataLen, DecodeResult)) { // 到达目的地,退出循环 if(DecodeResult.dwIpAddr.s_addr == destSockAddr.sin_addr.s_addr) { bReachDestHost = true; // printf("reach\n"); } // 打印IP地址 cout << '\t' << inet_ntoa(DecodeResult.dwIpAddr) << endl; break; } } // 接收超时,打印*号 else if(WSAGetLastError() == WSAETIMEDOUT) { cout << " " << '*' << '\t' << "Request timed out." << endl; break; } } iTTL++; } cout << "reach: " << bReachDestHost << endl; return 0; } //cqupt 202.202.32.35 //csdn 39.96.126.153 //cnblogs 101.37.113.127