Traceroute(因未知原因未能測試成功,請指教)

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章