tracert 实现伪代码

#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
using namespace std;
#pragma comment(lib, "Ws2_32.lib")

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

//ICMP报头
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;    //往返时间
    in_addr dwIPaddr;        //返回报文的IP地址
}DECODE_RESULT;

//计算网际校验和函数
USHORT checksum(USHORT *pBuf,int iSize)
{
    unsigned long cksum=0;
    while(iSize>1)
    {
        cksum+=*pBuf++;
        iSize-=sizeof(USHORT);
    }
    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,BYTE ICMP_ECHO_REPLY,BYTE  ICMP_TIMEOUT)
{

    //检查数据报大小的合法性
    /*作者*/
    IP_HEADER* pIpHdr = (IP_HEADER*)pBuf;
    /*作者*/
    int iIpHdrLen = pIpHdr->hdr_len * 4;//计算ip头部长度

    /*作者*/
    if (iPacketSize < (int)(iIpHdrLen+sizeof(ICMP_HEADER)))
        return FALSE;
    /*作者*/



    //根据ICMP报文类型提取ID字段和序列号字段
    ICMP_HEADER *pIcmpHdr=(ICMP_HEADER *)(pBuf+iIpHdrLen);
    USHORT usID,usSquNo;
    if(pIcmpHdr->type==ICMP_ECHO_REPLY)    //ICMP回显应答报文
    {
        usID=pIcmpHdr->id;        //报文ID
        usSquNo=pIcmpHdr->seq;    //报文序列号
    }
    else if(pIcmpHdr->type==ICMP_TIMEOUT)//ICMP超时差错报文
    {
        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;        //报文ID
        usSquNo=pInnerIcmpHdr->seq;    //序列号
    }
    else
    {
        return false;
    }

    //检查ID和序列号以确定收到期待数据报
    if(usID!=(USHORT)GetCurrentProcessId()||usSquNo!=DecodeResult.usSeqNo)
    {
        return false;
    }

    //记录IP地址并计算往返时间
    DecodeResult.dwIPaddr.s_addr=pIpHdr->sourceIP;
    DecodeResult.dwRoundTripTime=GetTickCount()-DecodeResult.dwRoundTripTime;

    //处理正确收到的ICMP数据报
    if (pIcmpHdr->type == ICMP_ECHO_REPLY ||pIcmpHdr->type == ICMP_TIMEOUT)
    {
        //输出往返时间信息
        if(DecodeResult.dwRoundTripTime)
            cout<<"      "<<DecodeResult.dwRoundTripTime<<"ms"<<flush;
        else
            cout<<"      "<<"<1ms"<<flush;
    }
    return true;

}

int main()
{
    //初始化Windows sockets网络环境
    WSADATA wsa;
    WSAStartup(MAKEWORD(2,2),&wsa);
    char IpAddress[255];
    cout<<"请输入一个IP地址或域名:";
    cin>>IpAddress;

    //将命令行转化为IP地址
     u_long ulDestIP=inet_addr(IpAddress);
     //u_long ulDestIP=inet_addr(argv[1]);

    if(ulDestIP==INADDR_NONE)
    {
        //转换不成功时按域名解析
        hostent * pHostent=gethostbyname(IpAddress);
        //hostent * pHostent=gethostbyname(argv[1]);
        if(pHostent)
        {
            ulDestIP=(*(in_addr*)pHostent->h_addr).s_addr;
        }
        else
        {
            //cout<<"输入的IP地址或域名无效!"<<endl;
            WSACleanup();
            return -1;
        }
    }
    cout<<"Tracing root to "<<IpAddress<<" with a maximum of 30 hops.\n"<<endl;
    //cout<<"Tracing root to "<<argv[1]<<" with a maximum of 30 hops.\n"<<endl;
    //填充目地端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回显请求消息,并以TTL递增的顺序发送报文
    //ICMP类型字段
    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;    //回显应答超时时间
    const int DEF_MAX_HOP=30;            //最大跳站数

    //填充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));        //初始化接收缓冲区
    //作者 自加

    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 iMaxHot=DEF_MAX_HOP;    //循环的最大次数
    DECODE_RESULT DecodeResult;    //传递给报文解码函数的结构化参数
    while(!bReachDestHost&&iMaxHot--)
    {
        //设置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;    //当前序号
        cout<<" DecodeResult.usSeqNo:  "<< DecodeResult.usSeqNo<<endl;
        DecodeResult.dwRoundTripTime=GetTickCount();        //当前时间
        //cout<<"DecodeResult.dwRoundTripTime: "<<DecodeResult.dwRoundTripTime<<endl;
        //发送TCP回显请求信息
        int sendto_ad=sendto(sockRaw,IcmpSendBuf,sizeof(IcmpSendBuf),0,(sockaddr*)&destSockAddr,sizeof(destSockAddr));
        cout<<"sizeof(IcmpSendBuf) "<<sizeof(IcmpSendBuf)<<"    sendto: "<<sendto_ad<<endl;
        //由于sendto 返回值等于ICMPSENDBUF的长度
        //接收ICMP差错报文并进行解析处理
        sockaddr_in from;        //对端socket地址
        int iFromLen=sizeof(from);    //地址结构大小
        int iReadDataLen;        //接收数据长度
        while(1)
        {
            //接收数据有问题
            iReadDataLen=recvfrom(sockRaw,IcmpRecvBuf,MAX_ICMP_PACKET_SIZE,0,(sockaddr*)&from,&iFromLen);
            cout<<"sockRaw: "<<sockRaw<<endl;
            //cout<<"iReadDataLen ="<<iReadDataLen<<endl;
            //cout<<"SOCKET_ERROR = "<<SOCKET_ERROR<<endl;
            if(iReadDataLen!=SOCKET_ERROR)//有数据到达
            {
                //cout<<"adfa"<<endl;

                //对数据包进行解码
                if(DecodeIcmpResponse(IcmpRecvBuf,iReadDataLen,DecodeResult,ICMP_ECHO_REPLY,ICMP_TIMEOUT))
                {
                    //到达目的地,退出循环
                    if(DecodeResult.dwIPaddr.s_addr==destSockAddr.sin_addr.s_addr)
                        bReachDestHost=true;
                    //输出IP地址
                    cout<<'\t'<<inet_ntoa(DecodeResult.dwIPaddr)<<endl;

                    break;
                }
            }
            else if(WSAGetLastError()==WSAETIMEDOUT)    //接收超时,输出*号
            {
                cout<<"last error: "<<WSAGetLastError();
                cout<<" WSAETIMEDOUT: "<<WSAETIMEDOUT;
                cout<<"         *"<<'\t'<<"Request timed out."<<endl;
                break;
            }
            else
            {
                break;
            }
        }
        iTTL++;    //递增TTL值
    }
   return 0;
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容