zoukankan      html  css  js  c++  java
  • 基于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
    ", usID, (USHORT)GetCurrentProcessId());
    //    printf("usSquNo: %d == DecodeResult: %d
    ", 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
    ");
                        }
    //                    打印IP地址
                        cout << '	' << inet_ntoa(DecodeResult.dwIpAddr) << endl;
                        break;
                    }
                }
    //            接收超时,打印*号
                else if(WSAGetLastError() == WSAETIMEDOUT) {
                    cout << "      " << '*' << '	' << "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
  • 相关阅读:
    数据持久化
    计算机中的上下文
    URL
    MVC之Control中使用AOP
    富客户端
    一些术语的解释
    docker mysql 安装
    用C#开发Windows服务
    java 图片文件Base64编码与二进制编码格式互相转换
    Camera打开前置摄像头或后置摄像头
  • 原文地址:https://www.cnblogs.com/downrainsun/p/11712730.html
Copyright © 2011-2022 走看看