zoukankan      html  css  js  c++  java
  • 用boost库实现traceroute小工具

          参考了网上几个 traceroute的实现版本,存在一些缺陷,比如没有做超时处理,或者只能在window下使用。自己用boost实现了一个traceroute小工具,在window下正常运行。

          先来看下面实现的原理。这些说明来自维基百科。traceroute,现代Linux系统称为tracepath,Windows系统称为tracert,是一种电脑网络工具。它可显示数据包在IP网络经过的路由器的IP地址。 程序利用增加存活时间(TTL)值来实现其功能的。每当数据包经过一个路由器,其存活时间就会减1。当其存活时间是0时,主机便取消数据包,并传送一个ICMP TTL数据包给原数据包的发出者。

          下面是具体实现的源代码:

      1 //
      2 // traceroute.cpp
      3 // ~~~~~~~~
      4 //
      5 // BoSheng Lu 2013-04-25
      6 //
      7 // Version 1.0.
      8 //
      9 
     10 #include <boost/asio.hpp>
     11 #include <boost/bind.hpp>
     12 #include<boost/thread.hpp>
     13 #include <istream>
     14 #include <iostream>
     15 #include<iomanip>
     16 #include<string> 
     17 #include <ostream>
     18 
     19 #include "icmp_header.hpp"
     20 #include "ipv4_header.hpp"
     21 
     22 using boost::asio::ip::icmp;
     23 using boost::asio::deadline_timer;
     24 namespace posix_time = boost::posix_time;
     25 
     26 class traceroute
     27 {
     28 public:
     29     traceroute(boost::asio::io_service& io_service, const char* destination)
     30         : resolver_(io_service), socket_(io_service, icmp::v4()),
     31         sequence_number_(0), max_hop_(30)
     32     {
     33         icmp::resolver::query query(icmp::v4(), destination, "");
     34         destination_ = *resolver_.resolve(query);
     35 
     36         std::cout << "\nTracing route to " << destination 
     37         << " [" << destination_.address().to_string() << "]"
     38         << " with a maximum of " << max_hop_ << " hops.\n" << std::endl;
     39 
     40 #if defined(BOOST_WINDOWS)
     41         int timeout = 5000;
     42         if( setsockopt(socket_.native(), SOL_SOCKET, SO_RCVTIMEO, 
     43             (const char*)&timeout, sizeof(timeout)))
     44         {
     45              std::cout << "RCVTIMEO not set properly." << std::endl;
     46             throw std::runtime_error("RCVTIMEO not set properly");
     47         }
     48 #else
     49         struct timeval tv;
     50         tv.tv_sec  = 5; 
     51         tv.tv_usec = 0;         
     52         if( setsockopt(socket_.native(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
     53         {
     54             std::cout << "RCVTIMEO not set properly." << std::endl;
     55             throw std::runtime_error("RCVTIMEO not set properly");
     56         }
     57 #endif
     58 
     59         reach_dest_host_ = false;
     60         int ttl = 1;
     61         while (!reach_dest_host_ && max_hop_--)
     62         {
     63             const boost::asio::ip::unicast::hops option(ttl);
     64             socket_.set_option(option);
     65 
     66             boost::asio::ip::unicast::hops op;
     67             socket_.get_option(op);
     68             if( ttl !=  op.value() )
     69             {
     70                 std::cout << "TTL not set properly. Should be "
     71                     << ttl << " but was set to "
     72                     << op.value() << '.' << std::endl;
     73                 throw std::runtime_error("TTL not set properly");        
     74             }
     75 
     76             std::cout << std::setw(3) << ttl << std::flush;
     77             start_send();
     78             start_receive();
     79          
     80             ttl++;
     81         }//while
     82     }
     83 
     84 private:
     85     void start_send()
     86     {
     87         std::string body("\"Hello!\" from Asio ping.");
     88 
     89         // Create an ICMP header for an echo request.
     90         icmp_header echo_request;
     91         echo_request.type(icmp_header::echo_request);
     92         echo_request.code(0);
     93         echo_request.identifier(get_identifier());
     94         echo_request.sequence_number(++sequence_number_);
     95         compute_checksum(echo_request, body.begin(), body.end());
     96 
     97         // Encode the request packet.
     98         boost::asio::streambuf request_buffer;
     99         std::ostream os(&request_buffer);
    100         os << echo_request << body;
    101 
    102         // Send the request.
    103         time_sent_ = posix_time::microsec_clock::universal_time();
    104         socket_.send_to(request_buffer.data(), destination_);  
    105     }
    106 
    107 
    108     void start_receive()
    109     {
    110         // Discard any data already in the buffer.
    111         reply_buffer_.consume(reply_buffer_.size());
    112 
    113         // Wait for a reply. We prepare the buffer to receive up to 64KB.
    114         boost::system::error_code ec;
    115         boost::asio::socket_base::message_flags flags = 0;
    116         size_t len = socket_.receive(reply_buffer_.prepare(65536), flags, ec);
    117     
    118         handle_receive(len, ec);
    119     }
    120 
    121     void handle_receive(std::size_t length, boost::system::error_code& ec)
    122     {
    123         // The actual number of bytes received is committed to the buffer so that we
    124         // can extract it using a std::istream object.
    125         reply_buffer_.commit(length);
    126 
    127         // Decode the reply packet.
    128         std::istream is(&reply_buffer_);
    129         ipv4_header ipv4_hdr;
    130         is >> ipv4_hdr;
    131 
    132         if(ec)
    133         {
    134             std::cout << std::setw(9) << '*' << '\t' << "Request timed out." << std::endl;
    135             return;
    136         }
    137     
    138         posix_time::ptime now = posix_time::microsec_clock::universal_time();
    139         long long dwRoundTripTime = (now - time_sent_).total_milliseconds();
    140 
    141         if(dwRoundTripTime)
    142         {
    143             std::cout << std::setw(6) << dwRoundTripTime << " ms"  << std::flush;
    144         }
    145         else
    146         {
    147             std::cout << std::setw(6) << "<1" << " ms" << std::flush;
    148         }
    149         
    150         boost::this_thread::sleep(boost::posix_time::seconds(2)); 
    151     
    152         std::cout << '\t' << ipv4_hdr.source_address() << std::endl;
    153 
    154         // Print out some information about the reply packet.
    155         if( ipv4_hdr.source_address().to_string() == destination_.address().to_string() )
    156         {
    157             reach_dest_host_ = true;
    158             std::cout << std::endl;
    159             std::cout << std::endl;
    160             std::cout << "traceroute sucess!" << std::endl;
    161 
    162             icmp_header icmp_hdr;
    163             is >> icmp_hdr;
    164 
    165             if (is && icmp_hdr.type() == icmp_header::echo_reply
    166                 && icmp_hdr.identifier() == get_identifier()
    167                 && icmp_hdr.sequence_number() == sequence_number_)
    168             {
    169                 // Print out some information about the reply packet.
    170                 std::cout << length - ipv4_hdr.header_length()
    171                  << " bytes from " << ipv4_hdr.source_address()
    172                 << ": icmp_seq=" << icmp_hdr.sequence_number()
    173                 << ", ttl=" << ipv4_hdr.time_to_live()
    174                 << ", time=" << dwRoundTripTime << " ms"
    175                 << std::endl;
    176             }
    177         }
    178      }
    179 
    180     static unsigned short get_identifier()
    181     {
    182 #if defined(BOOST_WINDOWS)
    183         return static_cast<unsigned short>(::GetCurrentProcessId());
    184 #else
    185         return static_cast<unsigned short>(::getpid());
    186 #endif
    187     }
    188 
    189     icmp::resolver resolver_;
    190     icmp::endpoint destination_;
    191     icmp::socket socket_;
    192     unsigned short sequence_number_;
    193     posix_time::ptime time_sent_;
    194     boost::asio::streambuf reply_buffer_;
    195     bool reach_dest_host_;
    196     std::size_t max_hop_;
    197 };
    198 
    199 int main(int argc, char* argv[])
    200 {
    201     try
    202     {
    203         if(argc != 2)
    204         {
    205             std::cerr << "Usage: ping <host>" << std::endl;
    206 #if !defined(BOOST_WINDOWS)
    207             std::cerr << "(You may need to run this program as root.)" << std::endl;
    208 #endif
    209             return 1;
    210         }
    211 
    212         boost::asio::io_service io_service;
    213         traceroute p(io_service, argv[1]);
    214         io_service.run();
    215     }
    216     catch (std::exception& e)
    217     {
    218         std::cerr << "Exception: " << e.what() << std::endl;
    219     }
    220 }

           这个源码实现使用了boost库附带的boost asio使用示例中的代码,下面也将这个例子中使用到的代码附上,方便使用。至于boost库的编译使用另请参考其他的文章。

          下面是ipv4_header.hpp

    //
    // ipv4_header.hpp
    // ~~~~~~~~~~~~~~~
    //
    // Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
    //
    // Distributed under the Boost Software License, Version 1.0. (See accompanying
    // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
    //
    
    #ifndef IPV4_HEADER_HPP
    #define IPV4_HEADER_HPP
    
    #include <algorithm>
    #include <boost/asio/ip/address_v4.hpp>
    
    // Packet header for IPv4.
    //
    // The wire format of an IPv4 header is:
    // 
    // 0               8               16                             31
    // +-------+-------+---------------+------------------------------+      ---
    // |       |       |               |                              |       ^
    // |version|header |    type of    |    total length in bytes     |       |
    // |  (4)  | length|    service    |                              |       |
    // +-------+-------+---------------+-+-+-+------------------------+       |
    // |                               | | | |                        |       |
    // |        identification         |0|D|M|    fragment offset     |       |
    // |                               | |F|F|                        |       |
    // +---------------+---------------+-+-+-+------------------------+       |
    // |               |               |                              |       |
    // | time to live  |   protocol    |       header checksum        |   20 bytes
    // |               |               |                              |       |
    // +---------------+---------------+------------------------------+       |
    // |                                                              |       |
    // |                      source IPv4 address                     |       |
    // |                                                              |       |
    // +--------------------------------------------------------------+       |
    // |                                                              |       |
    // |                   destination IPv4 address                   |       |
    // |                                                              |       v
    // +--------------------------------------------------------------+      ---
    // |                                                              |       ^
    // |                                                              |       |
    // /                        options (if any)                      /    0 - 40
    // /                                                              /     bytes
    // |                                                              |       |
    // |                                                              |       v
    // +--------------------------------------------------------------+      ---
    
    class ipv4_header
    {
    public:
      ipv4_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); }
    
      unsigned char version() const { return (rep_[0] >> 4) & 0xF; }
      unsigned short header_length() const { return (rep_[0] & 0xF) * 4; }
      unsigned char type_of_service() const { return rep_[1]; }
      unsigned short total_length() const { return decode(2, 3); }
      unsigned short identification() const { return decode(4, 5); }
      bool dont_fragment() const { return (rep_[6] & 0x40) != 0; }
      bool more_fragments() const { return (rep_[6] & 0x20) != 0; }
      unsigned short fragment_offset() const { return decode(6, 7) & 0x1FFF; }
      unsigned int time_to_live() const { return rep_[8]; }
      unsigned char protocol() const { return rep_[9]; }
      unsigned short header_checksum() const { return decode(10, 11); }
    
      boost::asio::ip::address_v4 source_address() const
      {
        boost::asio::ip::address_v4::bytes_type bytes
          = { { rep_[12], rep_[13], rep_[14], rep_[15] } };
        return boost::asio::ip::address_v4(bytes);
      }
    
      boost::asio::ip::address_v4 destination_address() const
      {
        boost::asio::ip::address_v4::bytes_type bytes
          = { { rep_[16], rep_[17], rep_[18], rep_[19] } };
        return boost::asio::ip::address_v4(bytes);
      }
    
      friend std::istream& operator>>(std::istream& is, ipv4_header& header)
      {
        is.read(reinterpret_cast<char*>(header.rep_), 20);
        if (header.version() != 4)
          is.setstate(std::ios::failbit);
        std::streamsize options_length = header.header_length() - 20;
        if (options_length < 0 || options_length > 40)
          is.setstate(std::ios::failbit);
        else
          is.read(reinterpret_cast<char*>(header.rep_) + 20, options_length);
        return is;
      }
    
    private:
      unsigned short decode(int a, int b) const
        { return (rep_[a] << 8) + rep_[b]; }
    
      unsigned char rep_[60];
    };
    
    #endif // IPV4_HEADER_HPP

           下面是icmp_header.hpp

    //
    // icmp_header.hpp
    // ~~~~~~~~~~~~~~~
    //
    // Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
    //
    // Distributed under the Boost Software License, Version 1.0. (See accompanying
    // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
    //
    
    #ifndef ICMP_HEADER_HPP
    #define ICMP_HEADER_HPP
    
    #include <istream>
    #include <ostream>
    #include <algorithm>
    
    // ICMP header for both IPv4 and IPv6.
    //
    // The wire format of an ICMP header is:
    // 
    // 0               8               16                             31
    // +---------------+---------------+------------------------------+      ---
    // |               |               |                              |       ^
    // |     type      |     code      |          checksum            |       |
    // |               |               |                              |       |
    // +---------------+---------------+------------------------------+    8 bytes
    // |                               |                              |       |
    // |          identifier           |       sequence number        |       |
    // |                               |                              |       v
    // +-------------------------------+------------------------------+      ---
    
    class icmp_header
    {
    public:
      enum { echo_reply = 0, destination_unreachable = 3, source_quench = 4,
        redirect = 5, echo_request = 8, time_exceeded = 11, parameter_problem = 12,
        timestamp_request = 13, timestamp_reply = 14, info_request = 15,
        info_reply = 16, address_request = 17, address_reply = 18 };
    
      icmp_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); }
    
      unsigned char type() const { return rep_[0]; }
      unsigned char code() const { return rep_[1]; }
      unsigned short checksum() const { return decode(2, 3); }
      unsigned short identifier() const { return decode(4, 5); }
      unsigned short sequence_number() const { return decode(6, 7); }
    
      void type(unsigned char n) { rep_[0] = n; }
      void code(unsigned char n) { rep_[1] = n; }
      void checksum(unsigned short n) { encode(2, 3, n); }
      void identifier(unsigned short n) { encode(4, 5, n); }
      void sequence_number(unsigned short n) { encode(6, 7, n); }
    
      friend std::istream& operator>>(std::istream& is, icmp_header& header)
        { return is.read(reinterpret_cast<char*>(header.rep_), 8); }
    
      friend std::ostream& operator<<(std::ostream& os, const icmp_header& header)
        { return os.write(reinterpret_cast<const char*>(header.rep_), 8); }
    
    private:
      unsigned short decode(int a, int b) const
        { return (rep_[a] << 8) + rep_[b]; }
    
      void encode(int a, int b, unsigned short n)
      {
        rep_[a] = static_cast<unsigned char>(n >> 8);
        rep_[b] = static_cast<unsigned char>(n & 0xFF);
      }
    
      unsigned char rep_[8];
    };
    
    template <typename Iterator>
    void compute_checksum(icmp_header& header,
        Iterator body_begin, Iterator body_end)
    {
      unsigned int sum = (header.type() << 8) + header.code()
        + header.identifier() + header.sequence_number();
    
      Iterator body_iter = body_begin;
      while (body_iter != body_end)
      {
        sum += (static_cast<unsigned char>(*body_iter++) << 8);
        if (body_iter != body_end)
          sum += static_cast<unsigned char>(*body_iter++);
      }
    
      sum = (sum >> 16) + (sum & 0xFFFF);
      sum += (sum >> 16);
      header.checksum(static_cast<unsigned short>(~sum));
    }
    
    #endif // ICMP_HEADER_HPP
  • 相关阅读:
    温故vue对vue计算属性computed的分析
    bootStrap Table 如何使用
    css 的一些知识点的整理
    css 宽高自适应的div 元素 如何居中 垂直居中
    BOM,Dom 回顾
    DOM
    字符串的一些常用方法 string
    js if for 详解 获取元素方式 及一些js 基础知识
    Java入门1
    python字符串
  • 原文地址:https://www.cnblogs.com/spruce/p/3044233.html
Copyright © 2011-2022 走看看