#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不到消息)。原因不明。程序貌似沒錯。