├── asio_helper.h ├── async_timer.cpp ├── async_timer.h ├── auto_cancel.h ├── client.cpp ├── common.h ├── config_parser.cpp ├── config_parser.h ├── connecting.cpp ├── connecting.h ├── cpp_utils.h ├── icmp_header.h ├── ipv4_header.h ├── main.cpp ├── net_common.h ├── network.h ├── ping.cpp ├── ping.h ├── posix_stacktrace.h ├── server.cpp ├── tcp_service.cpp ├── tcp_service.h ├── tunnel.cpp ├── tunnel.h ├── tunnel_common.h ├── tunnel_tcp_service.cpp ├── tunnel_tcp_service.h ├── tunnel_udp_service.cpp ├── tunnel_udp_service.h ├── udp2tcp_tunnel.pro ├── udp_service.cpp ├── udp_service.h └── underlying_transport_impl.h /asio_helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | #include "net_common.h" 4 | 5 | namespace asio_helper 6 | { 7 | 8 | class shared_const_buffer 9 | { 10 | public: 11 | template 12 | shared_const_buffer(byte_it begin, byte_it end, optional const& dest = none) 13 | 14 | : data_ (make_shared(begin, end)) 15 | , buffer_(buffer(*data_)) 16 | , dest_ (dest) 17 | { 18 | } 19 | 20 | shared_const_buffer(const void* data, size_t size, optional const& dest = none) 21 | 22 | : data_ (make_shared(reinterpret_cast(data), reinterpret_cast(data) + size)) 23 | , buffer_(buffer(*data_)) 24 | , dest_ (dest) 25 | { 26 | } 27 | 28 | // Implement the ConstBufferSequence requirements 29 | typedef const_buffer value_type; 30 | typedef const const_buffer* const_iterator; 31 | 32 | const_iterator begin() const { return &buffer_ ; } 33 | const_iterator end () const { return &buffer_ + 1; } 34 | 35 | operator const_buffer() const{ return buffer_; } 36 | 37 | public: 38 | optional const& dest() const { return dest_; } 39 | 40 | private: 41 | typedef vector array_t; 42 | 43 | private: 44 | shared_ptr data_; 45 | const_buffer buffer_; 46 | 47 | private: 48 | optional dest_; 49 | }; 50 | 51 | // Implement the ConvertibleToConstBuffer requirements for shared_const_buffer 52 | inline const void* buffer_cast_helper(const shared_const_buffer& b) { return buffer_cast(*(b.begin())); } 53 | inline std::size_t buffer_size_helper(const shared_const_buffer& b) { return buffer_size(*(b.begin())) ; } 54 | 55 | template 56 | class shared_const_buffers_seq 57 | { 58 | public: 59 | typedef list buffer_seq_t; 60 | 61 | public: 62 | shared_const_buffers_seq() 63 | { 64 | } 65 | 66 | template 67 | shared_const_buffers_seq(buffer_it begin, buffer_it end) 68 | : data_(make_shared(begin, end)) 69 | { 70 | } 71 | 72 | explicit shared_const_buffers_seq(buffer_seq_t&& seq) 73 | : data_(make_shared(forward(seq))) 74 | { 75 | } 76 | 77 | // Implement the ConstBufferSequence requirements. 78 | typedef typename buffer_seq_t::value_type value_type; 79 | typedef typename buffer_seq_t::const_iterator const_iterator; 80 | 81 | const_iterator begin() const { return data_->begin(); } 82 | const_iterator end () const { return data_->end (); } 83 | 84 | private: 85 | shared_ptr data_; 86 | }; 87 | 88 | 89 | } // namespace details 90 | -------------------------------------------------------------------------------- /async_timer.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "async_timer.h" 3 | 4 | struct async_timer::impl 5 | : enable_cancel_resource 6 | { 7 | typedef shared_ptr ptr_t; 8 | 9 | static ptr_t create(io_service& io, on_timer_f const& on_timer, posix_time::time_duration const& dtime) 10 | { 11 | ptr_t ptr(new impl(io, on_timer)); 12 | 13 | ptr->timer_.expires_from_now(dtime); 14 | ptr->timer_.async_wait(boost::bind(&impl::on_timer, ptr, _1)); 15 | 16 | return ptr; 17 | } 18 | 19 | private: 20 | impl(io_service& io, on_timer_f const& on_timer) 21 | : enable_cancel_resource(timer_) 22 | 23 | , timer_ (io) 24 | , on_timer_ (on_timer) 25 | { 26 | } 27 | 28 | void on_timer(error_code const& code) 29 | { 30 | if(cancelled() || code) 31 | return; 32 | 33 | on_timer_(); 34 | } 35 | 36 | private: 37 | deadline_timer timer_; 38 | on_timer_f on_timer_; 39 | }; 40 | 41 | 42 | //////////////////////////////////////////////////////////////////////// 43 | async_timer::async_timer(io_service& io, on_timer_f const& on_timer) 44 | : io_ (io) 45 | , on_timer_ (on_timer) 46 | { 47 | } 48 | 49 | async_timer::~async_timer() 50 | { 51 | } 52 | 53 | void async_timer::wait(posix_time::time_duration const& dtime) 54 | { 55 | pimpl_.reset(impl::create(io_, on_timer_, dtime)); 56 | } 57 | 58 | void async_timer::cancel() 59 | { 60 | pimpl_.reset(); 61 | } 62 | -------------------------------------------------------------------------------- /async_timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "auto_cancel.h" 3 | 4 | typedef function on_timer_f; 5 | 6 | struct async_timer 7 | { 8 | async_timer(io_service& io, on_timer_f const& on_timer); 9 | ~async_timer(); 10 | 11 | void wait(posix_time::time_duration const& dtime); 12 | void cancel(); 13 | 14 | private: 15 | struct impl; 16 | auto_cancel_ptr pimpl_; 17 | 18 | private: 19 | io_service& io_; 20 | on_timer_f on_timer_; 21 | }; 22 | 23 | -------------------------------------------------------------------------------- /auto_cancel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct enable_cancel_resource 4 | { 5 | template 6 | explicit enable_cancel_resource(resource& res) 7 | : on_cancel_(bind(&resource::cancel, ref(res))) 8 | , cancelled_(false) 9 | {} 10 | 11 | void cancel() 12 | { 13 | cancelled_ = true; 14 | 15 | if (on_cancel_) 16 | on_cancel_(); 17 | } 18 | 19 | void detach_resource() 20 | { 21 | on_cancel_ = 0; 22 | } 23 | 24 | bool cancelled() const 25 | { 26 | return cancelled_; 27 | } 28 | 29 | private: 30 | typedef function on_cancel_f; 31 | 32 | private: 33 | on_cancel_f on_cancel_; 34 | bool cancelled_; 35 | }; 36 | 37 | template 38 | struct auto_cancel_ptr 39 | : noncopyable 40 | { 41 | typedef shared_ptr ptr_t; 42 | 43 | explicit auto_cancel_ptr(ptr_t ptr = ptr_t()) 44 | { 45 | reset(ptr); 46 | } 47 | 48 | ~auto_cancel_ptr() 49 | { 50 | reset(); 51 | } 52 | 53 | void reset(ptr_t ptr = ptr_t()) 54 | { 55 | if (ptr_) 56 | { 57 | ptr_->cancel(); 58 | ptr_.reset(); 59 | } 60 | 61 | ptr_ = ptr; 62 | } 63 | 64 | T& operator* () const { return *ptr_; } 65 | T* operator->() const { return ptr_.get(); } 66 | T* get () const { return ptr_.get(); } 67 | 68 | private: 69 | ptr_t ptr_; 70 | }; 71 | -------------------------------------------------------------------------------- /client.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "network.h" 3 | #include "async_timer.h" 4 | 5 | struct client 6 | { 7 | client(io_service& io, string server, short port) 8 | : timer_ (io, bind(&client::on_tick, this)) 9 | , connector_(io, network::endpoint(server, port), bind(&client::on_connected, this, _1, _2), bind(&client::on_disconnected, this), trace_error) 10 | , port_ (port) 11 | { 12 | //cock_the_clock(1); 13 | } 14 | 15 | private: 16 | void on_connected(tcp::socket& sock, network::endpoint const&) 17 | { 18 | cout << "Bingo, connected!" << endl; 19 | 20 | tcp_sock_ = in_place( 21 | ref(sock), 22 | bind(&client::on_receive, this, _1, _2), 23 | bind(&client::on_disconnected, this), 24 | trace_error); 25 | 26 | udp_sock_ = in_place( 27 | ref(sock.get_io_service()), 28 | network::endpoint(ip::address_v4::broadcast(), port_ + 1), 29 | none, 30 | bind(&client::on_receive, this, _1, _2), 31 | trace_error); 32 | 33 | cock_the_clock(1); 34 | } 35 | 36 | void cock_the_clock(size_t seconds) 37 | { 38 | if (!tcp_sock_) 39 | cout << "Cocking the clock without tcp socket" << endl; 40 | 41 | // test 42 | timer_.wait(boost::posix_time::milliseconds(2 * seconds)); 43 | } 44 | 45 | void on_tick() 46 | { 47 | cock_the_clock(1); 48 | 49 | static size_t counter = 0; 50 | test_msg_data msg(++counter); 51 | 52 | //udp_sock_->send(&msg, sizeof(msg)); 53 | if (!tcp_sock_) 54 | cout << "oppa, no tcp socket here!" << endl; 55 | 56 | tcp_sock_->send(&msg, sizeof(msg)); 57 | //cout << "client has sent: " << msg.counter << endl; 58 | } 59 | 60 | void on_receive(const void* data, size_t size) 61 | { 62 | if (size != sizeof(test_msg_data)) 63 | cout << "Size is wrong, got " << size << " should be: " << sizeof(test_msg_data) << endl; 64 | 65 | assert(size == sizeof(test_msg_data)); 66 | 67 | test_msg_data const& msg = *reinterpret_cast(data); 68 | cout << "Received back from server: " << msg.counter << endl; 69 | 70 | if (last_counter_) 71 | assert(msg.counter - *last_counter_ == 1); 72 | 73 | last_counter_ = msg.counter; 74 | } 75 | 76 | void on_disconnected() 77 | { 78 | timer_.cancel(); 79 | tcp_sock_ .reset(); 80 | udp_sock_ .reset(); 81 | 82 | cout << "Olala, remote host has been disconnected" << endl; 83 | } 84 | 85 | private: 86 | optional tcp_sock_; 87 | optional udp_sock_; 88 | async_timer timer_; 89 | network::async_connector connector_; 90 | optional last_counter_; 91 | const size_t port_; 92 | }; 93 | 94 | /////////////////////////////////////////////////// 95 | // ping test 96 | /* 97 | void on_pinged(io_service& io, string server, bool success, posix_time::milliseconds const& delay); 98 | 99 | void start_ping(io_service& io, string server) 100 | { 101 | network::ping(io, server, posix_time::milliseconds(500), bind(&on_pinged, ref(io), server, _1, _2)); 102 | } 103 | 104 | void on_pinged(io_service& io, string server, bool success, posix_time::milliseconds const& delay) 105 | { 106 | cout << "ping " << (success ? "succedded" : "failed") << " timeout: " << delay << endl; 107 | 108 | deadline_timer timer(io); 109 | timer.expires_from_now(posix_time::seconds(1)); 110 | timer.wait(); 111 | 112 | start_ping(io, server); 113 | } 114 | */ 115 | 116 | void run_tcp_client(string server, size_t port) 117 | { 118 | io_service io; 119 | client cl(io, server, port); 120 | 121 | //start_ping(io, server); 122 | 123 | io.run(); 124 | } 125 | 126 | 127 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | 36 | using std::cin; 37 | using std::cerr; 38 | using std::cout; 39 | using std::endl; 40 | 41 | using std::string; 42 | using std::vector; 43 | using std::list; 44 | using std::array; 45 | using std::queue; 46 | using std::set; 47 | using std::map; 48 | 49 | using std::move; 50 | using std::forward; 51 | 52 | using boost::ref; 53 | using boost::cref; 54 | 55 | using boost::bind; 56 | using boost::function; 57 | 58 | using boost::lexical_cast; 59 | using boost::bad_lexical_cast; 60 | 61 | using boost::format; 62 | 63 | using boost::optional; 64 | using boost::in_place; 65 | 66 | using boost::split; 67 | using boost::is_any_of; 68 | 69 | using boost::enable_shared_from_this; 70 | using boost::noncopyable; 71 | 72 | using boost::none; 73 | 74 | using boost::system::error_code; 75 | using boost::asio::ip::tcp; 76 | using boost::asio::ip::udp; 77 | 78 | using namespace boost::asio; 79 | 80 | using boost::shared_ptr; 81 | using boost::weak_ptr; 82 | using boost::scoped_ptr; 83 | using boost::make_shared; 84 | 85 | namespace posix_time = boost::posix_time; 86 | 87 | inline void trace_error_from_source(string source, error_code const& code) 88 | { 89 | cerr 90 | << "Error: " 91 | << source 92 | << " -- " 93 | << code.message() 94 | << "; category: " << code.category().name() 95 | << "; value: " << code.value() 96 | << endl; 97 | } 98 | 99 | typedef function on_error_f; 100 | inline on_error_f error_tracer(string source) 101 | { 102 | return bind(trace_error_from_source, source, _1); 103 | } 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /config_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "tunnel.h" 3 | #include "config_parser.h" 4 | 5 | namespace 6 | { 7 | using namespace network; 8 | using namespace boost; 9 | using namespace boost::algorithm; 10 | 11 | struct config_parser 12 | { 13 | config_parser(string filename, tunnel& t) 14 | : tunnel_(t) 15 | { 16 | std::ifstream in(filename); 17 | 18 | while(in.good()) 19 | { 20 | const size_t bufsize = 0x400; 21 | 22 | char buf[bufsize]; 23 | in.getline(buf, bufsize); 24 | 25 | string str = buf; 26 | trim(str); 27 | 28 | if (str.empty()) 29 | continue; 30 | 31 | if (starts_with(str, "tcp")) 32 | process_tcp(str); 33 | 34 | else if (starts_with(str, "#")) 35 | ; // skip - it's a comment 36 | 37 | else 38 | process_udp_point(str); 39 | } 40 | } 41 | 42 | private: 43 | void process_tcp(string str) 44 | { 45 | vector tokens; 46 | split(tokens, str, boost::is_any_of("=:"), boost::token_compress_on); 47 | 48 | if (tokens.front() == "tcp_client") 49 | { 50 | if (tokens.size() < 3 || tokens.size() > 4) 51 | throw cfg_error("invalid tcp client description: " + str); 52 | 53 | endpoint point(tokens[1] + ":" + tokens[2]); 54 | 55 | if (tokens.size() == 3) 56 | tunnel_.create_tcp_sender(true, point); 57 | 58 | if (tokens.size() == 4) 59 | tunnel_.create_tcp_sender(true, point, boost::posix_time::seconds(lexical_cast(tokens[3]))); 60 | } 61 | else if (tokens.front() == "tcp_server") 62 | { 63 | if (tokens.size() != 3) 64 | throw cfg_error("invalid tcp server description: " + str); 65 | 66 | tunnel_.create_tcp_sender(false, endpoint(tokens[1] + ":" + tokens[2])); 67 | } 68 | else 69 | throw cfg_error("invalid tcp point description: " + tokens.front()); 70 | } 71 | 72 | void process_udp_point(string str) 73 | { 74 | vector tokens; 75 | split(tokens, str, boost::is_any_of("->"), boost::token_compress_on); 76 | 77 | if (tokens.size() < 2 || tokens.size() > 3) 78 | throw cfg_error("invalid udp point description: " + str); 79 | 80 | if (tokens.size() == 2) 81 | tunnel_.create_udp_receiver(endpoint(tokens[0]), endpoint(tokens[1])); 82 | 83 | if (tokens.size() == 3) 84 | tunnel_.create_udp_receiver(endpoint(tokens[0]), endpoint(tokens[2]), tokens[1]); 85 | } 86 | 87 | private: 88 | tunnel& tunnel_; 89 | }; 90 | 91 | 92 | } // 'anonymous' 93 | 94 | void parse_config(string filename, tunnel& t) 95 | { 96 | config_parser(filename, t); 97 | } 98 | -------------------------------------------------------------------------------- /config_parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct cfg_error 4 | : std::runtime_error 5 | { 6 | cfg_error(std::string err) 7 | : runtime_error(err) 8 | { 9 | } 10 | }; 11 | 12 | struct tunnel; 13 | void parse_config(string filename, tunnel& t); 14 | -------------------------------------------------------------------------------- /connecting.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "connecting.h" 3 | #include "asio_helper.h" 4 | #include "auto_cancel.h" 5 | 6 | namespace network 7 | { 8 | 9 | struct async_connector::impl 10 | : noncopyable 11 | , enable_cancel_resource 12 | { 13 | typedef shared_ptr ptr_t; 14 | 15 | static ptr_t create(io_service& io, endpoint const& remote_server, on_connected_f const& on_connected, on_refused_f const& on_refused, on_error_f const& on_error) 16 | { 17 | // don't worry - the object will be deleted automatically after establishing connection 18 | ptr_t connector(new impl(io, on_connected, on_refused, on_error)); 19 | connector->sock_.async_connect(remote_server, bind(&impl::on_connected, connector, _1)); 20 | 21 | return connector; 22 | } 23 | 24 | private: 25 | impl(io_service& io, on_connected_f const& on_connected, on_refused_f const& on_refused, on_error_f const& on_error) 26 | 27 | : enable_cancel_resource(sock_) 28 | 29 | , sock_ (io) 30 | , on_connected_ (on_connected) 31 | , on_refused_ (on_refused) 32 | , on_error_ (on_error ) 33 | { 34 | assert(on_connected_); 35 | } 36 | 37 | void on_connected(error_code const& code) 38 | { 39 | if (cancelled()) 40 | return; 41 | 42 | if (code) 43 | { 44 | if ((code.category() == error::system_category && code.value() == error::connection_refused) || 45 | (code.category() == error::system_category && code.value() == error::timed_out)) 46 | { 47 | on_refused_(code); 48 | return; 49 | } 50 | 51 | on_error_(code); 52 | return; 53 | } 54 | 55 | tcp::endpoint e = sock_.remote_endpoint(); 56 | on_connected_(sock_, endpoint(e.address().to_v4(), e.port())); 57 | 58 | enable_cancel_resource::detach_resource(); 59 | } 60 | 61 | 62 | private: 63 | tcp::socket sock_; 64 | on_connected_f on_connected_; 65 | on_refused_f on_refused_; 66 | on_error_f on_error_; 67 | }; 68 | 69 | //////////////////////////////////////////////////////////////////////// 70 | async_connector::async_connector(io_service& io, endpoint const& remote_server, on_connected_f const& on_connected, on_refused_f const& on_refused, on_error_f const& on_error) 71 | : pimpl_(impl::create(io, remote_server, on_connected, on_refused, on_error)) 72 | { 73 | } 74 | 75 | async_connector::~async_connector() 76 | { 77 | } 78 | 79 | /////////////////////////////////////////////////////////////////////// 80 | // underlying_acceptor 81 | 82 | struct async_acceptor::impl 83 | : noncopyable 84 | , enable_shared_from_this 85 | , enable_cancel_resource 86 | { 87 | typedef shared_ptr ptr_t; 88 | 89 | public: 90 | static ptr_t create(io_service& io, endpoint const& local_bind, network::on_accept_f const& on_accept, network::on_error_f const& on_error) 91 | { 92 | ptr_t acceptor(new impl(ref(io), local_bind, boost::cref(on_accept), boost::cref(on_error))); 93 | acceptor->start_async_accept(); 94 | 95 | return acceptor; 96 | } 97 | 98 | private: 99 | impl(io_service& io, endpoint const& local_bind, network::on_accept_f const& on_accept, network::on_error_f const& on_error) 100 | 101 | : enable_cancel_resource(acceptor_) 102 | 103 | , acceptor_ (io, local_bind) 104 | , on_accept_(on_accept) 105 | , on_error_ (on_error ) 106 | { 107 | } 108 | 109 | void start_async_accept() 110 | { 111 | auto sock = make_shared(ref(acceptor_.get_io_service())); 112 | auto peer = make_shared(); 113 | 114 | acceptor_.async_accept(*sock, *peer, bind(&impl::on_accept, shared_from_this(), sock, peer, _1)); 115 | } 116 | 117 | void on_accept(shared_ptr socket, shared_ptr peer, error_code const& code) 118 | { 119 | if (cancelled()) 120 | return; 121 | 122 | if (code) 123 | { 124 | on_error_(code); 125 | return; 126 | } 127 | 128 | start_async_accept(); 129 | on_accept_(*socket, endpoint(peer->address().to_v4(), peer->port())); 130 | } 131 | 132 | private: 133 | tcp::acceptor acceptor_; 134 | network::on_accept_f on_accept_; 135 | network::on_error_f on_error_; 136 | }; 137 | 138 | 139 | //////////////////////////////////////////////////////////////////////// 140 | // async_acceptor 141 | async_acceptor::async_acceptor(io_service& io, endpoint const& local_bind, on_accept_f const& on_accept, on_error_f const& on_error) 142 | : acceptor_(impl::create(io, local_bind, on_accept, on_error)) 143 | { 144 | } 145 | 146 | async_acceptor::~async_acceptor() 147 | { 148 | } 149 | 150 | } // namespace network 151 | 152 | -------------------------------------------------------------------------------- /connecting.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | #include "net_common.h" 4 | #include "auto_cancel.h" 5 | 6 | namespace network 7 | { 8 | 9 | // can't use r-value ref on sock here, as it's not supported by boost::in_place and has problems with boost::function 10 | typedef function on_connected_f ; // moveable socket 11 | typedef function on_refused_f; 12 | typedef function on_accept_f ; // moveable socket 13 | 14 | struct async_connector 15 | { 16 | async_connector(io_service& io, endpoint const& remote_server, on_connected_f const&, on_refused_f const&, on_error_f const&); 17 | ~async_connector(); 18 | 19 | private: 20 | struct impl; 21 | auto_cancel_ptr pimpl_; 22 | }; 23 | 24 | struct async_acceptor 25 | : noncopyable 26 | { 27 | async_acceptor(io_service& io, endpoint const& local_bind, on_accept_f const& on_accept, on_error_f const& on_error); 28 | ~async_acceptor(); 29 | 30 | 31 | private: 32 | struct impl; 33 | auto_cancel_ptr acceptor_; 34 | }; 35 | 36 | } // namespace network 37 | -------------------------------------------------------------------------------- /cpp_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | -------------------------------------------------------------------------------- /icmp_header.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // From Boost samples (without modifications) 4 | // link: http://www.boost.org/doc/libs/1_48_0/doc/html/boost_asio/example/icmp/ipv4_header.hpp 5 | 6 | // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) 7 | // 8 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | // ICMP header for both IPv4 and IPv6. 16 | // 17 | // The wire format of an ICMP header is: 18 | // 19 | // 0 8 16 31 20 | // +---------------+---------------+------------------------------+ --- 21 | // | | | | ^ 22 | // | type | code | checksum | | 23 | // | | | | | 24 | // +---------------+---------------+------------------------------+ 8 bytes 25 | // | | | | 26 | // | identifier | sequence number | | 27 | // | | | v 28 | // +-------------------------------+------------------------------+ --- 29 | 30 | class icmp_header 31 | { 32 | public: 33 | enum { echo_reply = 0, destination_unreachable = 3, source_quench = 4, 34 | redirect = 5, echo_request = 8, time_exceeded = 11, parameter_problem = 12, 35 | timestamp_request = 13, timestamp_reply = 14, info_request = 15, 36 | info_reply = 16, address_request = 17, address_reply = 18 }; 37 | 38 | icmp_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); } 39 | 40 | unsigned char type() const { return rep_[0]; } 41 | unsigned char code() const { return rep_[1]; } 42 | unsigned short checksum() const { return decode(2, 3); } 43 | unsigned short identifier() const { return decode(4, 5); } 44 | unsigned short unique_number() const { return decode(6, 7); } 45 | 46 | void type(unsigned char n) { rep_[0] = n; } 47 | void code(unsigned char n) { rep_[1] = n; } 48 | void checksum(unsigned short n) { encode(2, 3, n); } 49 | void identifier(unsigned short n) { encode(4, 5, n); } 50 | void unique_number(unsigned short n) { encode(6, 7, n); } 51 | 52 | friend std::istream& operator>>(std::istream& is, icmp_header& header) 53 | { return is.read(reinterpret_cast(header.rep_), 8); } 54 | 55 | friend std::ostream& operator<<(std::ostream& os, const icmp_header& header) 56 | { return os.write(reinterpret_cast(header.rep_), 8); } 57 | 58 | private: 59 | unsigned short decode(int a, int b) const 60 | { return (rep_[a] << 8) + rep_[b]; } 61 | 62 | void encode(int a, int b, unsigned short n) 63 | { 64 | rep_[a] = static_cast(n >> 8); 65 | rep_[b] = static_cast(n & 0xFF); 66 | } 67 | 68 | unsigned char rep_[8]; 69 | }; 70 | 71 | template 72 | void compute_checksum(icmp_header& header, 73 | Iterator body_begin, Iterator body_end) 74 | { 75 | unsigned int sum = (header.type() << 8) + header.code() 76 | + header.identifier() + header.unique_number(); 77 | 78 | Iterator body_iter = body_begin; 79 | while (body_iter != body_end) 80 | { 81 | sum += (static_cast(*body_iter++) << 8); 82 | if (body_iter != body_end) 83 | sum += static_cast(*body_iter++); 84 | } 85 | 86 | sum = (sum >> 16) + (sum & 0xFFFF); 87 | sum += (sum >> 16); 88 | header.checksum(static_cast(~sum)); 89 | } 90 | -------------------------------------------------------------------------------- /ipv4_header.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // From Boost samples (without modifications) 4 | // link: http://www.boost.org/doc/libs/1_48_0/doc/html/boost_asio/example/icmp/icmp_header.hpp 5 | 6 | 7 | // 8 | // ipv4_header.hpp 9 | // ~~~~~~~~~~~~~~~ 10 | // 11 | // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) 12 | // 13 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 14 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 15 | // 16 | 17 | #include 18 | #include 19 | 20 | // Packet header for IPv4. 21 | // 22 | // The wire format of an IPv4 header is: 23 | // 24 | // 0 8 16 31 25 | // +-------+-------+---------------+------------------------------+ --- 26 | // | | | | | ^ 27 | // |version|header | type of | total length in bytes | | 28 | // | (4) | length| service | | | 29 | // +-------+-------+---------------+-+-+-+------------------------+ | 30 | // | | | | | | | 31 | // | identification |0|D|M| fragment offset | | 32 | // | | |F|F| | | 33 | // +---------------+---------------+-+-+-+------------------------+ | 34 | // | | | | | 35 | // | time to live | protocol | header checksum | 20 bytes 36 | // | | | | | 37 | // +---------------+---------------+------------------------------+ | 38 | // | | | 39 | // | source IPv4 address | | 40 | // | | | 41 | // +--------------------------------------------------------------+ | 42 | // | | | 43 | // | destination IPv4 address | | 44 | // | | v 45 | // +--------------------------------------------------------------+ --- 46 | // | | ^ 47 | // | | | 48 | // / options (if any) / 0 - 40 49 | // / / bytes 50 | // | | | 51 | // | | v 52 | // +--------------------------------------------------------------+ --- 53 | 54 | class ipv4_header 55 | { 56 | public: 57 | ipv4_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); } 58 | 59 | unsigned char version() const { return (rep_[0] >> 4) & 0xF; } 60 | unsigned short header_length() const { return (rep_[0] & 0xF) * 4; } 61 | unsigned char type_of_service() const { return rep_[1]; } 62 | unsigned short total_length() const { return decode(2, 3); } 63 | unsigned short identification() const { return decode(4, 5); } 64 | bool dont_fragment() const { return (rep_[6] & 0x40) != 0; } 65 | bool more_fragments() const { return (rep_[6] & 0x20) != 0; } 66 | unsigned short fragment_offset() const { return decode(6, 7) & 0x1FFF; } 67 | unsigned int time_to_live() const { return rep_[8]; } 68 | unsigned char protocol() const { return rep_[9]; } 69 | unsigned short header_checksum() const { return decode(10, 11); } 70 | 71 | boost::asio::ip::address_v4 source_address() const 72 | { 73 | boost::asio::ip::address_v4::bytes_type bytes 74 | = { { rep_[12], rep_[13], rep_[14], rep_[15] } }; 75 | return boost::asio::ip::address_v4(bytes); 76 | } 77 | 78 | boost::asio::ip::address_v4 destination_address() const 79 | { 80 | boost::asio::ip::address_v4::bytes_type bytes 81 | = { { rep_[16], rep_[17], rep_[18], rep_[19] } }; 82 | return boost::asio::ip::address_v4(bytes); 83 | } 84 | 85 | friend std::istream& operator>>(std::istream& is, ipv4_header& header) 86 | { 87 | is.read(reinterpret_cast(header.rep_), 20); 88 | if (header.version() != 4) 89 | is.setstate(std::ios::failbit); 90 | std::streamsize options_length = header.header_length() - 20; 91 | if (options_length < 0 || options_length > 40) 92 | is.setstate(std::ios::failbit); 93 | else 94 | is.read(reinterpret_cast(header.rep_) + 20, options_length); 95 | return is; 96 | } 97 | 98 | private: 99 | unsigned short decode(int a, int b) const 100 | { return (rep_[a] << 8) + rep_[b]; } 101 | 102 | unsigned char rep_[60]; 103 | }; 104 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "tunnel.h" 3 | #include "config_parser.h" 4 | 5 | int main(int argc, char** argv) 6 | { 7 | try 8 | { 9 | if (argc != 2) 10 | { 11 | cerr << "invalid config" << endl; 12 | cout << "usage: udp2tcp_tunnel " << endl; 13 | return 1; 14 | } 15 | 16 | tunnel tun; 17 | 18 | parse_config(argv[1], tun); 19 | tun.run(); 20 | 21 | return 0; 22 | } 23 | catch (cfg_error const& e) 24 | { 25 | cout << "invalid parametes: " << e.what() << endl; 26 | return 1; 27 | 28 | } 29 | catch (std::exception const& e) 30 | { 31 | cout << "c++ exception caught: " << e.what() << endl; 32 | return 1; 33 | } 34 | catch (...) 35 | { 36 | cout << "unknown eception caught" << endl; 37 | return 1; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /net_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace network 4 | { 5 | 6 | typedef function on_receive_f; 7 | typedef function on_error_f ; 8 | 9 | struct endpoint 10 | { 11 | endpoint() 12 | : addr(ip::address_v4::any()) 13 | , port(0) 14 | { 15 | } 16 | 17 | endpoint(string point) // format is "address:port" 18 | { 19 | vector tokens; 20 | split(tokens, point, is_any_of(":"), boost::token_compress_on); 21 | 22 | if (tokens.size() != 2) 23 | throw std::runtime_error("invalid IPv4 address format " + point); 24 | 25 | addr = ip::address_v4::from_string(tokens.front()); 26 | port = lexical_cast(tokens.back()); 27 | } 28 | 29 | endpoint(ip::address_v4 address, size_t port = 0) 30 | : addr (address) 31 | , port (port ) 32 | {} 33 | 34 | endpoint(const char* bytes) 35 | : addr (*reinterpret_cast(bytes)) 36 | , port (*reinterpret_cast(bytes + sizeof(uint32_t))) 37 | { 38 | } 39 | 40 | operator tcp::endpoint() const { return tcp::endpoint(addr, port); } 41 | operator udp::endpoint() const { return udp::endpoint(addr, port); } 42 | 43 | ///// 44 | static size_t serialize_size() 45 | { 46 | return sizeof(uint32_t) + sizeof(uint16_t); 47 | } 48 | 49 | template 50 | void serialize(iter it) const 51 | { 52 | const uint32_t saddr = static_cast(addr.to_ulong()); 53 | const uint16_t sport = static_cast(port); 54 | 55 | const char* addr_buf = reinterpret_cast(&saddr); 56 | const char* port_buf = reinterpret_cast(&sport); 57 | 58 | std::copy(addr_buf, addr_buf + sizeof(saddr), it); 59 | std::copy(port_buf, port_buf + sizeof(sport), it); 60 | } 61 | 62 | ip::address_v4 addr; 63 | size_t port; 64 | }; 65 | 66 | inline std::ostream& operator<<(std::ostream& o, endpoint const& point) 67 | { 68 | return o << point.addr.to_string() << ":" << point.port; 69 | } 70 | 71 | inline bool operator<(endpoint const& lhs, endpoint const& rhs) 72 | { 73 | return lhs.addr < rhs.addr || 74 | (lhs.addr == rhs.addr && 75 | lhs.port < rhs.port); 76 | } 77 | 78 | 79 | ///////////////////////////////////////////////////////////// 80 | 81 | } // namespace network 82 | -------------------------------------------------------------------------------- /network.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "net_common.h" 4 | #include "tcp_service.h" 5 | #include "connecting.h" 6 | #include "udp_service.h" 7 | #include "ping.h" 8 | -------------------------------------------------------------------------------- /ping.cpp: -------------------------------------------------------------------------------- 1 | // From Boost samples (with some modifications) 2 | // link: http://www.boost.org/doc/libs/1_48_0/doc/html/boost_asio/example/icmp/icmp_header.hpp 3 | 4 | 5 | // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #include "common.h" 12 | #include "ping.h" 13 | 14 | #include "asio_helper.h" 15 | 16 | #include "icmp_header.h" 17 | #include "ipv4_header.h" 18 | #include "auto_cancel.h" 19 | 20 | namespace network 21 | { 22 | 23 | using boost::asio::ip::icmp; 24 | using boost::asio::deadline_timer; 25 | 26 | struct async_pinger::impl 27 | : enable_shared_from_this 28 | , enable_cancel_resource 29 | { 30 | typedef 31 | shared_ptr 32 | ptr_t; 33 | 34 | static ptr_t create(io_service& io, string destination, posix_time::milliseconds const& timeout, network::on_pinged_f const& on_pinged) 35 | { 36 | ptr_t pinger(new impl(io, destination, timeout, on_pinged)); 37 | pinger->start_ping(); 38 | 39 | return pinger; 40 | } 41 | 42 | private: 43 | impl(io_service& io, string destination, posix_time::milliseconds const& timeout, network::on_pinged_f const& on_pinged) 44 | 45 | : enable_cancel_resource(socket_) 46 | 47 | , socket_ (io, icmp::v4()) 48 | , timer_ (io) 49 | , timeout_ (timeout) 50 | , on_pinged_(on_pinged) 51 | { 52 | icmp::resolver::query query(icmp::v4(), destination, ""); 53 | destination_ = *icmp::resolver(io).resolve(query); 54 | } 55 | 56 | void start_ping() 57 | { 58 | send_request (); 59 | start_receive(); 60 | } 61 | 62 | private: 63 | void on_send(size_t bytes_transferred) 64 | { 65 | assert(bytes_transferred == sizeof(icmp_header)); 66 | } 67 | 68 | void send_request() 69 | { 70 | time_sent_ = posix_time::microsec_clock::universal_time(); 71 | 72 | // Create an ICMP header for an echo request. 73 | icmp_header echo_request; 74 | 75 | echo_request.type(icmp_header::echo_request); 76 | echo_request.code(0); 77 | echo_request.identifier(get_identifier()); 78 | echo_request.unique_number((unsigned short)(time_sent_ - epoch_start_).total_milliseconds()); 79 | compute_checksum(echo_request, 0, 0); 80 | 81 | asio_helper::shared_const_buffer shared_buf(&echo_request, sizeof(echo_request)); 82 | socket_.async_send_to(shared_buf, destination_, bind(&impl::on_send, shared_from_this(), _2)); 83 | 84 | // Wait up to timeout seconds for a reply. 85 | timer_.expires_at(time_sent_ + timeout_); 86 | timer_.async_wait(boost::bind(&impl::on_timeout, shared_from_this(), _1)); 87 | } 88 | 89 | void on_timeout(error_code const& code) 90 | { 91 | if (!code) 92 | { 93 | on_pinged_(false, timeout_); 94 | socket_.cancel(); 95 | } 96 | } 97 | 98 | void start_receive() 99 | { 100 | // Discard any data already in the buffer. 101 | reply_buffer_.consume(reply_buffer_.size()); 102 | 103 | // Wait for a reply. We prepare the buffer to receive up to 64KB. 104 | socket_.async_receive(reply_buffer_.prepare(1 << 16), 105 | boost::bind(&impl::on_receive, shared_from_this(), _1, _2)); 106 | } 107 | 108 | void on_receive(error_code const& code, std::size_t length) 109 | { 110 | if (cancelled()) 111 | return; 112 | 113 | if (code) 114 | return; 115 | 116 | reply_buffer_.commit(length); 117 | 118 | std::istream is(&reply_buffer_); 119 | ipv4_header ipv4_hdr; 120 | icmp_header icmp_hdr; 121 | 122 | is >> ipv4_hdr >> icmp_hdr; 123 | 124 | // We can receive all ICMP packets received by the host, so we need to 125 | // filter out only the echo replies that match the our identifier and 126 | // expected sequence number. 127 | if (is && 128 | ipv4_hdr.source_address() == destination_.address().to_v4() && 129 | icmp_hdr.type() == icmp_header::echo_reply && 130 | icmp_hdr.identifier() == get_identifier() && 131 | icmp_hdr.unique_number() == (unsigned short)(time_sent_ - epoch_start_).total_milliseconds()) 132 | { 133 | timer_.cancel(); 134 | 135 | posix_time::ptime now = posix_time::microsec_clock::universal_time(); 136 | on_pinged_(true, posix_time::milliseconds((now - time_sent_).total_milliseconds())); 137 | } 138 | else 139 | start_receive(); 140 | } 141 | 142 | static unsigned short get_identifier() 143 | { 144 | #if defined(BOOST_WINDOWS) 145 | return static_cast(::GetCurrentProcessId()); 146 | #else 147 | return static_cast(::getpid()); 148 | #endif 149 | } 150 | 151 | private: 152 | icmp::endpoint destination_; 153 | icmp::socket socket_; 154 | deadline_timer timer_; 155 | 156 | posix_time::ptime time_sent_; 157 | boost::asio::streambuf reply_buffer_; 158 | 159 | posix_time::milliseconds timeout_; 160 | network::on_pinged_f on_pinged_; 161 | 162 | size_t unique_number_; 163 | 164 | private: 165 | static posix_time::ptime epoch_start_; 166 | }; 167 | 168 | /////////////////////////////////////// 169 | posix_time::ptime async_pinger::impl::epoch_start_(boost::gregorian::date(1970, 1, 1)); 170 | 171 | } // 'anonymous' 172 | 173 | 174 | 175 | 176 | 177 | 178 | /////////////////////////////////////////////////////////////////////////////////////// 179 | network::async_pinger::async_pinger(io_service& io, string destination, posix_time::milliseconds const& timeout, on_pinged_f const& on_pinged) 180 | : pimpl_(impl::ptr_t(impl::create(io, destination, timeout, on_pinged))) 181 | { 182 | } 183 | 184 | network::async_pinger::~async_pinger() 185 | { 186 | } 187 | 188 | 189 | -------------------------------------------------------------------------------- /ping.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "net_common.h" 3 | #include "auto_cancel.h" 4 | 5 | namespace network 6 | { 7 | typedef 8 | function 9 | on_pinged_f; 10 | 11 | struct async_pinger 12 | { 13 | async_pinger(io_service& io, string destination, posix_time::milliseconds const& timeout, on_pinged_f const& on_pinged); 14 | ~async_pinger(); 15 | 16 | private: 17 | struct impl; 18 | auto_cancel_ptr pimpl_; 19 | }; 20 | 21 | } // namespace network 22 | -------------------------------------------------------------------------------- /posix_stacktrace.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | inline void print_stacktrace() 5 | { 6 | std::cout << "=== Stack Trace ===" << std::endl; 7 | 8 | const size_t max_stack_size = 1000; 9 | 10 | void* stack_pointers[max_stack_size]; 11 | int count = backtrace(stack_pointers, max_stack_size); 12 | 13 | char** func_names = backtrace_symbols(stack_pointers, count); 14 | 15 | // Print the stack trace 16 | for (int i = 0; i < count; ++i) 17 | std::cerr << func_names[i] << std::endl; 18 | 19 | // Free the string pointers 20 | free(func_names); 21 | } 22 | -------------------------------------------------------------------------------- /server.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "network.h" 3 | #include "async_timer.h" 4 | 5 | struct server 6 | { 7 | server(io_service& io, size_t port) 8 | : acceptor_(in_place(ref(io), network::endpoint("", port), bind(&server::on_accepted, this, _1, _2), trace_error)) 9 | , udp_sock_( 10 | in_place(ref(io), 11 | none, 12 | network::endpoint( 13 | ip::address_v4::broadcast(), 14 | port + 1), 15 | bind(&server::on_receive, this, _1, _2)/*network::on_receive_f()*/, 16 | trace_error)) 17 | , timer_ (io, bind(&server::remove_waiting_socket, this)) 18 | { 19 | } 20 | 21 | void on_accepted(tcp::socket& moveable_sock, network::endpoint const&) 22 | { 23 | cout << "accepted some connection" << endl; 24 | 25 | tcp_sock_ = in_place( 26 | ref(moveable_sock), 27 | bind(&server::on_receive, this, _1, _2), 28 | bind(&server::on_disconnected, this), 29 | trace_error); 30 | 31 | acceptor_.reset(); 32 | timer_.wait(posix_time::milliseconds(7300)); 33 | } 34 | 35 | void on_receive(const void* data, size_t size) 36 | { 37 | if (size != sizeof(test_msg_data)) 38 | cout << "Expected size is: " << sizeof(test_msg_data) << "; received: " << size << endl; 39 | 40 | assert(size == sizeof(test_msg_data)); 41 | const test_msg_data* msg = reinterpret_cast(data); 42 | 43 | cout << "server has received: " << msg->counter << endl; 44 | udp_sock_->send(msg, size); 45 | 46 | // check for deleteting from callback 47 | if (msg->counter > 155) 48 | { 49 | // udp_sock_.reset(); 50 | // tcp_sock_.reset(); 51 | // timer_.cancel_one(); 52 | } 53 | } 54 | 55 | void on_disconnected() 56 | { 57 | tcp_sock_.reset(); 58 | udp_sock_.reset(); 59 | cout << "Ooops! Client, I've lost you!" << endl; 60 | } 61 | 62 | void remove_waiting_socket() 63 | { 64 | cout << "Arrivederci client" << endl; 65 | tcp_sock_.reset(); 66 | udp_sock_.reset(); 67 | } 68 | 69 | private: 70 | optional acceptor_; 71 | optional tcp_sock_; 72 | optional udp_sock_; 73 | async_timer timer_ ; 74 | }; 75 | 76 | void run_tcp_server(string /*server*/, size_t port) 77 | { 78 | io_service io; 79 | server s(io, port); 80 | 81 | io.run(); 82 | } 83 | -------------------------------------------------------------------------------- /tcp_service.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include "tcp_service.h" 4 | #include "asio_helper.h" 5 | #include "underlying_transport_impl.h" 6 | 7 | namespace 8 | { 9 | struct tcp_transfer_strategy 10 | { 11 | template 12 | void async_send(tcp::socket& sock, buffers_sequence& buf_seq, callback const& cb) 13 | { 14 | // NOTE: firstly, I (Valera) thought that sending all the outgoing buffer on 'ready send callback' is a good idea. I was wrong :) 15 | // It has turned out, that in Solaris it lead to CPU usage 100%, if we wait a little without ability to 16 | // send data (e.g. unplug cable for a while) and send after this pause all the previously collected buffer. 17 | // Sending message by message doesn't give such an effect. 18 | 19 | shared_buf_seq_ = shared_buf_seq_t(buf_seq.begin(), next(buf_seq.begin())); 20 | buf_seq.pop_front(); 21 | 22 | async_write(sock, shared_buf_seq_, cb); 23 | } 24 | 25 | template 26 | void async_recv(tcp::socket& sock, buffer const& buf, callback const& cb) const 27 | { 28 | sock.async_read_some(buf, cb); 29 | } 30 | 31 | private: 32 | typedef 33 | asio_helper::shared_const_buffers_seq 34 | shared_buf_seq_t; 35 | 36 | private: 37 | shared_buf_seq_t shared_buf_seq_; 38 | }; 39 | } // 'anonymous' 40 | 41 | 42 | 43 | namespace network 44 | { 45 | 46 | /////////////////////////////////////////////////////////////////////////// 47 | struct tcp_socket::impl 48 | { 49 | typedef 50 | underlying_transport_impl 51 | underlying_transport; 52 | 53 | typedef underlying_transport::ptr_t transport_ptr; 54 | 55 | impl(tcp::socket& moveable_sock, 56 | on_receive_f const& on_receive, 57 | on_disconnected_f const& on_discon, 58 | on_error_f const& on_error) 59 | 60 | : on_discon_(on_discon ) 61 | , on_error_ (on_error ) 62 | , transport_( 63 | transport_ptr( 64 | underlying_transport::create( 65 | move( 66 | adjusted_socket( 67 | moveable_sock)), 68 | on_receive, 69 | bind(&impl::on_error, this, _1), 70 | &tcp_transfer_strat_))) 71 | { 72 | } 73 | 74 | void send(const void* data, size_t size) 75 | { 76 | transport_->send(data, size); 77 | } 78 | 79 | private: 80 | 81 | tcp::socket& adjusted_socket(tcp::socket& sock) 82 | { 83 | sock.set_option(tcp::no_delay(true)); 84 | return sock; 85 | } 86 | 87 | void on_error(error_code const& code) 88 | { 89 | if (code) 90 | { 91 | if // usually, by on_receive 92 | ((code.category() == error::misc_category && code.value () == error::eof) || 93 | 94 | // and these - by on_send 95 | (code.category() == error::system_category && code.value () == error::connection_reset) || 96 | (code.category() == error::system_category && code.value () == error::broken_pipe )) 97 | { 98 | on_discon_(code); 99 | } 100 | else 101 | on_error_(code); 102 | } 103 | } 104 | 105 | // tcp transport 106 | private: 107 | on_disconnected_f on_discon_; 108 | on_error_f on_error_; 109 | 110 | private: 111 | tcp_transfer_strategy tcp_transfer_strat_; 112 | auto_cancel_ptr transport_; 113 | }; 114 | 115 | 116 | ////////////////////////////////////////////////////////////////////// 117 | tcp_socket::tcp_socket( 118 | tcp::socket& moveable_sock, 119 | on_receive_f const& on_receive, 120 | on_disconnected_f const& on_discon, 121 | on_error_f const& on_error) 122 | 123 | : pimpl_(new tcp_socket::impl(moveable_sock, on_receive, on_discon, on_error)) 124 | { 125 | } 126 | 127 | void tcp_socket::send(const void* data, size_t size) 128 | { 129 | pimpl_->send(data, size); 130 | } 131 | 132 | 133 | 134 | /////////////////////////////////////////////////////////////////////// 135 | // tcp_fragment_socket 136 | tcp_fragment_wrapper::tcp_fragment_wrapper( 137 | tcp::socket& moveable_sock, 138 | on_receive_f const& on_receive, 139 | on_disconnected_f const& on_discon, 140 | on_error_f const& on_error) 141 | 142 | : sock_ (moveable_sock, bind(&tcp_fragment_wrapper::on_receive, this, _1, _2), on_discon, on_error) 143 | , on_receive_ (on_receive) 144 | 145 | { 146 | const size_t reserve_msg_size = 1 << 16; 147 | partial_msg_.reserve(reserve_msg_size); 148 | } 149 | 150 | void tcp_fragment_wrapper::send(const void* data, size_t size) 151 | { 152 | size_hdr_t sz(size); 153 | 154 | sock_.send(&sz , sizeof(sz)); 155 | sock_.send(data, size); 156 | } 157 | 158 | void tcp_fragment_wrapper::on_receive(const void* data, size_t size) 159 | { 160 | while (size > 0) 161 | { 162 | bool reading_size = partial_msg_.size() < sizeof(size_hdr_t); 163 | 164 | if (reading_size) 165 | reading_size = !fill_buffer(sizeof(size_hdr_t) - partial_msg_.size(), data, size); 166 | 167 | if (!reading_size) 168 | { 169 | size_hdr_t sz = *reinterpret_cast(&partial_msg_.front()); 170 | 171 | if(fill_buffer(sizeof(size_hdr_t) + sz - partial_msg_.size(), data, size)) 172 | { 173 | on_receive_(&partial_msg_.front() + sizeof(size_hdr_t), sz); 174 | partial_msg_.clear(); 175 | } 176 | } 177 | } 178 | } 179 | 180 | bool tcp_fragment_wrapper::fill_buffer(size_t to_fill, const void*& data, size_t& received) 181 | { 182 | size_t offset = std::min(to_fill, received); 183 | 184 | const char* char_data = reinterpret_cast(data); 185 | partial_msg_.insert(partial_msg_.end(), char_data, char_data + offset); 186 | 187 | char_data += offset; 188 | received -= offset; 189 | 190 | data = char_data; 191 | 192 | return to_fill == offset; 193 | } 194 | 195 | } // namespace network 196 | -------------------------------------------------------------------------------- /tcp_service.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "net_common.h" 3 | 4 | namespace network 5 | { 6 | 7 | typedef function on_disconnected_f; 8 | 9 | ////////////////////////////////////////////////// 10 | // socket for sending and receiving streaming data 11 | struct tcp_socket 12 | : noncopyable 13 | { 14 | // can't use r-value ref, as it's not supported by boost::in_place and has problems with boost::function 15 | tcp_socket(tcp::socket& moveable_sock, on_receive_f const&, on_disconnected_f const&, on_error_f const&); 16 | 17 | void send(const void* data, size_t size); 18 | 19 | private: 20 | struct impl; 21 | shared_ptr pimpl_; 22 | }; 23 | 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | // wrapper for fragmented super-protocol (to break tcp data stream into messages) 27 | struct tcp_fragment_wrapper 28 | : noncopyable 29 | { 30 | tcp_fragment_wrapper(tcp::socket& moveable_sock, on_receive_f const&, on_disconnected_f const&, on_error_f const&); 31 | 32 | void send(const void* data, size_t size); 33 | 34 | private: 35 | typedef int32_t size_hdr_t; 36 | 37 | private: 38 | void on_receive(const void* data, size_t size); 39 | bool fill_buffer(size_t to_fill, const void*& data, size_t& received); 40 | 41 | private: 42 | tcp_socket sock_; 43 | 44 | // for fragment receiving 45 | private: 46 | on_receive_f on_receive_; 47 | vector partial_msg_; 48 | }; 49 | 50 | } // namespace network 51 | -------------------------------------------------------------------------------- /tunnel.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "tunnel.h" 3 | 4 | using namespace network; 5 | 6 | 7 | void tunnel::create_udp_receiver(network::endpoint const& local_bind, network::endpoint const& remote_server, string remote_tcp_point) 8 | { 9 | if (sender_ == nullptr) 10 | { 11 | cerr << local_bind << "udp listening point creating was skipped as no tcp client/server was mentioned before" << endl; 12 | return; 13 | } 14 | 15 | receivers_[local_bind] = make_shared(ref(io_), local_bind, remote_server, sender_, remote_tcp_point); 16 | } 17 | 18 | void tunnel::create_tcp_sender(bool client, network::endpoint const& point, posix_time::time_duration const& reconnect_timeout) 19 | { 20 | sender_ = client 21 | ? tcp_sender_ptr(make_shared(ref(io_), point, reconnect_timeout)) 22 | : tcp_sender_ptr(make_shared(ref(io_), point)); 23 | } 24 | 25 | void tunnel::run() 26 | { 27 | io_.run(); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /tunnel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "net_common.h" 3 | #include "tunnel_tcp_service.h" 4 | #include "tunnel_udp_service.h" 5 | 6 | struct tunnel 7 | { 8 | void create_udp_receiver(network::endpoint const& local_bind, network::endpoint const& remote_server, string remote_tcp_point = ""); 9 | void create_tcp_sender(bool client, network::endpoint const& point, posix_time::time_duration const& reconnect_timeout = posix_time::seconds(2)); 10 | 11 | void run(); 12 | 13 | private: 14 | typedef map receivers_t; 15 | 16 | private: 17 | io_service io_; 18 | 19 | private: 20 | receivers_t receivers_; 21 | tcp_sender_ptr sender_; 22 | }; 23 | -------------------------------------------------------------------------------- /tunnel_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "network.h" 4 | #include "async_timer.h" 5 | 6 | struct tcp_sender 7 | { 8 | virtual void send(const void* data, size_t size, string remote_tcp_server) = 0; 9 | virtual ~tcp_sender(){} 10 | }; 11 | typedef shared_ptr tcp_sender_ptr; 12 | -------------------------------------------------------------------------------- /tunnel_tcp_service.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "tunnel_tcp_service.h" 3 | 4 | using namespace network; 5 | 6 | tcp_client::tcp_client(io_service& io, endpoint const& remote_server, posix_time::time_duration const& reconnect_timeout) 7 | : udp_sender_ (io) 8 | , reconnect_timer_ (io, bind(&tcp_client::start_connect, this, ref(io), remote_server)) 9 | , reconnect_timeout_(reconnect_timeout) 10 | { 11 | start_connect(io, remote_server); 12 | } 13 | 14 | 15 | void tcp_client::send(const void* data, size_t size, string) 16 | { 17 | if (sock_) 18 | sock_->send(data, size); 19 | } 20 | 21 | void tcp_client::start_connect(io_service& io, endpoint const& remote_server) 22 | { 23 | std::stringstream sstrm; 24 | sstrm << "Connection refused for " << remote_server; 25 | 26 | connector_.reset(); 27 | connector_ = in_place(ref(io), remote_server, bind(&tcp_client::on_connected, this, _1, _2), bind(&tcp_client::on_disconnected, this, sstrm.str(), _1), error_tracer("client connector")); 28 | } 29 | 30 | void tcp_client::on_receive(const void* data, size_t size) 31 | { 32 | udp_sender_.send(data, size); 33 | } 34 | 35 | void tcp_client::on_disconnected(string reason, error_code const& code) 36 | { 37 | sock_.reset(); 38 | cout << reason << " Error: " << code.category().name() << ":" << code.message() << endl; 39 | 40 | reconnect_timer_.wait(reconnect_timeout_); 41 | } 42 | 43 | void tcp_client::on_connected(tcp::socket& sock, endpoint const& remote_peer) 44 | { 45 | cout << "Connection established with " << remote_peer << endl; 46 | 47 | std::stringstream sstrm; 48 | sstrm << "Connection lost with " << remote_peer; 49 | 50 | sock_ = in_place(ref(sock), bind(&tcp_client::on_receive, this, _1, _2), bind(&tcp_client::on_disconnected, this, sstrm.str(), _1), error_tracer("client socket")); 51 | } 52 | 53 | 54 | ////////////////////////////////////////////////////////////////////// 55 | tcp_server::tcp_server(io_service& io, endpoint const& local_bind) 56 | : udp_sender_(io) 57 | , acceptor_ (io, local_bind, bind(&tcp_server::on_accept, this, _1, _2), error_tracer("server acceptor")) 58 | { 59 | } 60 | 61 | void tcp_server::send(const void* data, size_t size, string client) 62 | { 63 | if (client.empty()) 64 | { 65 | for (auto it = clients_.begin(); it != clients_.end(); ++it) 66 | it->second->send(data, size); 67 | } 68 | else 69 | { 70 | clients_t::iterator it = clients_.find(client); 71 | 72 | if (it != clients_.end()) 73 | it->second->send(data, size); 74 | } 75 | } 76 | 77 | void tcp_server::on_accept(tcp::socket& sock, endpoint const& remote_peer) 78 | { 79 | cout << "Connection with " << remote_peer.addr << " is established" << endl; 80 | 81 | clients_[remote_peer.addr.to_string()] = 82 | make_shared( 83 | ref (sock), 84 | bind(&tcp_server::on_receive , this, _1, _2), 85 | bind(&tcp_server::on_disconnected, this, remote_peer.addr.to_string(), _1), 86 | error_tracer("tcp_client")); 87 | } 88 | 89 | void tcp_server::on_receive(const void* data, size_t size) 90 | { 91 | udp_sender_.send(data, size); 92 | } 93 | 94 | void tcp_server::on_disconnected(string client, error_code const& code) 95 | { 96 | clients_.erase(client); 97 | cout << "Connection lost with " << client << " Error: " << code.category().name() << ":" << code.message() << endl; 98 | } 99 | -------------------------------------------------------------------------------- /tunnel_tcp_service.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "tunnel_common.h" 3 | #include "tunnel_udp_service.h" 4 | #include "tcp_service.h" 5 | 6 | struct tcp_client 7 | : tcp_sender 8 | { 9 | tcp_client(io_service& io, network::endpoint const& remote_server, posix_time::time_duration const& reconnect_timeout = posix_time::seconds(2)); 10 | void send(const void* data, size_t size, string); 11 | 12 | private: 13 | 14 | void start_connect(io_service& io, network::endpoint const& remote_server); 15 | 16 | void on_receive (const void* data, size_t size); 17 | void on_disconnected(string reason, error_code const&); 18 | void on_connected (tcp::socket& sock, network::endpoint const& remote_peer); 19 | 20 | private: 21 | optional sock_; 22 | optional connector_; 23 | 24 | private: 25 | udp_sender udp_sender_; 26 | 27 | private: 28 | async_timer reconnect_timer_; 29 | posix_time::time_duration reconnect_timeout_; 30 | }; 31 | 32 | 33 | struct tcp_server 34 | : tcp_sender 35 | { 36 | tcp_server(io_service& io, network::endpoint const& local_bind); 37 | void send(const void* data, size_t size, string client); 38 | 39 | private: 40 | void on_accept (tcp::socket& sock, network::endpoint const& remote_peer); 41 | void on_receive (const void* data, size_t size); 42 | void on_disconnected(string client, error_code const&); 43 | 44 | private: 45 | udp_sender udp_sender_; 46 | 47 | private: 48 | network::async_acceptor acceptor_; 49 | 50 | private: 51 | typedef 52 | map > 53 | clients_t; 54 | 55 | private: 56 | clients_t clients_; 57 | }; 58 | -------------------------------------------------------------------------------- /tunnel_udp_service.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "tunnel_udp_service.h" 3 | 4 | using namespace network; 5 | 6 | namespace 7 | { 8 | 9 | vector unite_with_endpoint(const void* data, size_t size, endpoint const& point) 10 | { 11 | vector buf; 12 | buf.reserve(size + endpoint::serialize_size()); 13 | 14 | point.serialize(back_inserter(buf)); 15 | 16 | char const* bytes = reinterpret_cast(data); 17 | copy(bytes, bytes + size, back_inserter(buf)); 18 | 19 | return buf; 20 | } 21 | 22 | } // 'anonymous' 23 | 24 | ////////////////////////////////////////////////////////////// 25 | udp_sender::udp_sender(io_service& io) 26 | : sock_ (io, none, none, on_receive_f(0), error_tracer("udp_sender")) 27 | { 28 | } 29 | 30 | void udp_sender::send(const void* data, size_t size) 31 | { 32 | size_t hdr_size = endpoint::serialize_size(); 33 | assert(size >= hdr_size); 34 | 35 | const char* bytes = reinterpret_cast(data); 36 | sock_.send(bytes + hdr_size, size - hdr_size, endpoint(bytes)); 37 | } 38 | 39 | 40 | ////////////////////////////////////////////////////////////// 41 | udp_receiver::udp_receiver(io_service& io, endpoint const& local_bind, endpoint const& remote_server, tcp_sender_ptr sndr, string remote_tcp_point) 42 | : sock_ (io, local_bind, none, bind(&udp_receiver::on_receive, this, _1, _2), error_tracer("udp_receiver")) 43 | , sender_ (sndr) 44 | , remote_server_ (remote_server) 45 | , remote_tcp_point_ (remote_tcp_point) 46 | { 47 | } 48 | 49 | void udp_receiver::on_receive(const void* data, size_t size) 50 | { 51 | vector buf = unite_with_endpoint(data, size, remote_server_); 52 | sender_->send(&buf[0], buf.size(), remote_tcp_point_); 53 | } 54 | -------------------------------------------------------------------------------- /tunnel_udp_service.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "tunnel_common.h" 3 | #include "udp_service.h" 4 | 5 | struct udp_sender 6 | : noncopyable 7 | { 8 | udp_sender(io_service& io); 9 | void send(const void* data, size_t size); 10 | 11 | private: 12 | network::udp_socket sock_; 13 | }; 14 | 15 | struct udp_receiver 16 | : noncopyable 17 | { 18 | udp_receiver(io_service& io, network::endpoint const& local_bind, network::endpoint const& remote_server, tcp_sender_ptr sndr, string remote_tcp_point = ""); 19 | 20 | private: 21 | void on_receive(const void* data, size_t size); 22 | 23 | private: 24 | network::udp_socket sock_; 25 | tcp_sender_ptr sender_; 26 | network::endpoint remote_server_; 27 | string remote_tcp_point_; 28 | }; 29 | 30 | typedef shared_ptr udp_sender_ptr; 31 | typedef shared_ptr udp_receiver_ptr; 32 | -------------------------------------------------------------------------------- /udp2tcp_tunnel.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | message($$CONFIG) 4 | 5 | BIN_PATH = ../bin 6 | 7 | #CONFIG(debug) : BUILD_PATH = $$join(BIN_PATH,,,"/debug") 8 | #CONFIG(release) : BUILD_PATH = $$join(BIN_PATH,,,"/release") 9 | 10 | debug{ 11 | BUILD_PATH = $$join(BIN_PATH,,,"/debug") 12 | message("Debug") 13 | } 14 | 15 | release{ 16 | BUILD_PATH = $$join(BIN_PATH,,,"/release") 17 | message("Release") 18 | } 19 | 20 | DESTDIR = $$BUILD_PATH 21 | OBJECTS_DIR = $$BUILD_PATH/misc 22 | MOC_DIR = $$BUILD_PATH/misc 23 | UI_DIR = $$BUILD_PATH/misc 24 | RCC_DIR = $$BUILD_PATH/misc 25 | 26 | CONFIG += console 27 | CONFIG -= qt 28 | 29 | LIBS += -lboost_system -lpthread -lboost_thread 30 | QMAKE_CXXFLAGS += -std=c++0x -g3 31 | QMAKE_LFLAGS += -static -static-libgcc -static-libstdc++ 32 | 33 | SOURCES += main.cpp \ 34 | connecting.cpp \ 35 | tcp_service.cpp \ 36 | udp_service.cpp \ 37 | #client.cpp \ 38 | #server.cpp \ 39 | ping.cpp \ 40 | async_timer.cpp \ 41 | tunnel_udp_service.cpp \ 42 | tunnel_tcp_service.cpp \ 43 | tunnel.cpp \ 44 | config_parser.cpp 45 | 46 | HEADERS += \ 47 | common.h \ 48 | tcp_service.h \ 49 | asio_helper.h \ 50 | connecting.h \ 51 | cpp_utils.h \ 52 | underlying_transport_impl.h \ 53 | net_common.h \ 54 | udp_service.h \ 55 | ipv4_header.h \ 56 | icmp_header.h \ 57 | ping.h \ 58 | network.h \ 59 | async_timer.h \ 60 | auto_cancel.h \ 61 | tunnel_tcp_service.h \ 62 | tunnel_udp_service.h \ 63 | tunnel_common.h \ 64 | tunnel.h \ 65 | config_parser.h 66 | 67 | -------------------------------------------------------------------------------- /udp_service.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include "asio_helper.h" 4 | #include "net_common.h" 5 | #include "udp_service.h" 6 | #include "underlying_transport_impl.h" 7 | 8 | #include "auto_cancel.h" 9 | 10 | namespace 11 | { 12 | udp::socket connected_udp_socket( 13 | io_service& io, 14 | optional const& local_bind) 15 | { 16 | udp::socket sock(io); 17 | sock.open(udp::v4()); 18 | 19 | sock.set_option(udp::socket::reuse_address(true)); 20 | sock.set_option(udp::socket::broadcast (true)); 21 | 22 | if (local_bind) 23 | sock.bind(udp::endpoint(*local_bind)); 24 | 25 | return sock; 26 | } 27 | 28 | /////////////////////////////////////////////////////////////////////////// 29 | struct udp_transfer_strategy 30 | { 31 | udp_transfer_strategy(udp::endpoint const& remote_peer) 32 | : remote_peer_(remote_peer) 33 | { 34 | } 35 | 36 | template 37 | void async_send(udp::socket& sock, buffers_sequence& buf_seq, callback const& cb) const 38 | { 39 | typename buffers_sequence::value_type msg = buf_seq.front(); 40 | buf_seq.pop_front(); 41 | 42 | sock.async_send_to(msg, msg.dest() ? *msg.dest() : remote_peer_, cb); 43 | } 44 | 45 | template 46 | void async_recv(udp::socket& sock, buffer const& buf, callback const& cb) const 47 | { 48 | sock.async_receive(buf, cb); 49 | } 50 | 51 | private: 52 | udp::endpoint remote_peer_; 53 | }; 54 | 55 | //template 56 | //struct scope_swap 57 | //{ 58 | // scope_swap(T* resource, optional new_value) 59 | // : resource_ (*resource) 60 | // , old_value_(*resource) 61 | // , swap_ (new_value) 62 | // { 63 | // if (swap_) 64 | // resource_ = *new_value; 65 | // } 66 | 67 | // ~scope_swap() 68 | // { 69 | // if (swap_) 70 | // resource_ = old_value_; 71 | // } 72 | 73 | //private: 74 | // T& resource_; 75 | // T old_value_; 76 | // bool swap_; 77 | //}; 78 | 79 | } // 'anonymous' 80 | 81 | 82 | namespace network 83 | { 84 | 85 | /////////////////////////////////////////////////////////////////////////// 86 | struct udp_socket::impl 87 | { 88 | typedef 89 | underlying_transport_impl 90 | underlying_transport; 91 | 92 | impl( 93 | io_service& io, 94 | optional const& local_bind, 95 | optional const& remote_server, 96 | on_receive_f const& on_receive, 97 | on_error_f const& on_error) 98 | 99 | : strat_ (remote_server ? *remote_server : endpoint()) 100 | , transport_( 101 | underlying_transport::ptr_t( 102 | underlying_transport::create( 103 | move(connected_udp_socket(io, local_bind)), 104 | on_receive, 105 | on_error, 106 | &strat_))) 107 | { 108 | } 109 | 110 | void send(const void* data, size_t size, optional const& dest) 111 | { 112 | transport_->send(data, size, dest); 113 | } 114 | 115 | private: 116 | udp_transfer_strategy strat_; 117 | auto_cancel_ptr transport_; 118 | }; 119 | 120 | 121 | /////////////////////////////////////////////////////////////////////////// 122 | udp_socket::udp_socket( 123 | io_service& io, 124 | optional const& local_bind, 125 | optional const& remote_server, 126 | on_receive_f const& on_receive, 127 | on_error_f const& on_error) 128 | 129 | : pimpl_(new udp_socket::impl(io, local_bind, remote_server, on_receive, on_error)) 130 | { 131 | } 132 | 133 | void udp_socket::send(const void* data, size_t size, optional const& destination) 134 | { 135 | pimpl_->send(data, size, destination); 136 | } 137 | 138 | } // namespace network 139 | -------------------------------------------------------------------------------- /udp_service.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "net_common.h" 3 | 4 | namespace network 5 | { 6 | 7 | struct udp_socket 8 | { 9 | udp_socket( 10 | io_service& io, 11 | optional const& local_bind, 12 | optional const& remote_server, 13 | on_receive_f const&, 14 | on_error_f const&); 15 | 16 | void send(const void* data, size_t size, optional const& destination = none); 17 | 18 | private: 19 | struct impl; 20 | shared_ptr pimpl_; 21 | }; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /underlying_transport_impl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "net_common.h" 3 | #include "asio_helper.h" 4 | #include "auto_cancel.h" 5 | 6 | namespace network 7 | { 8 | 9 | //////////////////////////////////////////////////////////////////////////////////// 10 | // underlying_transport for socket 11 | template 12 | struct underlying_transport_impl 13 | : noncopyable 14 | , enable_cancel_resource 15 | , enable_shared_from_this 16 | > 18 | { 19 | typedef underlying_transport_impl this_t; 20 | typedef shared_ptr ptr_t; 21 | typedef async_transfer_strategy strat_t; 22 | typedef typename protocol::socket socket_t; 23 | 24 | static ptr_t create(socket_t&& sock, on_receive_f const& on_receive, on_error_f const& on_error, strat_t* strat) 25 | { 26 | const size_t buf_size = 64 * 1024; 27 | sock.set_option(typename protocol::socket::send_buffer_size (buf_size)); 28 | sock.set_option(typename protocol::socket::receive_buffer_size(buf_size)); 29 | 30 | ptr_t ptr(new this_t(forward(sock), on_receive, on_error, strat)); 31 | strat->async_recv(ptr->sock_, buffer(ptr->buf_), bind(&this_t::on_receive, ptr, _1, _2)); 32 | 33 | return ptr; 34 | } 35 | 36 | void send(const void *data, size_t size, optional const& dest = none) 37 | { 38 | asio_helper::shared_const_buffer msg( 39 | static_cast(data), 40 | static_cast(data) + size, 41 | dest); 42 | 43 | bool ready_send_now = (msgs_.empty() && ready_send_); 44 | msgs_.push_back(msg); 45 | 46 | if (ready_send_now) 47 | { 48 | strat_.async_send( 49 | sock_, 50 | msgs_, 51 | bind(&this_t::on_send, this->shared_from_this(), _1, _2)); 52 | 53 | ready_send_ = false; 54 | } 55 | } 56 | 57 | private: 58 | underlying_transport_impl(socket_t&& sock, on_receive_f const& on_receive, on_error_f const& on_error, strat_t* strat) 59 | 60 | : enable_cancel_resource(sock_) 61 | 62 | , sock_ (forward(sock)) 63 | , strat_ (*strat) 64 | , ready_send_ (true) 65 | , on_receive_ (on_receive) 66 | , on_error_ (on_error) 67 | { 68 | } 69 | 70 | 71 | private: 72 | void on_send(const error_code& code, size_t) 73 | { 74 | if (cancelled()) 75 | return; 76 | 77 | if (code) 78 | { 79 | on_error_(code); 80 | return; 81 | } 82 | 83 | if (!msgs_.empty()) 84 | { 85 | strat_.async_send( 86 | sock_, 87 | msgs_, 88 | bind(&this_t::on_send, this->shared_from_this(), _1, _2)); 89 | } 90 | else 91 | ready_send_ = true; 92 | } 93 | 94 | private: 95 | void on_receive(const error_code& code, size_t bytes_transferred) 96 | { 97 | if (cancelled()) 98 | return; 99 | 100 | if (code) 101 | { 102 | if (on_error_) 103 | on_error_(code); 104 | 105 | return; 106 | } 107 | 108 | if (on_receive_) 109 | on_receive_(&buf_[0], bytes_transferred); 110 | 111 | strat_.async_recv(sock_, buffer(buf_), bind(&this_t::on_receive, this->shared_from_this(), _1, _2)); 112 | } 113 | 114 | private: 115 | socket_t sock_ ; 116 | 117 | private: 118 | strat_t& strat_; 119 | 120 | private: 121 | typedef 122 | list 123 | msg_list_t; 124 | 125 | private: 126 | bool ready_send_; 127 | msg_list_t msgs_; 128 | 129 | // for receiving 130 | private: 131 | static const size_t buf_size_ = 256 * 1024; 132 | 133 | on_receive_f on_receive_; 134 | std::array buf_; 135 | 136 | private: 137 | on_error_f on_error_; 138 | }; 139 | 140 | } // namespace network 141 | 142 | --------------------------------------------------------------------------------