#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#define DEF_ICMP_DATA_SIZE 1024
#define MAX_ICMP_PACKET_SIZE 2048
#include "iostream"
#include "winsock2.h"
#include "string.h"
#include "ws2tcpip.h"
#pragma comment(lib,"WS2_32")
using namespace std;
typedef struct icmp_hdr {
unsigned char icmp_type; //icmp包类型
unsigned char icmp_code; //代码
unsigned short icmp_checksum; //校验和
unsigned short icmp_id; //标识
unsigned short icmp_sequence; //序列号
unsigned long icmp_timestamp; //时间戳
} IcmpHeader;
unsigned short checksum(unsigned short *buff, int size);
int main(int argc, char **argv) {
//加载winsock2.2
WSADATA wsaData;
int ret;
if ((ret = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) {
cout << "初始化WinSock2.2出错!";
return -1;
}
//输入目的IP地址或域名
char szDestIp[256] = { 0 };
cout << "请输入目的地址:" << endl;
cin.getline(szDestIp, sizeof(szDestIp));
//解析域名
unsigned long ulDestIP = inet_addr(szDestIp);
if (ulDestIP == INADDR_NONE) { //如果不是IP地址
hostent* pHostent = gethostbyname(szDestIp);
if (pHostent != NULL) { //如果是域名,就解析域名
ulDestIP = (*(in_addr*)pHostent->h_addr).s_addr;
}
else {
cout << "无法解析主机名" << endl;
WSACleanup();
return -1;
}
}
cout << "路由:" << szDestIp << "(" << inet_ntoa(*(in_addr *)(&ulDestIP)) << ")" << endl;
//创建接收icmp包的原始套接字,绑定到本地端口
SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); //收发icmp包的原始套接字
sockaddr_in in;
in.sin_family = AF_INET;
in.sin_port = 0;
in.sin_addr.S_un.S_addr = INADDR_ANY;
if (bind(sRaw, (sockaddr *)&in, sizeof(in)) == SOCKET_ERROR) {
cout << "destination bind failed" << endl;
WSACleanup();
return -1;
}
int nTime = 10*1000;
setsockopt(sRaw, SOL_SOCKET, SO_RCVTIMEO, (char *)&nTime, sizeof(nTime));
//构造icmp包
char IcmpSendBuf[sizeof(IcmpHeader) + DEF_ICMP_DATA_SIZE];
char IcmpRecvBuf[MAX_ICMP_PACKET_SIZE];
memset(IcmpSendBuf, 0, sizeof(0));
memset(IcmpRecvBuf, 0, sizeof(0));
//填充icmp包
IcmpHeader *pIcmp = (IcmpHeader *)IcmpSendBuf;
pIcmp->icmp_type = 8; //回显请求
pIcmp->icmp_code = 0;
pIcmp->icmp_id = (unsigned short)GetCurrentProcessId();
memset(IcmpSendBuf + sizeof(IcmpHeader), 'E', DEF_ICMP_DATA_SIZE); //填充数据部分
//填充发送目的地址
sockaddr_in destAddr;
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(22);
destAddr.sin_addr.S_un.S_addr = ulDestIP;
int nRet, nTick, nTTL = 1, iSeqNo = 0;
//发送报文并接收路由器的差错报告报文
IcmpHeader *pICMPHdr; //指向报文首部的指针
char *szIP;
SOCKADDR_IN recvAddr;
int n, nLen = sizeof(recvAddr);
do {
setsockopt(sRaw, IPPROTO_IP, IP_TTL, (char *)&nTTL, sizeof(nTTL));
nTick = GetTickCount();
//填写序列号,计算校验和
((IcmpHeader *)IcmpSendBuf)->icmp_checksum = 0;
((IcmpHeader *)IcmpSendBuf)->icmp_sequence = htons(iSeqNo++);
((IcmpHeader *)IcmpSendBuf)->icmp_checksum = checksum((unsigned short*)IcmpSendBuf, sizeof(IcmpHeader) + DEF_ICMP_DATA_SIZE);
nRet = sendto(sRaw, IcmpSendBuf, sizeof(IcmpSendBuf), 0, (sockaddr *)&destAddr, sizeof(destAddr));
if (nRet == SOCKET_ERROR) {
cout << "send failed!" << endl;
break;
}
n = 0;
do {
n++;
//cout << n << endl;
nRet = recvfrom(sRaw, IcmpRecvBuf, sizeof(IcmpRecvBuf), 0, (sockaddr *)&recvAddr, &nLen);
//cout << sRaw << endl;
if (nRet == SOCKET_ERROR) {
cout << "receive failed! 错误码:" << WSAGetLastError() << endl;
closesocket(sRaw);
WSACleanup();
return -1;
}
pICMPHdr = (IcmpHeader *)&IcmpRecvBuf[20];
szIP = inet_ntoa(recvAddr.sin_addr);
if (pICMPHdr->icmp_type == 11 || pICMPHdr->icmp_type == 0 || pICMPHdr->icmp_type == 3)
break;
} while (n < 10);
if (n > 10)continue;
cout << nTTL << " 地址:" << szIP << " 用时" << GetTickCount() - nTick << " ms" << endl;
if (pICMPHdr->icmp_type == 3) {
switch (pICMPHdr->icmp_code) {
case 0:cout << "目的网络不可达!" << endl; break;
case 1:cout << "目的主机不可达!" << endl; break;
case 6:cout << "未知目的网络!" << endl; break;
case 7:cout << "未知目的主机!" << endl; break;
}
break;
}
if (destAddr.sin_addr.S_un.S_addr == recvAddr.sin_addr.S_un.S_addr) {
cout << "目标可达." << endl;
break;
}
} while (nTTL++ < 30);
closesocket(sRaw);
WSACleanup();
return 0;
}
unsigned short checksum(unsigned short * buff, int size) {
unsigned long cksum = 0;
while (size > 1) {
cksum += *buff++;
size -= sizeof(unsigned short);
}
if (size)cksum += *(char *)buff;
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (unsigned short)(~cksum);
}
实际上如果输入我家路由器的IP可以追踪到,但是输入www.baidu.com或者其ip地址就追踪不到(receive不到消息)。原因不明。程序貌似没错。