📄 tracert.cpp
字号:
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "ws2_32.lib")
// 定义ICMP信息类型
#define ICMP_ECHOREPLY 0 // 回应答复
#define ICMP_DESTUNREACH 3 // 目标机无法到达
#define ICMP_ECHO 8 // 回应请求
#define ICMP_TIMEOUT 11 // 超时消息
#define MAX_HOPS 30 // 默认的最大跳数
#define ICMP_PACKET_MIN 8 // 最小的ICMP包长度
#define ICMP_PACKET_SIZE 32
#define MAX_PACKET_SIZE 1024
// 定义IP首部格式
typedef struct _IPHeader
{
u_char VIHL; // 版本和首部长度
u_char ToS; // 服务类型
u_short TotalLen; // 总长度
u_short ID; // 标识号
u_short Frag_Flags; // 段偏移量
u_char TTL; // 生存时间
u_char Protocol; // 协议
u_short Checksum; // 首部校验和
struct in_addr SrcIP; // 源IP地址
struct in_addr DestIP; // 目的地址
} IPHeader;
// 定义ICMP首部格式
typedef struct _ICMPHeader
{
unsigned char Type; // 类型
unsigned char Code; // 代码
u_short Checksum; // 首部校验和
u_short ID; // 标识
u_short Seq; // 序列号
u_long Timestamp; // 时间戳
} ICMPHeader;
// 计算校验和
u_short checksum(u_short *buffer, int len)
{
register int nleft = len;
register u_short *w = buffer;
register u_short answer;
register int sum = 0;
// 使用32bit的累加器,进行16bit的反馈计算
while( nleft > 1 ) {
sum += *w++;
nleft -= 2;
}
// 补全奇数位
if( nleft == 1 ) {
u_short u = 0;
*(u_char *)(&u) = *(u_char *)w ;
sum += u;
}
// 将反馈的16bit从高位移至地位
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}
// 设置套接字TTL值
int SetTTL(SOCKET s, int nTTL)
{
int ret;
ret = setsockopt(s, IPPROTO_IP, IP_TTL, (LPSTR)&nTTL,
sizeof(int));
if (ret == SOCKET_ERROR)
{
printf("setsockopt(IP_TTL) error: %d\n",
WSAGetLastError());
return 0;
}
return 1;
}
//解析回应数据包
int ParseResp(char *buf, int bytes, SOCKADDR_IN *src, int ttl)
{
IPHeader *iphdr = NULL;
ICMPHeader *icmphdr = NULL;
unsigned short iphdrlen;
struct hostent *lpHostent = NULL;
struct in_addr inaddr = src->sin_addr;
// 提取ICMP包
icmphdr = (ICMPHeader*)(buf + sizeof(IPHeader));
switch (icmphdr->Type)
{
case ICMP_ECHOREPLY: // 得到回应
lpHostent = gethostbyaddr((const char *)&src->sin_addr,
AF_INET, sizeof(struct in_addr));
if (lpHostent != NULL)
printf("%2d %s [%s]\n", ttl, lpHostent->h_name,
inet_ntoa(inaddr));
return 1;
break;
case ICMP_TIMEOUT: // 得到路由器超时信息
printf("%2d %s\n", ttl, inet_ntoa(inaddr));
return 0;
break;
case ICMP_DESTUNREACH: // 不能到达目的地址
printf("%2d %s reports: Host is unreachable\n", ttl,
inet_ntoa(inaddr));
return 1;
break;
default:
printf("non-echo type %d recvd\n", icmphdr->Type);
return 1;
break;
}
return 0;
}
// 填充ICMP包
void FillIMCPData(char * icmp_data, int datasize)
{
ICMPHeader *icmp_hdr;
char *datapart;
icmp_hdr = (ICMPHeader*)icmp_data;
// 设置ICMP包
icmp_hdr->Type = ICMP_ECHO;
icmp_hdr->Code = 0;
icmp_hdr->ID = (u_short)GetCurrentProcessId();
icmp_hdr->Checksum = 0;
icmp_hdr->Seq = 0;
datapart = icmp_data + sizeof(ICMPHeader);
// 在数据区域随便填写一些内容
memset(datapart,'A', datasize - sizeof(ICMPHeader));
}
int main(int argc, char **argv)
{
WSADATA wsd;
SOCKET sockRaw;
HOSTENT *hp = NULL;
SOCKADDR_IN destAddr; // 源地址
SOCKADDR_IN srcAddr; // 目标机地址
int ret;
int datasize;
int srclen = sizeof(srcAddr);
int timeout; // 超时时间
int done=0; // 标识是否探测完毕
int maxhops; // 最大跳数
int ttl = 1;
char *icmp_data;
char *recvbuf;
BOOL bOpt;
u_short seq_no = 0;
// 初始化Winsock
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
printf("WSAStartup() error: %d\n", GetLastError());
return -1;
}
if (argc < 2)
printf("usage: tracert host-name [max-hops]\n");
if (argc == 3)
maxhops = atoi(argv[2]);
else
maxhops = MAX_HOPS;
// 创建ICMP类型的原始套接字
sockRaw = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP,
NULL, 0,WSA_FLAG_OVERLAPPED);
if (sockRaw == INVALID_SOCKET)
{
printf("WSASocket() error: %d\n", WSAGetLastError());
ExitProcess(-1);
}
// 设置超时时间
timeout = 1000;
ret = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO,
(char *)&timeout, sizeof(timeout));
if (ret == SOCKET_ERROR)
{
printf("setsockopt(SO_RCVTIMEO) error: %d\n",
WSAGetLastError());
return -1;
}
timeout = 1000;
ret = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO,
(char *)&timeout, sizeof(timeout));
if (ret == SOCKET_ERROR)
{
printf("setsockopt(SO_SNDTIMEO) error: %d\n",
WSAGetLastError());
return -1;
}
// 解析主机IP地址,判断是否有效
memset(&destAddr,0,sizeof(destAddr));
destAddr.sin_family = AF_INET;
if ((destAddr.sin_addr.s_addr = inet_addr(argv[1])) == INADDR_NONE)
{
hp = gethostbyname(argv[1]);
if (hp)
memcpy(&(destAddr.sin_addr), hp->h_addr, hp->h_length);
else
{
printf("Unable to resolve %s\n",argv[1]);
ExitProcess(-1);
}
}
// 设置发送的包大小
datasize = ICMP_PACKET_SIZE;
datasize += sizeof(ICMPHeader);
// 分配缓冲区空间
icmp_data = new char[MAX_PACKET_SIZE];
recvbuf = new char[MAX_PACKET_SIZE];
// 设置套接字不路由,指示位于基层的网络堆栈,忽略路由表的存在
bOpt = TRUE;
if (setsockopt(sockRaw, SOL_SOCKET, SO_DONTROUTE, (char *)&bOpt,
sizeof(BOOL)) == SOCKET_ERROR)
printf("setsockopt(SO_DONTROUTE) error: %d\n",
WSAGetLastError());
// 填充ICMP首部
memset(icmp_data, 0, MAX_PACKET_SIZE);
FillIMCPData(icmp_data, datasize);
printf("\nTracing route to %s [%s] over a maximum of %d hops:\n\n",
argv[1],inet_ntoa(destAddr.sin_addr), maxhops);
// 开始循环探测路由
for(ttl = 1; ((ttl < maxhops) && (!done)); ttl++)
{
// 设置套接字的TTL值
SetTTL(sockRaw, ttl);
// 设置ICMP首部数据段
((ICMPHeader*)icmp_data)->Checksum = 0;
((ICMPHeader*)icmp_data)->Timestamp = GetTickCount();
((ICMPHeader*)icmp_data)->Seq = seq_no++;
((ICMPHeader*)icmp_data)->Checksum = checksum((u_short*)icmp_data,
datasize);
// 发送ICMP数据包到目标主机
ret = sendto(sockRaw, icmp_data, datasize, 0,
(SOCKADDR *)&destAddr, sizeof(destAddr));
if (ret == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
printf("%2d Send request timed out.\n", ttl);
continue;
}
printf("sendto() error: %d\n", WSAGetLastError());
return -1;
}
// 接收从目标机或路由返回的数据
ret = recvfrom(sockRaw, recvbuf, MAX_PACKET_SIZE, 0,
(struct sockaddr*)&srcAddr, &srclen);
if (ret == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
printf("%2d Request timed out.\n", ttl);
continue;
}
printf("recvsrc() error: %d\n", WSAGetLastError());
return -1;
}
// 解析返回的响应
done = ParseResp(recvbuf, ret, &srcAddr, ttl);
Sleep(1000);
}
delete recvbuf;
delete icmp_data;
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -