zoukankan      html  css  js  c++  java
  • TraceRoute(tracert)源码(基于原始套接字实现)

     TraceRoute(tracert)源码(基于原始套接字实现) 佟强 2008.11.4

        TraceRoute实现原理 http://blog.csdn.net/microtong/archive/2008/11/04/3220450.aspx

    本程序实现Windows下tracert的功能,程序使用原始套接字发送ICMP回显请求报文,本接收ICMP超时差错报文,和ICMP回显应答报文,得到每一跳的路由器IP地址和往返时间。

    1. //TraceRoute.h
    2. #ifndef _ITRACERT_H_
    3. #define _ITRACERT_H_
    4. #pragma pack(1)
    5. //IP数据报头
    6. typedef struct
    7. {
    8.  unsigned char hdr_len :4;  // length of the header
    9.  unsigned char version :4;  // version of IP
    10.  unsigned char tos;   // type of service
    11.  unsigned short total_len;  // total length of the packet
    12.  unsigned short identifier;  // unique identifier
    13.  unsigned short frag_and_flags; // flags
    14.  unsigned char ttl;   // time to live
    15.  unsigned char protocol;  // protocol (TCP, UDP etc)
    16.  unsigned short checksum;  // IP checksum
    17.  unsigned long sourceIP;  // source IP address
    18.  unsigned long destIP;   // destination IP address
    19. } IP_HEADER;
    20. //ICMP数据报头
    21. typedef struct
    22. {
    23.  BYTE type;  //8位类型
    24.  BYTE code;  //8位代码
    25.  USHORT cksum;  //16位校验和
    26.  USHORT id;   //16位标识符
    27.  USHORT seq;  //16位序列号
    28. } ICMP_HEADER;
    29. //解码结果
    30. typedef struct
    31. {
    32.  USHORT usSeqNo;   //包序列号
    33.  DWORD dwRoundTripTime; //往返时间
    34.  in_addr dwIPaddr;  //对端IP地址
    35. } DECODE_RESULT;
    36. #pragma pack()
    37. //ICMP类型字段
    38. const BYTE ICMP_ECHO_REQUEST = 8; //请求回显
    39. const BYTE ICMP_ECHO_REPLY  = 0; //回显应答
    40. const BYTE ICMP_TIMEOUT   = 11; //传输超时
    41. const DWORD DEF_ICMP_TIMEOUT = 3000; //默认超时时间,单位ms
    42. const int DEF_ICMP_DATA_SIZE = 32; //默认ICMP数据部分长度
    43. const int MAX_ICMP_PACKET_SIZE = 1024; //最大ICMP数据报的大小
    44. const int DEF_MAX_HOP = 30;    //最大跳站数
    45. USHORT GenerateChecksum(USHORT* pBuf, int iSize);
    46. BOOL DecodeIcmpResponse(char* pBuf, int iPacketSize, DECODE_RESULT& stDecodeResult);
    47. #endif // _ITRACERT_H_
    48. //TraceRoute.cpp
    49. /*----------------------------------------------------------
    50. 功能说明:该程序简单实现了Windows操作系统的tracert命令功能,
    51.       可以输出IP报文从本机出发到达目的主机所经过的路由信息。
    52. -----------------------------------------------------------*/
    53. #include <iostream>
    54. #include <iomanip>
    55. #include <winsock2.h>
    56. #include <ws2tcpip.h>
    57. #include <stdio.h>
    58. #include "TraceRoute.h"
    59. #pragma comment(lib,"ws2_32")
    60. using namespace std;
    61. int main(int argc, char* argv[])
    62. {
    63.  //检查命令行参数
    64.  if (argc != 2)
    65.  {
    66.   cerr << "/nUsage: itracert ip_or_hostname/n";
    67.   return -1;
    68.  }
    69.  //初始化winsock2环境
    70.  WSADATA wsa;
    71.  if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    72.  {
    73.   cerr << "/nFailed to initialize the WinSock2 DLL/n"
    74.     << "error code: " << WSAGetLastError() << endl;
    75.   return -1;
    76.  }
    77.  //将命令行参数转换为IP地址
    78.  u_long ulDestIP = inet_addr(argv[1]);
    79.  if (ulDestIP == INADDR_NONE)
    80.  {
    81.   //转换不成功时按域名解析
    82.   hostent* pHostent = gethostbyname(argv[1]);
    83.   if (pHostent)
    84.   {
    85.    ulDestIP = (*(in_addr*)pHostent->h_addr).s_addr;
    86.    //输出屏幕信息
    87.    cout << "/nTracing route to " << argv[1] 
    88.      << " [" << inet_ntoa(*(in_addr*)(&ulDestIP)) << "]"
    89.      << " with a maximum of " << DEF_MAX_HOP << " hops./n" << endl;
    90.   }
    91.   else //解析主机名失败
    92.   {
    93.    cerr << "/nCould not resolve the host name " << argv[1] << '/n'
    94.      << "error code: " << WSAGetLastError() << endl;
    95.    WSACleanup();
    96.    return -1;
    97.   }
    98.  }
    99.  else
    100.  {
    101.   //输出屏幕信息
    102.   cout << "/nTracing route to " << argv[1] 
    103.     << " with a maximum of " << DEF_MAX_HOP << " hops./n" << endl;
    104.  }
    105.  //填充目的Socket地址
    106.  sockaddr_in destSockAddr;
    107.  ZeroMemory(&destSockAddr, sizeof(sockaddr_in));
    108.  destSockAddr.sin_family = AF_INET;
    109.  destSockAddr.sin_addr.s_addr = ulDestIP;
    110.  //使用ICMP协议创建Raw Socket
    111.  SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);
    112.  if (sockRaw == INVALID_SOCKET)
    113.  {
    114.   cerr << "/nFailed to create a raw socket/n"
    115.     << "error code: " << WSAGetLastError() << endl;
    116.   WSACleanup();
    117.   return -1;
    118.  }
    119.  //设置端口属性
    120.  int iTimeout = DEF_ICMP_TIMEOUT;
    121.  if (setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&iTimeout, sizeof(iTimeout)) == SOCKET_ERROR)
    122.  {
    123.   cerr << "/nFailed to set recv timeout/n"
    124.     << "error code: " << WSAGetLastError() << endl;
    125.   closesocket(sockRaw);
    126.   WSACleanup();
    127.   return -1;
    128.  }
    129.  //创建ICMP包发送缓冲区和接收缓冲区
    130.  char IcmpSendBuf[sizeof(ICMP_HEADER)+DEF_ICMP_DATA_SIZE];
    131.  memset(IcmpSendBuf, 0, sizeof(IcmpSendBuf));
    132.  char IcmpRecvBuf[MAX_ICMP_PACKET_SIZE];
    133.  memset(IcmpRecvBuf, 0, sizeof(IcmpRecvBuf));
    134.  //填充待发送的ICMP包
    135.  ICMP_HEADER* pIcmpHeader = (ICMP_HEADER*)IcmpSendBuf;
    136.  pIcmpHeader->type = ICMP_ECHO_REQUEST;
    137.  pIcmpHeader->code = 0;
    138.  pIcmpHeader->id = (USHORT)GetCurrentProcessId();
    139.  memset(IcmpSendBuf+sizeof(ICMP_HEADER), 'E', DEF_ICMP_DATA_SIZE);
    140.  //开始探测路由
    141.  DECODE_RESULT stDecodeResult;
    142.  BOOL bReachDestHost = FALSE;
    143.  USHORT usSeqNo = 0;
    144.  int iTTL = 1;
    145.  int iMaxHop = DEF_MAX_HOP;
    146.  while (!bReachDestHost && iMaxHop--)
    147.  {
    148.   //设置IP数据报头的ttl字段
    149.   setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char*)&iTTL, sizeof(iTTL));
    150.   //输出当前跳站数作为路由信息序号
    151.   cout << setw(3) << iTTL << flush;
    152.   //填充ICMP数据报剩余字段
    153.   ((ICMP_HEADER*)IcmpSendBuf)->cksum = 0;
    154.   ((ICMP_HEADER*)IcmpSendBuf)->seq = htons(usSeqNo++);
    155.   ((ICMP_HEADER*)IcmpSendBuf)->cksum = GenerateChecksum((USHORT*)IcmpSendBuf, sizeof(ICMP_HEADER)+DEF_ICMP_DATA_SIZE);
    156.   
    157.   //记录序列号和当前时间
    158.   stDecodeResult.usSeqNo = ((ICMP_HEADER*)IcmpSendBuf)->seq;
    159.   stDecodeResult.dwRoundTripTime = GetTickCount();
    160.   
    161.   //发送ICMP的EchoRequest数据报
    162.   if (sendto(sockRaw, IcmpSendBuf, sizeof(IcmpSendBuf), 0, 
    163.        (sockaddr*)&destSockAddr, sizeof(destSockAddr)) == SOCKET_ERROR)
    164.   {
    165.    //如果目的主机不可达则直接退出
    166.    if (WSAGetLastError() == WSAEHOSTUNREACH)
    167.     cout << '/t' << "Destination host unreachable./n" 
    168.       << "/nTrace complete./n" << endl;
    169.    closesocket(sockRaw);
    170.    WSACleanup();
    171.    return 0;
    172.   }
    173.   //接收ICMP的EchoReply数据报
    174.   //因为收到的可能并非程序所期待的数据报,所以需要循环接收直到收到所要数据或超时
    175.   sockaddr_in from;
    176.   int iFromLen = sizeof(from);
    177.   int iReadDataLen;
    178.   while (1)
    179.   {
    180.    //等待数据到达
    181.    iReadDataLen = recvfrom(sockRaw, IcmpRecvBuf, MAX_ICMP_PACKET_SIZE, 
    182.          0, (sockaddr*)&from, &iFromLen);
    183.    if (iReadDataLen != SOCKET_ERROR) //有数据包到达
    184.    {
    185.     //解码得到的数据包,如果解码正确则跳出接收循环发送下一个EchoRequest包
    186.     if (DecodeIcmpResponse(IcmpRecvBuf, iReadDataLen, stDecodeResult))
    187.     {
    188.      if (stDecodeResult.dwIPaddr.s_addr == destSockAddr.sin_addr.s_addr)
    189.       bReachDestHost = TRUE;
    190.      cout << '/t' << inet_ntoa(stDecodeResult.dwIPaddr) << endl;
    191.      break;
    192.     }
    193.    }
    194.    else if (WSAGetLastError() == WSAETIMEDOUT) //接收超时,打印星号
    195.    {
    196.     cout << setw(9) << '*' << '/t' << "Request timed out." << endl;
    197.     break;
    198.    }
    199.    else
    200.    {
    201.     cerr << "/nFailed to call recvfrom/n"
    202.       << "error code: " << WSAGetLastError() << endl;
    203.     closesocket(sockRaw);
    204.     WSACleanup();
    205.     return -1;
    206.    }
    207.   }
    208.   //TTL值加1
    209.   iTTL++;
    210.  }
    211.  //输出屏幕信息
    212.  cout << "/nTrace complete./n" << endl;
    213.  closesocket(sockRaw);
    214.  WSACleanup();
    215.  return 0;
    216. }
    217. //产生网际校验和
    218. USHORT GenerateChecksum(USHORT* pBuf, int iSize) 
    219. {
    220.  unsigned long cksum = 0;
    221.  while (iSize>1) 
    222.  {
    223.   cksum += *pBuf++;
    224.   iSize -= sizeof(USHORT);
    225.  }
    226.  if (iSize) 
    227.   cksum += *(UCHAR*)pBuf;
    228.  cksum = (cksum >> 16) + (cksum & 0xffff);
    229.  cksum += (cksum >> 16);
    230.  return (USHORT)(~cksum);
    231. }
    232. //解码得到的数据报
    233. BOOL DecodeIcmpResponse(char* pBuf, int iPacketSize, DECODE_RESULT& stDecodeResult)
    234. {
    235.  //检查数据报大小的合法性
    236.  IP_HEADER* pIpHdr = (IP_HEADER*)pBuf;
    237.  int iIpHdrLen = pIpHdr->hdr_len * 4;
    238.  if (iPacketSize < (int)(iIpHdrLen+sizeof(ICMP_HEADER)))
    239.   return FALSE;
    240.  //按照ICMP包类型检查id字段和序列号以确定是否是程序应接收的Icmp包
    241.  ICMP_HEADER* pIcmpHdr = (ICMP_HEADER*)(pBuf+iIpHdrLen);
    242.  USHORT usID, usSquNo;
    243.  if (pIcmpHdr->type == ICMP_ECHO_REPLY)
    244.  {
    245.   usID = pIcmpHdr->id;
    246.   usSquNo = pIcmpHdr->seq;
    247.  }
    248.  else if(pIcmpHdr->type == ICMP_TIMEOUT)
    249.  {
    250.   char* pInnerIpHdr = pBuf+iIpHdrLen+sizeof(ICMP_HEADER);  //载荷中的IP头
    251.   int iInnerIPHdrLen = ((IP_HEADER*)pInnerIpHdr)->hdr_len * 4;//载荷中的IP头长
    252.   ICMP_HEADER* pInnerIcmpHdr = (ICMP_HEADER*)(pInnerIpHdr+iInnerIPHdrLen);//载荷中的ICMP头
    253.   usID = pInnerIcmpHdr->id;
    254.   usSquNo = pInnerIcmpHdr->seq;
    255.  }
    256.  else
    257.   return FALSE;
    258.  if (usID != (USHORT)GetCurrentProcessId() || usSquNo !=stDecodeResult.usSeqNo) 
    259.   return FALSE;
    260.  //处理正确收到的ICMP数据报
    261.  if (pIcmpHdr->type == ICMP_ECHO_REPLY ||
    262.   pIcmpHdr->type == ICMP_TIMEOUT)
    263.  {
    264.   //返回解码结果
    265.   stDecodeResult.dwIPaddr.s_addr = pIpHdr->sourceIP;
    266.   stDecodeResult.dwRoundTripTime = GetTickCount()-stDecodeResult.dwRoundTripTime;
    267.   //打印屏幕信息
    268.   if (stDecodeResult.dwRoundTripTime)
    269.    cout << setw(6) << stDecodeResult.dwRoundTripTime << " ms" << flush;
    270.   else
    271.    cout << setw(6) << "<1" << " ms" << flush;
    272.   return TRUE;
    273.  }
    274.  return FALSE;
    275. }
  • 相关阅读:
    Angularjs总结(一)表单验证
    list集合中指定字段去重
    NodeJS学习笔记—2.AMD规范
    NodeJS学习笔记—1.CommonJS规范
    WCF上传、下载、删除文件
    .net RAW(16)与GUID互相转换
    Angularjs总结(六) 上传附件
    可以打开mdb文件的小软件
    数据库导出导入操作(expdp,impdp)
    用Ueditor存入数据库带HTML标签的文本,从数据库取出来后,anjular用ng-bind-html处理带HTML标签的文本
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6168127.html
Copyright © 2011-2022 走看看