#include #include #include #include #include #include "icmp_header.hpp" // from boost::asio examples on boost website // (also pasted last in this file) using boost::asio::ip::icmp; class traceroute: public boost::noncopyable { public: traceroute(const char* host): io(), resolver(io), socket(io, icmp::v4()), sequence_number(0) { icmp::resolver::query query(icmp::v4(), host, ""); destination = *resolver.resolve(query); } ~traceroute() { socket.close(); } void trace() { for( int ttl(1); ttl < 31 ; ttl++) { const boost::asio::ip::unicast::hops option( ttl ); socket.set_option(option); boost::asio::ip::unicast::hops op; socket.get_option(op); if( ttl != op.value() ) { std::ostringstream o; o << "TTL not set properly. Should be " << ttl << " but was set to " << op.value() << '.'; throw std::runtime_error(o.str()); } // Create an ICMP header for an echo request. icmp_header echo_request; echo_request.type(icmp_header::echo_request); echo_request.code(0); echo_request.identifier(get_identifier()); echo_request.sequence_number(++sequence_number); const std::string body(""); compute_checksum(echo_request, body.begin(), body.end()); // Encode the request packet. boost::asio::streambuf request_buffer; std::ostream os(&request_buffer); os << echo_request << body; // Send the request. socket.send_to(request_buffer.data(), destination); // Recieve some data and parse it. std::vector data(64,0); const std::size_t nr( socket.receive( boost::asio::buffer(data) ) ); if( nr < 16 ) { throw std::runtime_error("To few bytes returned."); } std::ostringstream remote_ip; remote_ip << (int)data[12] << '.' << (int)data[13] << '.' << (int)data[14] << '.' << (int)data[15]; std::cout << remote_ip.str() << '\n'; if( boost::asio::ip::address_v4::from_string( remote_ip.str() ) == destination.address() ) { break; } } } private: static const int port = 33434; boost::asio::io_service io; boost::asio::ip::icmp::resolver resolver; boost::asio::ip::icmp::socket socket ; unsigned short sequence_number; boost::asio::ip::icmp::endpoint destination; static unsigned short get_identifier() { #if defined(BOOST_WINDOWS) return static_cast(::GetCurrentProcessId()); #else return static_cast(::getpid()); #endif } }; int main( int argc, char** argv) { try { if( argc != 2 ) { throw std::invalid_argument("Usage: traceroute host"); } traceroute T( argv[1] ); T.trace(); } catch( const std::exception& e ) { std::cerr << e.what() << '\n'; } } // // icmp_header.hpp // ~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2010 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 #include #include // 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(header.rep_), 8); } friend std::ostream& operator<<(std::ostream& os, const icmp_header& header) { return os.write(reinterpret_cast(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(n >> 8); rep_[b] = static_cast(n & 0xFF); } unsigned char rep_[8]; }; template 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(*body_iter++) << 8); if (body_iter != body_end) sum += static_cast(*body_iter++); } sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); header.checksum(static_cast(~sum)); } #endif // ICMP_HEADER_HPP