基于icmp的tracert路由追踪程序

https://blog.csdn.net/u013271921/article/details/45488173

#include<winsock2.h>
//#include<iphlpapi.h>
#include <Ws2tcpip.h>
#include<iostream>
#include<conio.h>
//#include<bits/stdc++.h>
using namespace std;

const BYTE ICMP_ECHO_REQUEST = 8;//请求回显
const BYTE ICMP_ECHO_REPLY = 0;//回显应答
const BYTE ICMP_TIMEOUT = 11;//传输超时
const int DEF_ICMP_DAtA_SIZE = 32;//ICMP报文默认数据字段长度
const int MAX_ICMP_PACKET_SIZE = 1024;//ICMP报文最大长度(包含报头)
const DWORD DEF_ICMP_TIMEOUT = 3000;//回显应答超时时间,单位ms
const int DEF_MAX_HOP = 30;//最大跳站数

typedef struct {
    BYTE hdr_len :4;//4位头部长度
    BYTE version :4;//长度版本号
    BYTE tos;//8位服务类型
    USHORT total_len;//16位总长度
    USHORT identifier;//16位标识符
    USHORT frag_and_flags;//3位标志+13位片偏移
    BYTE ttl;//8位生存时间
    BYTE protocol;//8位上层协议号
    USHORT checksum;//16位校验和
    ULONG sourceIP;//32位源IP地址
    ULONG destIP;//32位目的IP地址
} IP_HEADER;

typedef struct {
    BYTE type;//8位类型字段
    BYTE code;//8位代码字段
    USHORT cksum;//16为校验和
    USHORT id;//16位标识符
    USHORT seq;//16位序列号
} ICMP_HEADER;

typedef struct {
//    序列号(输入参数)
    USHORT usSeqNo;
//    往返时间(输入、输出)
    DWORD dwRoundTripTime;
//    返回报文的IP地址(输出参数)
    in_addr dwIpAddr;
} DECODE_RESULT;

//计算网际校验和函数
USHORT checksum(USHORT* pBuf, int iSize) {
    ULONG cksum = 0;
    while(iSize > 1) {
        cksum += *pBuf++;
        iSize -= sizeof(USHORT);
    }
//    如果 iSize 为正,即为奇数个字节
    if(iSize) {
//        则在末尾补上一个字节,使之有偶数个字节
        cksum += *(UCHAR*)pBuf;
    }
    cksum = (cksum>>16) +(cksum & 0xffff);
    cksum += (cksum >> 16);
    return (USHORT)(~cksum);
}

bool DecodeIcmpResponse(char *pBuf, int iPacketSize, DECODE_RESULT& DecodeResult) {
//    计算IP头部长度
    int iIpHdrLen = ((IP_HEADER*)pBuf)->hdr_len * 4;
//    根据ICMP报文类型提取ID字段和序列号字段
    ICMP_HEADER *pIcmpHdr = (ICMP_HEADER *)(pBuf + iIpHdrLen);
    USHORT usID;
    USHORT usSquNo;
//    ICMP回显应答报文
    if(pIcmpHdr->type == ICMP_ECHO_REPLY) {
//        报文ID
        usID = pIcmpHdr->id;
//        序列号
        usSquNo = pIcmpHdr->seq;
    }
//    ICMP超时差错报文
    else if(pIcmpHdr->type == ICMP_TIMEOUT) {
//        载荷中的IP头
        char *pInnerIpHdr = pBuf + iIpHdrLen + sizeof(ICMP_HEADER);
//        载荷中的IP头长
        int iInnerIpHdrLen = ((IP_HEADER*)pInnerIpHdr)->hdr_len * 4;
//        载荷中的ICMP头
        ICMP_HEADER *pInnerIcmpHdr = (ICMP_HEADER*)(pInnerIpHdr + iInnerIpHdrLen);
//        报文ID
        usID = pInnerIcmpHdr->id;
//        序列号
        usSquNo = pInnerIcmpHdr->seq;
    }
    else {
        return false;
    }
//    printf("usID: %d == currentID: %d\n", usID, (USHORT)GetCurrentProcessId());
//    printf("usSquNo: %d == DecodeResult: %d\n", usSquNo, DecodeResult.usSeqNo);
//    检查ID和序列号以确定收到期待数据报
    if(usID != (USHORT)GetCurrentProcessId() || usSquNo != DecodeResult.usSeqNo) {
        return false;
    }
//    记录IP地址并计算往返时间
    DecodeResult.dwIpAddr.s_addr = ((IP_HEADER*)pBuf)->sourceIP;
    DecodeResult.dwRoundTripTime = GetTickCount() - DecodeResult.dwRoundTripTime;
//    打印往返时间信息
    if(DecodeResult.dwRoundTripTime) {
        cout << "   " << DecodeResult.dwRoundTripTime << "ms" << flush;
    }
    else {
        cout << "   " << "<1" << "ms" << flush;
    }
    return true;
}

