#include <iostream>
#include <iomanip>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
using namespace std;
//IP資料報頭
typedef struct
{
unsigned char hdr_len :4; // length of the header
unsigned char version :4; // version of IP
unsigned char tos; // type of service
unsigned short total_len; // total length of the packet
unsigned short identifier; // unique identifier
unsigned short frag_and_flags; // flags
unsigned char ttl; // time to live
unsigned char protocol; // protocol (TCP, UDP etc)
unsigned short checksum; // IP checksum
unsigned long sourceIP; // source IP address
unsigned long destIP; // destination IP address
} IP_HEADER;
//ICMP資料報頭
typedef struct
{
unsigned char type; //8位型別
unsigned char code; //8位代碼
unsigned short cksum; //16位校驗和
unsigned short id; //16位識別符號
unsigned short seq; //16位序列號
} ICMP_HEADER;
//解碼結果
typedef struct
{
unsigned short usSeqNo; //包序列號
unsigned long dwRoundTripTime; //往返時間
in_addr dwIPaddr; //對端IP地址
} DECODE_RESULT;
//ICMP型別欄位
const char ICMP_ECHO_REQUEST = 8; //請求回顯
const char ICMP_ECHO_REPLY = 0; //回顯應答
const char ICMP_TIMEOUT = 11; //傳輸超時
const long DEF_ICMP_TIMEOUT = 3000; //默認超時時間,單位ms
const int DEF_ICMP_DATA_SIZE = 32; //默認ICMP資料部分長度
const int MAX_ICMP_PACKET_SIZE = 1024; //最大ICMP資料報的大小
const int DEF_MAX_HOP = 30; //最大跳站數
//產生網際校驗和
unsigned short GenerateChecksum(unsigned short* pBuf, int iSize)
{
unsigned long cksum = 0;
while (iSize>1)
{
cksum += *pBuf++;
iSize -= sizeof(unsigned short);
}
if (iSize)
cksum += *(unsigned char*)pBuf;
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (unsigned short)(~cksum);
}
//解碼得到的資料報
BOOL DecodeIcmpResponse(char* pBuf, int iPacketSize, DECODE_RESULT& stDecodeResult)
{
//檢查資料報大小的合法性
IP_HEADER* pIpHdr = (IP_HEADER*)pBuf;
int iIpHdrLen = pIpHdr->hdr_len * 4;
if (iPacketSize < (int)(iIpHdrLen+sizeof(ICMP_HEADER)))
return -1;
//按照ICMP包型別檢查id欄位和序列號以確定是否是程式應接收的Icmp包
ICMP_HEADER* pIcmpHdr = (ICMP_HEADER*)(pBuf+iIpHdrLen);
unsigned short usID, usSquNo;
if (pIcmpHdr->type == ICMP_ECHO_REPLY)
{
usID = pIcmpHdr->id;
usSquNo = pIcmpHdr->seq;
}
else if(pIcmpHdr->type == ICMP_TIMEOUT)
{
char* pInnerIpHdr = pBuf+iIpHdrLen+sizeof(ICMP_HEADER); //載荷中的IP頭
int iInnerIPHdrLen = ((IP_HEADER*)pInnerIpHdr)->hdr_len * 4;//載荷中的IP頭長
ICMP_HEADER* pInnerIcmpHdr = (ICMP_HEADER*)(pInnerIpHdr+iInnerIPHdrLen);//載荷中的ICMP頭
usID = pInnerIcmpHdr->id;
usSquNo = pInnerIcmpHdr->seq;
}
else
return -1;
if (usID != (USHORT)GetCurrentProcessId() || usSquNo !=stDecodeResult.usSeqNo)
return -1;
//處理正確收到的ICMP資料報
if (pIcmpHdr->type == ICMP_ECHO_REPLY ||
pIcmpHdr->type == ICMP_TIMEOUT)
{
//回傳解碼結果
stDecodeResult.dwIPaddr.s_addr = pIpHdr->sourceIP;
stDecodeResult.dwRoundTripTime = GetTickCount()-stDecodeResult.dwRoundTripTime;
//列印螢屏資訊
if (stDecodeResult.dwRoundTripTime)
cout << setw(6) << stDecodeResult.dwRoundTripTime << " ms" << flush;
else
cout << setw(6) << "<1" << " ms" << flush;
return TRUE;
}
return -1;
}
unsigned long gethostname(char name[])
{
unsigned long ulDestIP = inet_addr(name);
if (ulDestIP == INADDR_NONE)
{
//轉換不成功時按域名決議
hostent* pHostent = gethostbyname(name);
if (pHostent)
{
ulDestIP = (*(in_addr*)pHostent->h_addr).s_addr;
//輸出螢屏資訊
cout << "\nTracing route to " << name
<< " [" << inet_ntoa(*(in_addr*)(&ulDestIP)) << "]"
<< " with a maximum of " << DEF_MAX_HOP << " hops.\n" << endl;
}
else //決議主機名失敗
{
cout << "/nCould not resolve the host name " << name << '\n'
<< "error code: " << WSAGetLastError() << endl;
exit(0);
}
}
else
{
//輸出螢屏資訊
cout << "\nTracing route to " << name
<< " with a maximum of " << DEF_MAX_HOP << " hops.\n" << endl;
}
return ulDestIP;
}
ICMP_HEADER* packetICMP( char IcmpSendBuf[],unsigned short usSeqNo)
{
ICMP_HEADER* pIcmpHeader = (ICMP_HEADER*)IcmpSendBuf;
pIcmpHeader->type = ICMP_ECHO_REQUEST;
pIcmpHeader->code = 0;
pIcmpHeader->id = (USHORT)GetCurrentProcessId();
memset(IcmpSendBuf+sizeof(ICMP_HEADER), 'E', DEF_ICMP_DATA_SIZE);
((ICMP_HEADER*)IcmpSendBuf)->cksum = 0;
((ICMP_HEADER*)IcmpSendBuf)->seq = htons(usSeqNo);
((ICMP_HEADER*)IcmpSendBuf)->cksum = GenerateChecksum((USHORT*)IcmpSendBuf, sizeof(ICMP_HEADER)+DEF_ICMP_DATA_SIZE);
return (ICMP_HEADER*)IcmpSendBuf;
}
int main()
{
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
cout << "\nFailed to initialize the WinSock2 DLL\n"
<< "error code: " << WSAGetLastError() << endl;
return -1;
}
char string[100];
cin>>string;
//填充目的Socket地址
sockaddr_in destSockAddr;
ZeroMemory(&destSockAddr, sizeof(sockaddr_in));
destSockAddr.sin_family = AF_INET;
destSockAddr.sin_addr.s_addr = gethostname(string);
//使用ICMP協議創建Raw Socket
SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (sockRaw == INVALID_SOCKET)
{
cout << "\nFailed to create a raw socket\n"
<< "error code: " << WSAGetLastError() << endl;
WSACleanup();
return -1;
}
//設定埠屬性
int iTimeout = DEF_ICMP_TIMEOUT;
if (setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&iTimeout, sizeof(iTimeout)) == SOCKET_ERROR)
{
cout << "\nFailed to set recv timeout\n"
<< "error code: " << WSAGetLastError() << endl;
closesocket(sockRaw);
WSACleanup();
return -1;
}
//創建ICMP包發送緩沖區和接識訓沖區
char IcmpSendBuf[sizeof(ICMP_HEADER)+DEF_ICMP_DATA_SIZE];
memset(IcmpSendBuf, 0, sizeof(IcmpSendBuf));
char IcmpRecvBuf[MAX_ICMP_PACKET_SIZE];
memset(IcmpRecvBuf, 0, sizeof(IcmpRecvBuf));
DECODE_RESULT stDecodeResult;
BOOL bReachDestHost = FALSE;
unsigned short usSeqNo = 0;
int iTTL = 1;
int iMaxHop = DEF_MAX_HOP;
while (!bReachDestHost && iMaxHop--)
{
//設定IP資料報頭的ttl欄位
setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char*)&iTTL, sizeof(iTTL));
//輸出當前跳站數作為路由資訊序號
cout << setw(3) << iTTL << " ";
int send=sendto(sockRaw, (char *)packetICMP(IcmpSendBuf,usSeqNo++), sizeof(IcmpSendBuf), 0,
(sockaddr*)&destSockAddr, sizeof(destSockAddr));
stDecodeResult.usSeqNo = ((ICMP_HEADER*)IcmpSendBuf)->seq;
stDecodeResult.dwRoundTripTime = GetTickCount();
if(send==SOCKET_ERROR)
{
//如果目的主機不可達則直接退出
if (WSAGetLastError() == WSAEHOSTUNREACH)
cout << "Destination host unreachable.\n"
<< "\nTrace complete.\n" << endl;
closesocket(sockRaw);
WSACleanup();
return 0;
}
sockaddr_in from;
int iFromLen = sizeof(SOCKADDR);
int iReadDataLen;
while (true)
{
//等待資料到達
iReadDataLen = recvfrom(sockRaw, IcmpRecvBuf, MAX_ICMP_PACKET_SIZE,
0, (sockaddr*)&from, &iFromLen);
if (iReadDataLen != SOCKET_ERROR) //有資料包到達
{
//解碼得到的資料包,如果解碼正確則跳出接識訓圈發送下一個EchoRequest包
if (DecodeIcmpResponse(IcmpRecvBuf, iReadDataLen, stDecodeResult))
{
if (stDecodeResult.dwIPaddr.s_addr == destSockAddr.sin_addr.s_addr)
bReachDestHost = TRUE;
cout <<" "<<inet_ntoa(stDecodeResult.dwIPaddr) << endl;
break;
}
}
else if (WSAGetLastError() == WSAETIMEDOUT) //接收超時,列印星號
{
cout << setw(9) <<'*' << "Request timed out." << endl;
break;
}
else
{
cout<< "\nFailed to call recvfrom\n"
<< "error code: " << WSAGetLastError() << endl;
closesocket(sockRaw);
WSACleanup();
return -1;
}
}
//TTL值加1
iTTL++;
}
cout << "\nTrace complete.\n" << endl;
closesocket(sockRaw);
WSACleanup();
return 0;
}
代碼如上,求大神糾錯
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/124437.html
標籤:網絡及通訊開發
