diff options
Diffstat (limited to 'contrib/epee')
18 files changed, 451 insertions, 147 deletions
diff --git a/contrib/epee/include/copyable_atomic.h b/contrib/epee/include/copyable_atomic.h index 410b4b4ff..00a5f484b 100644 --- a/contrib/epee/include/copyable_atomic.h +++ b/contrib/epee/include/copyable_atomic.h @@ -35,6 +35,8 @@ namespace epee public: copyable_atomic() {}; + copyable_atomic(uint32_t value) + { store(value); } copyable_atomic(const copyable_atomic& a):std::atomic<uint32_t>(a.load()) {} copyable_atomic& operator= (const copyable_atomic& a) diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h index 982aaea06..7ac07d112 100644 --- a/contrib/epee/include/misc_log_ex.h +++ b/contrib/epee/include/misc_log_ex.h @@ -105,9 +105,9 @@ #define _dbg2(x) MDEBUG(x) #define _dbg1(x) MDEBUG(x) #define _info(x) MINFO(x) -#define _note(x) MINFO(x) -#define _fact(x) MINFO(x) -#define _mark(x) MINFO(x) +#define _note(x) MDEBUG(x) +#define _fact(x) MDEBUG(x) +#define _mark(x) MDEBUG(x) #define _warn(x) MWARNING(x) #define _erro(x) MERROR(x) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 76988a26e..00d03567c 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -80,7 +80,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) m_throttle_speed_in("speed_in", "throttle_speed_in"), m_throttle_speed_out("speed_out", "throttle_speed_out") { - MINFO("test, connection constructor set m_connection_type="<<m_connection_type); + MDEBUG("test, connection constructor set m_connection_type="<<m_connection_type); } PRAGMA_WARNING_DISABLE_VS(4355) //--------------------------------------------------------------------------------- @@ -137,14 +137,14 @@ PRAGMA_WARNING_DISABLE_VS(4355) CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get local endpoint: " << ec.message() << ':' << ec.value()); context = boost::value_initialized<t_connection_context>(); - long ip_ = boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong()); + const unsigned long ip_{boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong())}; // create a random uuid boost::uuids::uuid random_uuid; // that stuff turns out to be included, even though it's from src... Taking advantage random_uuid = crypto::rand<boost::uuids::uuid>(); - context.set_details(random_uuid, new epee::net_utils::ipv4_network_address(ip_, remote_ep.port()), is_income); + context.set_details(random_uuid, epee::net_utils::ipv4_network_address(ip_, remote_ep.port()), is_income); _dbg3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) << " to " << local_ep.address().to_string() << ':' << local_ep.port() << ", total sockets objects " << m_ref_sock_count); @@ -264,7 +264,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) address = endpoint.address().to_string(); port = boost::lexical_cast<std::string>(endpoint.port()); } - MINFO(" connection type " << to_string( m_connection_type ) << " " + MDEBUG(" connection type " << to_string( m_connection_type ) << " " << socket_.local_endpoint().address().to_string() << ":" << socket_.local_endpoint().port() << " <--> " << address << ":" << port); } diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h index 144acad9d..a66fb7c23 100644..100755 --- a/contrib/epee/include/net/http_base.h +++ b/contrib/epee/include/net/http_base.h @@ -46,6 +46,7 @@ namespace net_utils { enum http_method{ + http_method_options, http_method_get, http_method_post, http_method_put, @@ -115,6 +116,7 @@ namespace net_utils std::string m_host; //"Host:" std::string m_cookie; //"Cookie:" std::string m_user_agent; //"User-Agent:" + std::string m_origin; //"Origin:" fields_list m_etc_fields; void clear() @@ -128,6 +130,7 @@ namespace net_utils m_host.clear(); m_cookie.clear(); m_user_agent.clear(); + m_origin.clear(); m_etc_fields.clear(); } }; diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index 8e099e2bc..80a4504e3 100644..100755 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -274,6 +274,7 @@ using namespace std; chunked_state m_chunked_state; std::string m_chunked_cache; critical_section m_lock; + bool m_ssl; public: explicit http_simple_client() @@ -291,33 +292,35 @@ using namespace std; , m_chunked_state() , m_chunked_cache() , m_lock() + , m_ssl(false) {} const std::string &get_host() const { return m_host_buff; }; const std::string &get_port() const { return m_port; }; - bool set_server(const std::string& address, boost::optional<login> user) + bool set_server(const std::string& address, boost::optional<login> user, bool ssl = false) { http::url_content parsed{}; const bool r = parse_url(address, parsed); CHECK_AND_ASSERT_MES(r, false, "failed to parse url: " << address); - set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user)); + set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user), ssl); return true; } - void set_server(std::string host, std::string port, boost::optional<login> user) + void set_server(std::string host, std::string port, boost::optional<login> user, bool ssl = false) { CRITICAL_REGION_LOCAL(m_lock); disconnect(); m_host_buff = std::move(host); m_port = std::move(port); m_auth = user ? http_client_auth{std::move(*user)} : http_client_auth{}; + m_ssl = ssl; } bool connect(std::chrono::milliseconds timeout) { CRITICAL_REGION_LOCAL(m_lock); - return m_net_client.connect(m_host_buff, m_port, timeout); + return m_net_client.connect(m_host_buff, m_port, timeout, m_ssl); } //--------------------------------------------------------------------------- bool disconnect() @@ -392,7 +395,6 @@ using namespace std; res = m_net_client.send(body, timeout); CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND"); - m_response_info.clear(); m_state = reciev_machine_state_header; if (!handle_reciev(timeout)) @@ -747,10 +749,10 @@ using namespace std; MTRACE("http_stream_filter::parse_cached_header(*)"); STATIC_REGEXP_EXPR_1(rexp_mach_field, - "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)" - // 12 3 4 5 6 7 8 9 10 + "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)|(Origin)" + // 12 3 4 5 6 7 8 9 10 11 "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", - //11 1213 14 + //12 13 14 15 boost::regex::icase | boost::regex::normal); boost::smatch result; @@ -762,7 +764,7 @@ using namespace std; //lookup all fields and fill well-known fields while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) { - const size_t field_val = 13; + const size_t field_val = 14; //const size_t field_etc_name = 11; int i = 2; //start position = 2 @@ -786,8 +788,10 @@ using namespace std; body_info.m_cookie = result[field_val]; else if(result[i++].matched)//"User-Agent" body_info.m_user_agent = result[field_val]; + else if(result[i++].matched)//"Origin" + body_info.m_origin = result[field_val]; else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) - body_info.m_etc_fields.emplace_back(result[11], result[field_val]); + body_info.m_etc_fields.emplace_back(result[12], result[field_val]); else {CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:"<<m_cache_to_process);} diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h index babe49ad7..652d8ff6f 100644..100755 --- a/contrib/epee/include/net/http_protocol_handler.h +++ b/contrib/epee/include/net/http_protocol_handler.h @@ -54,6 +54,7 @@ namespace net_utils struct http_server_config { std::string m_folder; + std::vector<std::string> m_access_control_origins; boost::optional<login> m_user; critical_section m_lock; }; @@ -193,6 +194,7 @@ namespace net_utils response.m_response_code = 200; response.m_response_comment = "OK"; response.m_body.clear(); + return m_config.m_phandler->handle_http_request(query_info, response, m_conn_context); } diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index c92a13bcc..c3350bf73 100644..100755 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -316,7 +316,10 @@ namespace net_utils CHECK_AND_ASSERT_MES(result[0].matched, false, "simple_http_connection_handler::analize_http_method() assert failed..."); http_ver_major = boost::lexical_cast<int>(result[11]); http_ver_minor = boost::lexical_cast<int>(result[12]); - if(result[4].matched) + + if(result[3].matched) + method = http::http_method_options; + else if(result[4].matched) method = http::http_method_get; else if(result[5].matched) method = http::http_method_head; @@ -472,8 +475,8 @@ namespace net_utils bool simple_http_connection_handler<t_connection_context>::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos) { STATIC_REGEXP_EXPR_1(rexp_mach_field, - "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)" - // 12 3 4 5 6 7 8 9 10 + "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)|(Origin)" + // 12 3 4 5 6 7 8 9 10 11 "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", //11 1213 14 boost::regex::icase | boost::regex::normal); @@ -487,8 +490,8 @@ namespace net_utils //lookup all fields and fill well-known fields while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) { - const size_t field_val = 13; - const size_t field_etc_name = 11; + const size_t field_val = 14; + const size_t field_etc_name = 12; int i = 2; //start position = 2 if(result[i++].matched)//"Connection" @@ -509,6 +512,8 @@ namespace net_utils body_info.m_cookie = result[field_val]; else if(result[i++].matched)//"User-Agent" body_info.m_user_agent = result[field_val]; + else if(result[i++].matched)//"Origin" + body_info.m_origin = result[field_val]; else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) body_info.m_etc_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val])); else @@ -537,17 +542,27 @@ namespace net_utils template<class t_connection_context> bool simple_http_connection_handler<t_connection_context>::handle_request_and_send_response(const http::http_request_info& query_info) { - http_response_info response; - bool res = handle_request(query_info, response); + http_response_info response{}; //CHECK_AND_ASSERT_MES(res, res, "handle_request(query_info, response) returned false" ); + bool res = true; + + if (query_info.m_http_method != http::http_method_options) + { + res = handle_request(query_info, response); + } + else + { + response.m_response_code = 200; + response.m_response_comment = "OK"; + } std::string response_data = get_response_header(response); - //LOG_PRINT_L0("HTTP_SEND: << \r\n" << response_data + response.m_body); + LOG_PRINT_L3("HTTP_RESPONSE_HEAD: << \r\n" << response_data); m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size()); - if(response.m_body.size() && (query_info.m_http_method != http::http_method_head)) + if ((response.m_body.size() && (query_info.m_http_method != http::http_method_head)) || (query_info.m_http_method == http::http_method_options)) m_psnd_hndlr->do_send((void*)response.m_body.data(), response.m_body.size()); return res; } @@ -579,7 +594,6 @@ namespace net_utils response.m_response_comment = "OK"; response.m_mime_tipe = get_file_mime_tipe(uri_to_path); - return true; } //----------------------------------------------------------------------------------- @@ -591,8 +605,12 @@ namespace net_utils "Server: Epee-based\r\n" "Content-Length: "; buf += boost::lexical_cast<std::string>(response.m_body.size()) + "\r\n"; - buf += "Content-Type: "; - buf += response.m_mime_tipe + "\r\n"; + + if(!response.m_mime_tipe.empty()) + { + buf += "Content-Type: "; + buf += response.m_mime_tipe + "\r\n"; + } buf += "Last-Modified: "; time_t tm; @@ -612,6 +630,19 @@ namespace net_utils m_want_close = true; } } + + // Cross-origin resource sharing + if(m_query_info.m_header_info.m_origin.size()) + { + if (std::binary_search(m_config.m_access_control_origins.begin(), m_config.m_access_control_origins.end(), m_query_info.m_header_info.m_origin)) + { + buf += "Access-Control-Allow-Origin: "; + buf += m_query_info.m_header_info.m_origin; + buf += "\r\n"; + buf += "Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS\r\n"; + } + } + //add additional fields, if it is for(fields_list::const_iterator it = response.m_additional_fields.begin(); it!=response.m_additional_fields.end(); it++) buf += it->first + ":" + it->second + "\r\n"; diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 429e3e1af..429e3e1af 100644..100755 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h index acecbb2d4..0788c6a4b 100644..100755 --- a/contrib/epee/include/net/http_server_impl_base.h +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -56,6 +56,7 @@ namespace epee {} bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0", + std::vector<std::string> access_control_origins = std::vector<std::string>(), boost::optional<net_utils::http::login> user = boost::none) { @@ -65,6 +66,10 @@ namespace epee //here set folder for hosting reqests m_net_server.get_config_object().m_folder = ""; + //set access control allow origins if configured + std::sort(access_control_origins.begin(), access_control_origins.end()); + m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins); + m_net_server.get_config_object().m_user = std::move(user); MGINFO("Binding on " << bind_ip << ":" << bind_port); diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index 1d808cc4c..ee0e13fc1 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -36,7 +36,9 @@ #include <istream> #include <ostream> #include <string> +#include <boost/version.hpp> #include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> #include <boost/asio/steady_timer.hpp> #include <boost/preprocessor/selection/min.hpp> #include <boost/lambda/bind.hpp> @@ -85,11 +87,13 @@ namespace net_utils public: inline - blocked_mode_client():m_socket(m_io_service), - m_initialized(false), + blocked_mode_client():m_initialized(false), m_connected(false), m_deadline(m_io_service), - m_shutdowned(0) + m_shutdowned(0), + m_ssl(false), + m_ctx(boost::asio::ssl::context::sslv23), + m_ssl_socket(m_io_service,m_ctx) { @@ -113,18 +117,25 @@ namespace net_utils } inline - bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout, const std::string& bind_ip = "0.0.0.0") + bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout, bool ssl = false, const std::string& bind_ip = "0.0.0.0") { - return connect(addr, std::to_string(port), timeout, bind_ip); + return connect(addr, std::to_string(port), timeout, ssl, bind_ip); } inline - bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, const std::string& bind_ip = "0.0.0.0") + bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, bool ssl = false, const std::string& bind_ip = "0.0.0.0") { m_connected = false; + m_ssl = ssl; try { - m_socket.close(); + m_ssl_socket.next_layer().close(); + + // Set SSL options + // disable sslv2 + m_ctx.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2); + m_ctx.set_default_verify_paths(); + // Get a list of endpoints corresponding to the server name. @@ -147,11 +158,11 @@ namespace net_utils boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); - m_socket.open(remote_endpoint.protocol()); + m_ssl_socket.next_layer().open(remote_endpoint.protocol()); if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) { boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(addr.c_str()), 0); - m_socket.bind(local_endpoint); + m_ssl_socket.next_layer().bind(local_endpoint); } @@ -160,17 +171,24 @@ namespace net_utils boost::system::error_code ec = boost::asio::error::would_block; - //m_socket.connect(remote_endpoint); - m_socket.async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1); + m_ssl_socket.next_layer().async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1); while (ec == boost::asio::error::would_block) { m_io_service.run_one(); } - if (!ec && m_socket.is_open()) + if (!ec && m_ssl_socket.next_layer().is_open()) { m_connected = true; m_deadline.expires_at(std::chrono::steady_clock::time_point::max()); + // SSL Options + if(m_ssl) { + // Disable verification of host certificate + m_ssl_socket.set_verify_mode(boost::asio::ssl::verify_peer); + // Handshake + m_ssl_socket.next_layer().set_option(boost::asio::ip::tcp::no_delay(true)); + m_ssl_socket.handshake(boost::asio::ssl::stream_base::client); + } return true; }else { @@ -193,7 +211,6 @@ namespace net_utils return true; } - inline bool disconnect() { @@ -202,8 +219,9 @@ namespace net_utils if(m_connected) { m_connected = false; - m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both); - + if(m_ssl) + shutdown_ssl(); + m_ssl_socket.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both); } } @@ -240,7 +258,7 @@ namespace net_utils // object is used as a callback and will update the ec variable when the // operation completes. The blocking_udp_client.cpp example shows how you // can use boost::bind rather than boost::lambda. - boost::asio::async_write(m_socket, boost::asio::buffer(buff), boost::lambda::var(ec) = boost::lambda::_1); + async_write(buff.c_str(), buff.size(), ec); // Block until the asynchronous operation has completed. while (ec == boost::asio::error::would_block) @@ -302,9 +320,7 @@ namespace net_utils */ boost::system::error_code ec; - size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec); - - + size_t writen = write(data, sz, ec); if (!writen || ec) { @@ -334,10 +350,7 @@ namespace net_utils bool is_connected() { - return m_connected && m_socket.is_open(); - //TRY_ENTRY() - //return m_socket.is_open(); - //CATCH_ENTRY_L0("is_connected", false) + return m_connected && m_ssl_socket.next_layer().is_open(); } inline @@ -369,8 +382,8 @@ namespace net_utils handler_obj hndlr(ec, bytes_transfered); char local_buff[10000] = {0}; - //m_socket.async_read_some(boost::asio::buffer(local_buff, sizeof(local_buff)), hndlr); - boost::asio::async_read(m_socket, boost::asio::buffer(local_buff, sizeof(local_buff)), boost::asio::transfer_at_least(1), hndlr); + + async_read(local_buff, sizeof(local_buff), boost::asio::transfer_at_least(1), hndlr); // Block until the asynchronous operation has completed. while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned)) @@ -451,10 +464,8 @@ namespace net_utils handler_obj hndlr(ec, bytes_transfered); - - //char local_buff[10000] = {0}; - boost::asio::async_read(m_socket, boost::asio::buffer((char*)buff.data(), buff.size()), boost::asio::transfer_at_least(buff.size()), hndlr); - + async_read((char*)buff.data(), buff.size(), boost::asio::transfer_at_least(buff.size()), hndlr); + // Block until the asynchronous operation has completed. while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned)) { @@ -500,10 +511,18 @@ namespace net_utils bool shutdown() { m_deadline.cancel(); - boost::system::error_code ignored_ec; - m_socket.cancel(ignored_ec); - m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); - m_socket.close(ignored_ec); + boost::system::error_code ec; + if(m_ssl) + shutdown_ssl(); + m_ssl_socket.next_layer().cancel(ec); + if(ec) + MDEBUG("Problems at cancel: " << ec.message()); + m_ssl_socket.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + if(ec) + MDEBUG("Problems at shutdown: " << ec.message()); + m_ssl_socket.next_layer().close(ec); + if(ec) + MDEBUG("Problems at close: " << ec.message()); boost::interprocess::ipcdetail::atomic_write32(&m_shutdowned, 1); m_connected = false; return true; @@ -520,7 +539,7 @@ namespace net_utils boost::asio::ip::tcp::socket& get_socket() { - return m_socket; + return m_ssl_socket.next_layer(); } private: @@ -537,7 +556,7 @@ namespace net_utils // connect(), read_line() or write_line() functions to return. LOG_PRINT_L3("Timed out socket"); m_connected = false; - m_socket.close(); + m_ssl_socket.next_layer().close(); // There is no longer an active deadline. The expiry is set to positive // infinity so that the actor takes no action until a new deadline is set. @@ -547,12 +566,61 @@ namespace net_utils // Put the actor back to sleep. m_deadline.async_wait(boost::bind(&blocked_mode_client::check_deadline, this)); } - + void shutdown_ssl() { + // ssl socket shutdown blocks if server doesn't respond. We close after 2 secs + boost::system::error_code ec = boost::asio::error::would_block; + m_deadline.expires_from_now(std::chrono::milliseconds(2000)); + m_ssl_socket.async_shutdown(boost::lambda::var(ec) = boost::lambda::_1); + while (ec == boost::asio::error::would_block) + { + m_io_service.run_one(); + } + // Ignore "short read" error + if (ec.category() == boost::asio::error::get_ssl_category() && + ec.value() != +#if BOOST_VERSION >= 106200 + boost::asio::ssl::error::stream_truncated +#else // older Boost supports only OpenSSL 1.0, so 1.0-only macros are appropriate + ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ) +#endif + ) + MDEBUG("Problems at ssl shutdown: " << ec.message()); + } + + protected: + bool write(const void* data, size_t sz, boost::system::error_code& ec) + { + bool success; + if(m_ssl) + success = boost::asio::write(m_ssl_socket, boost::asio::buffer(data, sz), ec); + else + success = boost::asio::write(m_ssl_socket.next_layer(), boost::asio::buffer(data, sz), ec); + return success; + } + + void async_write(const void* data, size_t sz, boost::system::error_code& ec) + { + if(m_ssl) + boost::asio::async_write(m_ssl_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); + else + boost::asio::async_write(m_ssl_socket.next_layer(), boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); + } + + void async_read(char* buff, size_t sz, boost::asio::detail::transfer_at_least_t transfer_at_least, handler_obj& hndlr) + { + if(!m_ssl) + boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(buff, sz), transfer_at_least, hndlr); + else + boost::asio::async_read(m_ssl_socket, boost::asio::buffer(buff, sz), transfer_at_least, hndlr); + + } protected: boost::asio::io_service m_io_service; - boost::asio::ip::tcp::socket m_socket; + boost::asio::ssl::context m_ctx; + boost::asio::ssl::stream<boost::asio::ip::tcp::socket> m_ssl_socket; + bool m_ssl; bool m_initialized; bool m_connected; boost::asio::steady_timer m_deadline; @@ -618,8 +686,8 @@ namespace net_utils boost::system::error_code ec; - size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec); - + size_t writen = write(data, sz, ec); + if (!writen || ec) { LOG_PRINT_L3("Problems at write: " << ec.message()); @@ -660,7 +728,7 @@ namespace net_utils // asynchronous operations are cancelled. This allows the blocked // connect(), read_line() or write_line() functions to return. LOG_PRINT_L3("Timed out socket"); - m_socket.close(); + m_ssl_socket.next_layer().close(); // There is no longer an active deadline. The expiry is set to positive // infinity so that the actor takes no action until a new deadline is set. diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h index df83517ff..0e31ee86f 100644 --- a/contrib/epee/include/net/net_utils_base.h +++ b/contrib/epee/include/net/net_utils_base.h @@ -29,11 +29,12 @@ #ifndef _NET_UTILS_BASE_H_ #define _NET_UTILS_BASE_H_ -#include <typeinfo> #include <boost/asio/io_service.hpp> #include <boost/uuid/uuid.hpp> +#include <memory> +#include <typeinfo> +#include <type_traits> #include "serialization/keyvalue_serialization.h" -#include "net/local_ip.h" #include "string_tools.h" #include "misc_log_ex.h" @@ -49,78 +50,119 @@ namespace epee { namespace net_utils { - struct network_address_base + class ipv4_network_address { - public: - bool operator==(const network_address_base &other) const { return m_full_id == other.m_full_id; } - bool operator!=(const network_address_base &other) const { return !operator==(other); } - bool operator<(const network_address_base &other) const { return m_full_id < other.m_full_id; } - bool is_same_host(const network_address_base &other) const { return m_host_id == other.m_host_id; } - virtual std::string str() const = 0; - virtual std::string host_str() const = 0; - virtual bool is_loopback() const = 0; - virtual bool is_local() const = 0; - virtual uint8_t get_type_id() const = 0; - protected: - // A very simple non cryptographic hash function by Fowler, Noll, Vo - uint64_t fnv1a(const uint8_t *data, size_t len) const { - uint64_t h = 0xcbf29ce484222325; - while (len--) - h = (h ^ *data++) * 0x100000001b3; - return h; - } - uint64_t m_host_id; - uint64_t m_full_id; + uint32_t m_ip; + uint16_t m_port; - protected: - virtual ~network_address_base() {} - }; - struct ipv4_network_address: public network_address_base - { - void init_ids() - { - m_host_id = fnv1a((const uint8_t*)&m_ip, sizeof(m_ip)); - m_full_id = fnv1a((const uint8_t*)&m_ip, sizeof(m_ip) + sizeof(m_port)); - } public: - ipv4_network_address(uint32_t ip, uint16_t port): network_address_base(), m_ip(ip), m_port(port) { init_ids(); } - uint32_t ip() const { return m_ip; } - uint16_t port() const { return m_port; } - virtual std::string str() const { return epee::string_tools::get_ip_string_from_int32(m_ip) + ":" + std::to_string(m_port); } - virtual std::string host_str() const { return epee::string_tools::get_ip_string_from_int32(m_ip); } - virtual bool is_loopback() const { return epee::net_utils::is_ip_loopback(m_ip); } - virtual bool is_local() const { return epee::net_utils::is_ip_local(m_ip); } - virtual uint8_t get_type_id() const { return ID; } - public: // serialization + constexpr ipv4_network_address(uint32_t ip, uint16_t port) noexcept + : m_ip(ip), m_port(port) {} + + bool equal(const ipv4_network_address& other) const noexcept; + bool less(const ipv4_network_address& other) const noexcept; + constexpr bool is_same_host(const ipv4_network_address& other) const noexcept + { return ip() == other.ip(); } + + constexpr uint32_t ip() const noexcept { return m_ip; } + constexpr uint16_t port() const noexcept { return m_port; } + std::string str() const; + std::string host_str() const; + bool is_loopback() const; + bool is_local() const; + static constexpr uint8_t get_type_id() noexcept { return ID; } + static const uint8_t ID = 1; -#pragma pack(push) -#pragma pack(1) - uint32_t m_ip; - uint16_t m_port; -#pragma pack(pop) BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(m_ip) KV_SERIALIZE(m_port) - if (!is_store) - const_cast<ipv4_network_address&>(this_ref).init_ids(); END_KV_SERIALIZE_MAP() }; - class network_address: public boost::shared_ptr<network_address_base> + + inline bool operator==(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept + { return lhs.equal(rhs); } + inline bool operator!=(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept + { return !lhs.equal(rhs); } + inline bool operator<(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept + { return lhs.less(rhs); } + inline bool operator<=(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept + { return !rhs.less(lhs); } + inline bool operator>(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept + { return rhs.less(lhs); } + inline bool operator>=(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept + { return !lhs.less(rhs); } + + class network_address { + struct interface + { + virtual ~interface() {}; + + virtual bool equal(const interface&) const = 0; + virtual bool less(const interface&) const = 0; + virtual bool is_same_host(const interface&) const = 0; + + virtual std::string str() const = 0; + virtual std::string host_str() const = 0; + virtual bool is_loopback() const = 0; + virtual bool is_local() const = 0; + virtual uint8_t get_type_id() const = 0; + }; + + template<typename T> + struct implementation final : interface + { + T value; + + implementation(const T& src) : value(src) {} + ~implementation() = default; + + // Type-checks for cast are done in cpp + static const T& cast(const interface& src) noexcept + { return static_cast<const implementation<T>&>(src).value; } + + virtual bool equal(const interface& other) const override + { return value.equal(cast(other)); } + + virtual bool less(const interface& other) const override + { return value.less(cast(other)); } + + virtual bool is_same_host(const interface& other) const override + { return value.is_same_host(cast(other)); } + + virtual std::string str() const override { return value.str(); } + virtual std::string host_str() const override { return value.host_str(); } + virtual bool is_loopback() const override { return value.is_loopback(); } + virtual bool is_local() const override { return value.is_local(); } + virtual uint8_t get_type_id() const override { return value.get_type_id(); } + }; + + std::shared_ptr<interface> self; + + template<typename Type> + Type& as_mutable() const + { + // types `implmentation<Type>` and `implementation<const Type>` are unique + using Type_ = typename std::remove_const<Type>::type; + network_address::interface* const self_ = self.get(); // avoid clang warning in typeid + if (!self_ || typeid(implementation<Type_>) != typeid(*self_)) + throw std::bad_cast{}; + return static_cast<implementation<Type_>*>(self_)->value; + } public: - network_address() {} - network_address(ipv4_network_address *address): boost::shared_ptr<network_address_base>(address) {} - bool operator==(const network_address &other) const { return (*this)->operator==(*other); } - bool operator!=(const network_address &other) const { return (*this)->operator!=(*other); } - bool operator<(const network_address &other) const { return (*this)->operator<(*other); } - bool is_same_host(const network_address &other) const { return (*this)->is_same_host(*other); } - std::string str() const { return (*this) ? (*this)->str() : "<none>"; } - std::string host_str() const { return (*this) ? (*this)->host_str() : "<none>"; } - bool is_loopback() const { return (*this)->is_loopback(); } - bool is_local() const { return (*this)->is_local(); } - uint8_t get_type_id() const { return (*this)->get_type_id(); } - template<typename Type> Type &as() { if (get_type_id() != Type::ID) throw std::runtime_error("Bad type"); return *(Type*)get(); } - template<typename Type> const Type &as() const { if (get_type_id() != Type::ID) throw std::runtime_error("Bad type"); return *(const Type*)get(); } + network_address() : self(nullptr) {} + template<typename T> + network_address(const T& src) + : self(std::make_shared<implementation<T>>(src)) {} + bool equal(const network_address &other) const; + bool less(const network_address &other) const; + bool is_same_host(const network_address &other) const; + std::string str() const { return self ? self->str() : "<none>"; } + std::string host_str() const { return self ? self->host_str() : "<none>"; } + bool is_loopback() const { return self ? self->is_loopback() : false; } + bool is_local() const { return self ? self->is_local() : false; } + uint8_t get_type_id() const { return self ? self->get_type_id() : 0; } + template<typename Type> const Type &as() const { return as_mutable<const Type>(); } BEGIN_KV_SERIALIZE_MAP() uint8_t type = is_store ? this_ref.get_type_id() : 0; @@ -129,13 +171,27 @@ namespace net_utils { case ipv4_network_address::ID: if (!is_store) - const_cast<network_address&>(this_ref).reset(new ipv4_network_address(0, 0)); - KV_SERIALIZE(template as<ipv4_network_address>()); + const_cast<network_address&>(this_ref) = ipv4_network_address{0, 0}; + KV_SERIALIZE(template as_mutable<ipv4_network_address>()); break; default: MERROR("Unsupported network address type: " << type); return false; } END_KV_SERIALIZE_MAP() }; + + inline bool operator==(const network_address& lhs, const network_address& rhs) + { return lhs.equal(rhs); } + inline bool operator!=(const network_address& lhs, const network_address& rhs) + { return !lhs.equal(rhs); } + inline bool operator<(const network_address& lhs, const network_address& rhs) + { return lhs.less(rhs); } + inline bool operator<=(const network_address& lhs, const network_address& rhs) + { return !rhs.less(lhs); } + inline bool operator>(const network_address& lhs, const network_address& rhs) + { return rhs.less(lhs); } + inline bool operator>=(const network_address& lhs, const network_address& rhs) + { return !lhs.less(rhs); } + inline bool create_network_address(network_address &address, const std::string &string, uint16_t default_port = 0) { uint32_t ip; @@ -144,7 +200,7 @@ namespace net_utils { if (default_port && !port) port = default_port; - address.reset(new ipv4_network_address(ip, port)); + address = ipv4_network_address{ip, port}; return true; } return false; @@ -182,7 +238,7 @@ namespace net_utils {} connection_context_base(): m_connection_id(), - m_remote_address(new ipv4_network_address(0,0)), + m_remote_address(ipv4_network_address{0,0}), m_is_income(false), m_started(time(NULL)), m_last_recv(0), @@ -235,7 +291,7 @@ namespace net_utils std::string print_connection_context(const connection_context_base& ctx) { std::stringstream ss; - ss << ctx.m_remote_address->str() << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT"); + ss << ctx.m_remote_address.str() << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT"); return ss.str(); } @@ -243,7 +299,7 @@ namespace net_utils std::string print_connection_context_short(const connection_context_base& ctx) { std::stringstream ss; - ss << ctx.m_remote_address->str() << (ctx.m_is_income ? " INC":" OUT"); + ss << ctx.m_remote_address.str() << (ctx.m_is_income ? " INC":" OUT"); return ss.str(); } diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h index 4423f2608..a94ecacc5 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h +++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -117,9 +117,9 @@ namespace epee typename stl_container::value_type exchange_val; typename t_storage::harray hval_array = stg.get_first_value(pname, exchange_val, hparent_section); if(!hval_array) return false; - container.push_back(std::move(exchange_val)); + container.insert(container.end(), std::move(exchange_val)); while(stg.get_next_value(hval_array, exchange_val)) - container.push_back(std::move(exchange_val)); + container.insert(container.end(), std::move(exchange_val)); return true; }//-------------------------------------------------------------------------------------------------------------------- template<class stl_container, class t_storage> @@ -152,7 +152,7 @@ namespace epee "size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type)); size_t count = (loaded_size/sizeof(typename stl_container::value_type)); for(size_t i = 0; i < count; i++) - container.push_back(*(pelem++)); + container.insert(container.end(), *(pelem++)); } return res; } @@ -186,12 +186,12 @@ namespace epee typename t_storage::harray hsec_array = stg.get_first_section(pname, hchild_section, hparent_section); if(!hsec_array || !hchild_section) return false; res = val._load(stg, hchild_section); - container.push_back(val); + container.insert(container.end(), val); while(stg.get_next_section(hsec_array, hchild_section)) { typename stl_container::value_type val_l = typename stl_container::value_type(); res |= val_l._load(stg, hchild_section); - container.push_back(std::move(val_l)); + container.insert(container.end(), std::move(val_l)); } return res; } @@ -250,6 +250,18 @@ namespace epee return unserialize_stl_container_t_val(d, stg, hparent_section, pname); } //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_serialize(const std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_unserialize(std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- }; template<> struct kv_serialization_overloads_impl_is_base_serializable_types<false> @@ -302,6 +314,18 @@ namespace epee { return unserialize_stl_container_t_obj(d, stg, hparent_section, pname); } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_serialize(const std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_unserialize(std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_obj(d, stg, hparent_section, pname); + } }; template<class t_storage> struct base_serializable_types: public boost::mpl::vector<uint64_t, uint32_t, uint16_t, uint8_t, int64_t, int32_t, int16_t, int8_t, double, bool, std::string, typename t_storage::meta_entry>::type @@ -399,5 +423,17 @@ namespace epee { return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname); } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + bool kv_serialize(const std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + bool kv_unserialize(std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname); + } } } diff --git a/contrib/epee/include/storages/http_abstract_invoke.h b/contrib/epee/include/storages/http_abstract_invoke.h index 823ce6731..6517f1253 100644 --- a/contrib/epee/include/storages/http_abstract_invoke.h +++ b/contrib/epee/include/storages/http_abstract_invoke.h @@ -44,8 +44,11 @@ namespace epee if(!serialization::store_t_to_json(out_struct, req_param)) return false; + http::fields_list additional_params; + additional_params.push_back(std::make_pair("Content-Type","application/json; charset=utf-8")); + const http::http_response_info* pri = NULL; - if(!transport.invoke(uri, method, req_param, timeout, std::addressof(pri))) + if(!transport.invoke(uri, method, req_param, timeout, std::addressof(pri), std::move(additional_params))) { LOG_PRINT_L1("Failed to invoke http request to " << uri); return false; diff --git a/contrib/epee/include/storages/portable_storage_val_converters.h b/contrib/epee/include/storages/portable_storage_val_converters.h index e9b91c82b..f4a16cfae 100644 --- a/contrib/epee/include/storages/portable_storage_val_converters.h +++ b/contrib/epee/include/storages/portable_storage_val_converters.h @@ -28,6 +28,8 @@ #pragma once +#include <regex> + #include "misc_language.h" #include "portable_storage_base.h" #include "warnings.h" @@ -131,6 +133,31 @@ POP_WARNINGS } }; + // For MyMonero/OpenMonero backend compatibility + // MyMonero backend sends amount, fees and timestamp values as strings. + // Until MM backend is updated, this is needed for compatibility between OpenMonero and MyMonero. + template<> + struct convert_to_integral<std::string, uint64_t, false> + { + static void convert(const std::string& from, uint64_t& to) + { + MTRACE("Converting std::string to uint64_t. Source: " << from); + // String only contains digits + if(std::all_of(from.begin(), from.end(), ::isdigit)) + to = boost::lexical_cast<uint64_t>(from); + // MyMonero ISO 8061 timestamp (2017-05-06T16:27:06Z) + else if (std::regex_match (from, std::regex("\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\dZ"))) + { + // Convert to unix timestamp + std::tm tm = {}; + std::istringstream ss(from); + if (ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S")) + to = std::mktime(&tm); + } else + ASSERT_AND_THROW_WRONG_CONVERSION(); + } + }; + template<class from_type, class to_type> struct is_convertable: std::integral_constant<bool, std::is_integral<to_type>::value && diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index ce7b2fb87..25639263c 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -39,6 +39,7 @@ #include <cstdlib> #include <string> #include <type_traits> +#include <regex> #include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid_io.hpp> #include <boost/lexical_cast.hpp> @@ -349,6 +350,11 @@ POP_WARNINGS s = *(t_pod_type*)bin_buff.data(); return true; } + //---------------------------------------------------------------------------- + inline bool validate_hex(uint64_t length, const std::string& str) + { + return std::regex_match(str, std::regex("'^[0-9abcdefABCDEF]+$'")) && str.size() == length; + } //---------------------------------------------------------------------------- inline std::string get_extension(const std::string& str) { diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index c61a6e684..3b41f415e 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -27,9 +27,9 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if (USE_READLINE AND GNU_READLINE_FOUND) - add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp string_tools.cpp readline_buffer.cpp) + add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp readline_buffer.cpp) else() - add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp string_tools.cpp) + add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp) endif() # Build and install libepee if we're building for GUI @@ -49,4 +49,5 @@ target_link_libraries(epee easylogging ${Boost_FILESYSTEM_LIBRARY} PRIVATE + ${OPENSSL_LIBRARIES} ${EXTRA_LIBRARIES}) diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index cfb2b7b15..74b7dd2f4 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -94,7 +94,7 @@ static const char *get_default_categories(int level) categories = "*:WARNING,net:FATAL,net.p2p:FATAL,net.cn:FATAL,global:INFO,verify:FATAL,stacktrace:INFO,logging:INFO,msgwriter:INFO"; break; case 1: - categories = "*:WARNING,global:INFO,stacktrace:INFO,logging:INFO,msgwriter:INFO"; + categories = "*:INFO,global:INFO,stacktrace:INFO,logging:INFO,msgwriter:INFO"; break; case 2: categories = "*:DEBUG"; diff --git a/contrib/epee/src/net_utils_base.cpp b/contrib/epee/src/net_utils_base.cpp new file mode 100644 index 000000000..22afcf819 --- /dev/null +++ b/contrib/epee/src/net_utils_base.cpp @@ -0,0 +1,60 @@ + +#include "net/net_utils_base.h" + +#include <cstring> +#include <typeindex> +#include "net/local_ip.h" + +namespace epee { namespace net_utils +{ + const uint8_t ipv4_network_address::ID; + + bool ipv4_network_address::equal(const ipv4_network_address& other) const noexcept + { return is_same_host(other) && port() == other.port(); } + + bool ipv4_network_address::less(const ipv4_network_address& other) const noexcept + { return is_same_host(other) ? port() < other.port() : ip() < other.ip(); } + + std::string ipv4_network_address::str() const + { return string_tools::get_ip_string_from_int32(ip()) + ":" + std::to_string(port()); } + + std::string ipv4_network_address::host_str() const { return string_tools::get_ip_string_from_int32(ip()); } + bool ipv4_network_address::is_loopback() const { return net_utils::is_ip_loopback(ip()); } + bool ipv4_network_address::is_local() const { return net_utils::is_ip_local(ip()); } + + + bool network_address::equal(const network_address& other) const + { + // clang typeid workaround + network_address::interface const* const self_ = self.get(); + network_address::interface const* const other_self = other.self.get(); + if (self_ == other_self) return true; + if (!self_ || !other_self) return false; + if (typeid(*self_) != typeid(*other_self)) return false; + return self_->equal(*other_self); + } + + bool network_address::less(const network_address& other) const + { + // clang typeid workaround + network_address::interface const* const self_ = self.get(); + network_address::interface const* const other_self = other.self.get(); + if (self_ == other_self) return false; + if (!self_ || !other_self) return self == nullptr; + if (typeid(*self_) != typeid(*other_self)) + return self_->get_type_id() < other_self->get_type_id(); + return self_->less(*other_self); + } + + bool network_address::is_same_host(const network_address& other) const + { + // clang typeid workaround + network_address::interface const* const self_ = self.get(); + network_address::interface const* const other_self = other.self.get(); + if (self_ == other_self) return true; + if (!self_ || !other_self) return false; + if (typeid(*self_) != typeid(*other_self)) return false; + return self_->is_same_host(*other_self); + } +}} + |