WSADATA wsa;
char IcmpSendBuf[sizeof(ICMP_HEADER) + DEF_ICMP_DAtA_SIZE];//发送缓冲区
char IcmpRecvBuf[sizeof(ICMP_HEADER) + DEF_ICMP_DAtA_SIZE];//接收缓冲区
char argv[4][100];
int main() {
    WSAStartup(MAKEWORD(2, 2), &wsa);
    //将命令行参数转换成IP地址
    scanf("%s", argv[1]);
    ULONG ulDestIP = inet_addr(argv[1]);
    if(ulDestIP == INADDR_NONE) {
        //转换不成功时按域名解析
        hostent *pHostent = gethostbyname(argv[1]);
        if(pHostent) ulDestIP = (*(in_addr*) pHostent->h_addr).s_addr;
        else {
            WSACleanup();
            return -1;
        }
    }
    //填充目的端socket地址。
    sockaddr_in destSockAddr;
    ZeroMemory(&destSockAddr, sizeof(sockaddr_in));
    destSockAddr.sin_family = AF_INET;
    destSockAddr.sin_addr.s_addr = ulDestIP;
    SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);
    int iTimeout = 3000;
    //设置超时机制
    setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*) &iTimeout, sizeof(iTimeout));
    setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*) &iTimeout, sizeof(iTimeout));
    ICMP_HEADER *pIcmpHeader = (ICMP_HEADER*) IcmpSendBuf;
    pIcmpHeader->type = ICMP_ECHO_REQUEST;//类型为请求回显
    pIcmpHeader->code = 0;//代码字段为0
    pIcmpHeader->id=(USHORT)GetCurrentProcessId();//ID字段为当前进程号
    memset(IcmpSendBuf + sizeof(ICMP_HEADER), 'E', DEF_ICMP_DAtA_SIZE);//数据字段
    USHORT usSeqNo = 0;//ICMP报文序列号
    int iTTL = 1;//TTL初始值为1
    bool bReachDestHost = false;//循环退出标志
    int iMaxHop = DEF_MAX_HOP;//循环的最大次数
    DECODE_RESULT DecodeResult;//传递给报文解码函数的结构化参数
    while(!bReachDestHost && iMaxHop--) {
        //设置IP报头的TTL字段
        setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char*)&iTTL, sizeof(iTTL));
        cout << iTTL << flush;//输出当前序号
        //填充ICMP报文中每次发送时需要变化的字段
        ((ICMP_HEADER*)IcmpSendBuf)->cksum = 0;//校验和先置为0
        ((ICMP_HEADER*)IcmpSendBuf)->seq = htons(usSeqNo++);//填充序列号
        //计算校验和
        ((ICMP_HEADER*)IcmpSendBuf)->cksum = checksum((USHORT*)IcmpSendBuf, sizeof(ICMP_HEADER) + DEF_ICMP_DAtA_SIZE);
        DecodeResult.usSeqNo = ((ICMP_HEADER*)IcmpSendBuf)->seq;//当前序号
        DecodeResult.dwRoundTripTime = GetTickCount();//当前时间
        //发送ICMP回显请求消息
        sendto(sockRaw, IcmpSendBuf, sizeof(IcmpSendBuf), 0, (sockaddr*)&destSockAddr, sizeof(destSockAddr));
//        接收ICMP报文
//        对端Socket地址
        sockaddr_in from;
//        地址结构大小
        int iFromLen = sizeof(from);
//        接收数据长度
        int iReadDataLen;
//        循环接收直到收到所需数据或超时
        while(1) {
            iReadDataLen = recvfrom(sockRaw, IcmpRecvBuf, MAX_ICMP_PACKET_SIZE, 0, (sockaddr*)&from, &iFromLen);
//            有数据到达
            if(iReadDataLen != SOCKET_ERROR) {
//                对数据包进行解析
                if(DecodeIcmpResponse(IcmpRecvBuf, iReadDataLen, DecodeResult)) {
//                    到达目的地,退出循环
                    if(DecodeResult.dwIpAddr.s_addr == destSockAddr.sin_addr.s_addr) {
                        bReachDestHost = true;
//                        printf("reach\n");
                    }
//                    打印IP地址
                    cout << '\t' << inet_ntoa(DecodeResult.dwIpAddr) << endl;
                    break;
                }
            }
//            接收超时,打印*号
            else if(WSAGetLastError() == WSAETIMEDOUT) {
                cout << "      " << '*' << '\t' << "Request timed out." << endl;
                break;
            }
        }
        iTTL++;
    }
    cout << "reach:  " << bReachDestHost << endl;
    return 0;
}

//cqupt  202.202.32.35
//csdn 39.96.126.153
//cnblogs 101.37.113.127

猜你喜欢

转载自www.cnblogs.com/downrainsun/p/11712730.html