From 8b0068773525e585ce4c98d4ea2ce66dd6cf4aad Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Fri, 25 Aug 2017 11:14:46 -0400 Subject: [PATCH] Upgrades to epee::net_utils::network_address - internal nullptr checks - prevent modifications to network_address (shallow copy issues) - automagically works with any type containing interface functions - removed fnv1a hashing - ipv4_network_address now flattened with no base class --- .../epee/include/net/abstract_tcp_server2.inl | 4 +- contrib/epee/include/net/net_utils_base.h | 196 +++++++++------ contrib/epee/src/CMakeLists.txt | 4 +- contrib/epee/src/net_utils_base.cpp | 60 +++++ src/p2p/net_node.inl | 8 +- src/p2p/net_peerlist_boost_serialization.h | 23 +- src/p2p/p2p_protocol_defs.h | 6 +- src/rpc/core_rpc_server.cpp | 2 +- tests/unit_tests/ban.cpp | 2 +- tests/unit_tests/epee_utils.cpp | 231 ++++++++++++++++++ tests/unit_tests/test_peerlist.cpp | 2 +- 11 files changed, 446 insertions(+), 92 deletions(-) create mode 100644 contrib/epee/src/net_utils_base.cpp diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 76988a26e..94ef7c3b3 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -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(); - 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(); - 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); 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 #include #include +#include +#include +#include #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; - - 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 - static const uint8_t ID = 1; -#pragma pack(push) -#pragma pack(1) uint32_t m_ip; uint16_t m_port; -#pragma pack(pop) + + public: + 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; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(m_ip) KV_SERIALIZE(m_port) - if (!is_store) - const_cast(this_ref).init_ids(); END_KV_SERIALIZE_MAP() }; - class network_address: public boost::shared_ptr + + 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 + 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&>(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 self; + + template + Type& as_mutable() const + { + // types `implmentation` and `implementation` are unique + using Type_ = typename std::remove_const::type; + network_address::interface* const self_ = self.get(); // avoid clang warning in typeid + if (!self_ || typeid(implementation) != typeid(*self_)) + throw std::bad_cast{}; + return static_cast*>(self_)->value; + } public: - network_address() {} - network_address(ipv4_network_address *address): boost::shared_ptr(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() : ""; } - std::string host_str() const { return (*this) ? (*this)->host_str() : ""; } - 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 Type &as() { if (get_type_id() != Type::ID) throw std::runtime_error("Bad type"); return *(Type*)get(); } - template 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 + network_address(const T& src) + : self(std::make_shared>(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() : ""; } + std::string host_str() const { return self ? self->host_str() : ""; } + 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 const Type &as() const { return as_mutable(); } 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(this_ref).reset(new ipv4_network_address(0, 0)); - KV_SERIALIZE(template as()); + const_cast(this_ref) = ipv4_network_address{0, 0}; + KV_SERIALIZE(template as_mutable()); 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/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index c61a6e684..294515f83 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 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 +#include +#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); + } +}} + diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index f1ca50f76..6162d649b 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -390,7 +390,7 @@ namespace nodetool ip::tcp::endpoint endpoint = *i; if (endpoint.address().is_v4()) { - epee::net_utils::network_address na(new epee::net_utils::ipv4_network_address(boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_ulong()), endpoint.port())); + epee::net_utils::network_address na{epee::net_utils::ipv4_network_address{boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_ulong()), endpoint.port()}}; seed_nodes.push_back(na); MINFO("Added seed node: " << na.str()); } @@ -1521,7 +1521,7 @@ namespace nodetool return false; std::string ip = epee::string_tools::get_ip_string_from_int32(actual_ip); std::string port = epee::string_tools::num_to_string_fast(node_data.my_port); - epee::net_utils::network_address address(new epee::net_utils::ipv4_network_address(actual_ip, node_data.my_port)); + epee::net_utils::network_address address{epee::net_utils::ipv4_network_address(actual_ip, node_data.my_port)}; peerid_type pr = node_data.peer_id; bool r = m_net_server.connect_async(ip, port, m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ address, pr, this]( const typename net_server::t_connection_context& ping_context, @@ -1669,7 +1669,7 @@ namespace nodetool if(arg.node_data.peer_id != m_config.m_peer_id && arg.node_data.my_port) { peerid_type peer_id_l = arg.node_data.peer_id; - uint32_t port_l = arg.node_data.my_port; + uint32_t port_l = arg.node_data.my_port; //try ping to be sure that we can add this peer to peer_list try_ping(arg.node_data, context, [peer_id_l, port_l, context, this]() { @@ -1678,7 +1678,7 @@ namespace nodetool //called only(!) if success pinged, update local peerlist peerlist_entry pe; const epee::net_utils::network_address na = context.m_remote_address; - pe.adr.reset(new epee::net_utils::ipv4_network_address(na.as().ip(), port_l)); + pe.adr = epee::net_utils::ipv4_network_address(na.as().ip(), port_l); time_t last_seen; time(&last_seen); pe.last_seen = static_cast(last_seen); diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index 6ea2d48fd..079524aa1 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -31,11 +31,19 @@ #pragma once #include "net/net_utils_base.h" +#include "p2p/p2p_protocol_defs.h" namespace boost { namespace serialization { + template + inline void do_serialize(Archive &a, epee::net_utils::network_address& na, T local) + { + if (typename Archive::is_saving()) local = na.as(); + a & local; + if (!typename Archive::is_saving()) na = local; + } template inline void serialize(Archive &a, epee::net_utils::network_address& na, const ver_type ver) { @@ -46,10 +54,8 @@ namespace boost switch (type) { case epee::net_utils::ipv4_network_address::ID: - if (!typename Archive::is_saving()) - na.reset(new epee::net_utils::ipv4_network_address(0, 0)); - a & na.as(); - break; + do_serialize(a, na, epee::net_utils::ipv4_network_address{0, 0}); + break; default: throw std::runtime_error("Unsupported network address type"); } @@ -57,13 +63,14 @@ namespace boost template inline void serialize(Archive &a, epee::net_utils::ipv4_network_address& na, const ver_type ver) { - a & na.m_ip; - a & na.m_port; + uint32_t ip{na.ip()}; + uint16_t port{na.port()}; + a & ip; + a & port; if (!typename Archive::is_saving()) - na.init_ids(); + na = epee::net_utils::ipv4_network_address{ip, port}; } - template inline void serialize(Archive &a, nodetool::peerlist_entry& pl, const ver_type ver) { diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index f2b2cd1da..d49d83989 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -120,7 +120,7 @@ namespace nodetool ss << std::setfill ('0') << std::setw (8) << std::hex << std::noshowbase; for(const peerlist_entry& pe: pl) { - ss << pe.id << "\t" << pe.adr->str() << " \tlast_seen: " << epee::misc_utils::get_time_interval_string(now_time - pe.last_seen) << std::endl; + ss << pe.id << "\t" << pe.adr.str() << " \tlast_seen: " << epee::misc_utils::get_time_interval_string(now_time - pe.last_seen) << std::endl; } return ss.str(); } @@ -216,7 +216,7 @@ namespace nodetool std::list> local_peerlist; epee::serialization::selector::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); for (const auto &p: local_peerlist) - ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({new epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen})); + ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen})); } } END_KV_SERIALIZE_MAP() @@ -275,7 +275,7 @@ namespace nodetool std::list> local_peerlist; epee::serialization::selector::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); for (const auto &p: local_peerlist) - ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({new epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen})); + ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen})); } } END_KV_SERIALIZE_MAP() diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9b91f438a..b9678f414 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1395,7 +1395,7 @@ namespace cryptonote } else { - na.reset(new epee::net_utils::ipv4_network_address(i->ip, 0)); + na = epee::net_utils::ipv4_network_address{i->ip, 0}; } if (i->ban) m_p2p.block_host(na, i->seconds); diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index b8d57452e..d15716a42 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -33,7 +33,7 @@ #include "p2p/net_node.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" -#define MAKE_IPV4_ADDRESS(a,b,c,d) new epee::net_utils::ipv4_network_address(MAKE_IP(a,b,c,d),0) +#define MAKE_IPV4_ADDRESS(a,b,c,d) epee::net_utils::ipv4_network_address{MAKE_IP(a,b,c,d),0} namespace cryptonote { class blockchain_storage; diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index e8ddbe3f5..f6cb0c163 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include #include #include #include @@ -42,7 +43,11 @@ # include #endif +#include "boost/archive/portable_binary_iarchive.hpp" +#include "boost/archive/portable_binary_oarchive.hpp" #include "hex.h" +#include "net/net_utils_base.h" +#include "p2p/net_peerlist_boost_serialization.h" #include "span.h" #include "string_tools.h" @@ -100,6 +105,40 @@ namespace boost::range::iota(out, 0); return out; } + + #define CHECK_EQUAL(lhs, rhs) \ + EXPECT_TRUE( lhs == rhs ); \ + EXPECT_TRUE( rhs == lhs ); \ + EXPECT_FALSE( lhs != rhs ); \ + EXPECT_FALSE( rhs != lhs ); \ + EXPECT_FALSE( lhs < rhs ); \ + EXPECT_FALSE( rhs < lhs ); \ + EXPECT_TRUE( lhs <= rhs ); \ + EXPECT_TRUE( rhs <= lhs ); \ + EXPECT_FALSE( lhs > rhs ); \ + EXPECT_FALSE( rhs > lhs ); \ + EXPECT_TRUE( lhs >= rhs ); \ + EXPECT_TRUE( rhs >= lhs ) + + #define CHECK_LESS(lhs, rhs) \ + EXPECT_FALSE( lhs == rhs ); \ + EXPECT_FALSE( rhs == lhs ); \ + EXPECT_TRUE( lhs != rhs ); \ + EXPECT_TRUE( rhs != lhs ); \ + EXPECT_TRUE( lhs < rhs ); \ + EXPECT_FALSE( rhs < lhs ); \ + EXPECT_TRUE( lhs <= rhs ); \ + EXPECT_FALSE( rhs <= lhs ); \ + EXPECT_FALSE( lhs > rhs ); \ + EXPECT_TRUE( rhs > lhs ); \ + EXPECT_FALSE( lhs >= rhs ); \ + EXPECT_TRUE( rhs >= lhs ) + + #ifdef BOOST_LITTLE_ENDIAN + #define CHECK_LESS_ENDIAN(lhs, rhs) CHECK_LESS( rhs , lhs ) + #else + #define CHECK_LESS_ENDIAN(lhs, rhs) CHECK_LESS( lhs , rhs ) + #endif } TEST(Span, Traits) @@ -419,3 +458,195 @@ TEST(StringTools, GetIpInt32) EXPECT_EQ(htonl(0xff0aff00), ip); } +TEST(NetUtils, IPv4NetworkAddress) +{ + const auto ip1 = boost::endian::native_to_big(0x330012FFu); + const auto ip_loopback = boost::endian::native_to_big(0x7F000001u); + const auto ip_local = boost::endian::native_to_big(0x0A000000u); + + epee::net_utils::ipv4_network_address address1{ip1, 65535}; + CHECK_EQUAL(address1, address1); + EXPECT_STREQ("51.0.18.255:65535", address1.str().c_str()); + EXPECT_STREQ("51.0.18.255", address1.host_str().c_str()); + EXPECT_FALSE(address1.is_loopback()); + EXPECT_FALSE(address1.is_local()); + EXPECT_EQ(epee::net_utils::ipv4_network_address::ID, address1.get_type_id()); + EXPECT_EQ(ip1, address1.ip()); + EXPECT_EQ(65535, address1.port()); + EXPECT_TRUE(epee::net_utils::ipv4_network_address{std::move(address1)} == address1); + EXPECT_TRUE(epee::net_utils::ipv4_network_address{address1} == address1); + + const epee::net_utils::ipv4_network_address loopback{ip_loopback, 0}; + CHECK_EQUAL(loopback, loopback); + CHECK_LESS_ENDIAN(address1, loopback); + EXPECT_STREQ("127.0.0.1:0", loopback.str().c_str()); + EXPECT_STREQ("127.0.0.1", loopback.host_str().c_str()); + EXPECT_TRUE(loopback.is_loopback()); + EXPECT_FALSE(loopback.is_local()); + EXPECT_EQ(epee::net_utils::ipv4_network_address::ID, address1.get_type_id()); + EXPECT_EQ(ip_loopback, loopback.ip()); + EXPECT_EQ(0, loopback.port()); + + const epee::net_utils::ipv4_network_address local{ip_local, 8080}; + CHECK_EQUAL(local, local); + CHECK_LESS(local, address1); + CHECK_LESS(local, loopback); + EXPECT_FALSE(local.is_loopback()); + EXPECT_TRUE(local.is_local()); + + epee::net_utils::ipv4_network_address address2{ip1, 55}; + CHECK_EQUAL(address2, address2); + CHECK_LESS_ENDIAN(address2, loopback); + CHECK_LESS(local, address2); + EXPECT_STREQ("51.0.18.255:55", address2.str().c_str()); + EXPECT_STREQ("51.0.18.255", address2.host_str().c_str()); + + + address2 = std::move(address1); + CHECK_EQUAL(address2, address1); + + address2 = local; + CHECK_EQUAL(address2, local); + CHECK_LESS(address2, address1); + + { + std::stringstream stream; + { + boost::archive::portable_binary_oarchive ostream{stream}; + ostream << address1; + } + { + boost::archive::portable_binary_iarchive istream{stream}; + istream >> address2; + } + } + CHECK_EQUAL(address1, address2); + EXPECT_EQ(ip1, address2.ip()); + EXPECT_EQ(65535, address2.port()); +} + +TEST(NetUtils, NetworkAddress) +{ + const auto ip1 = boost::endian::native_to_big(0x330012FFu); + const auto ip_loopback = boost::endian::native_to_big(0x7F000001u); + const auto ip_local = boost::endian::native_to_big(0x0A000000u); + + struct custom_address { + constexpr static bool equal(const custom_address&) noexcept { return false; } + constexpr static bool less(const custom_address&) noexcept { return false; } + constexpr static bool is_same_host(const custom_address&) noexcept { return false; } + constexpr static bool is_loopback() noexcept { return false; } + constexpr static bool is_local() noexcept { return false; } + static std::string str() { return {}; } + static std::string host_str() { return {}; } + constexpr static uint8_t get_type_id() noexcept { return uint8_t(-1); } + }; + + const epee::net_utils::network_address empty; + CHECK_EQUAL(empty, empty); + EXPECT_TRUE(empty.is_same_host(empty)); + EXPECT_STREQ("", empty.str().c_str()); + EXPECT_STREQ("", empty.host_str().c_str()); + EXPECT_FALSE(empty.is_loopback()); + EXPECT_FALSE(empty.is_local()); + EXPECT_EQ(0, empty.get_type_id()); + EXPECT_THROW(empty.as(), std::bad_cast); + + epee::net_utils::network_address address1{ + epee::net_utils::ipv4_network_address{ip1, 65535} + }; + CHECK_EQUAL(address1, address1); + CHECK_EQUAL(epee::net_utils::network_address{address1}, address1); + CHECK_LESS(empty, address1); + EXPECT_TRUE(address1.is_same_host(address1)); + EXPECT_FALSE(empty.is_same_host(address1)); + EXPECT_FALSE(address1.is_same_host(empty)); + EXPECT_STREQ("51.0.18.255:65535", address1.str().c_str()); + EXPECT_STREQ("51.0.18.255", address1.host_str().c_str()); + EXPECT_FALSE(address1.is_loopback()); + EXPECT_FALSE(address1.is_local()); + EXPECT_EQ(epee::net_utils::ipv4_network_address::ID, address1.get_type_id()); + EXPECT_NO_THROW(address1.as()); + EXPECT_THROW(address1.as(), std::bad_cast); + + const epee::net_utils::network_address loopback{ + epee::net_utils::ipv4_network_address{ip_loopback, 0} + }; + CHECK_EQUAL(loopback, loopback); + CHECK_LESS(empty, loopback); + CHECK_LESS_ENDIAN(address1, loopback); + EXPECT_TRUE(loopback.is_same_host(loopback)); + EXPECT_FALSE(loopback.is_same_host(address1)); + EXPECT_FALSE(address1.is_same_host(loopback)); + EXPECT_STREQ("127.0.0.1:0", loopback.str().c_str()); + EXPECT_STREQ("127.0.0.1", loopback.host_str().c_str()); + EXPECT_TRUE(loopback.is_loopback()); + EXPECT_FALSE(loopback.is_local()); + EXPECT_EQ(epee::net_utils::ipv4_network_address::ID, address1.get_type_id()); + + const epee::net_utils::network_address local{ + epee::net_utils::ipv4_network_address{ip_local, 8080} + }; + CHECK_EQUAL(local, local); + CHECK_LESS(local, loopback); + CHECK_LESS(local, address1); + EXPECT_FALSE(local.is_loopback()); + EXPECT_TRUE(local.is_local()); + + epee::net_utils::network_address address2{ + epee::net_utils::ipv4_network_address{ip1, 55} + }; + CHECK_EQUAL(address2, address2); + CHECK_LESS(address2, address1); + CHECK_LESS(local, address2); + CHECK_LESS_ENDIAN(address2, loopback); + EXPECT_TRUE(address1.is_same_host(address2)); + EXPECT_TRUE(address2.is_same_host(address1)); + EXPECT_STREQ("51.0.18.255:55", address2.str().c_str()); + EXPECT_STREQ("51.0.18.255", address2.host_str().c_str()); + + address2 = std::move(address1); + CHECK_EQUAL(address1, address1); + CHECK_EQUAL(empty, address1); + CHECK_LESS(address1, address2); + EXPECT_FALSE(address1.is_same_host(address2)); + EXPECT_FALSE(address2.is_same_host(address1)); + EXPECT_STREQ("51.0.18.255:65535", address2.str().c_str()); + EXPECT_STREQ("51.0.18.255", address2.host_str().c_str()); + EXPECT_FALSE(address1.is_loopback()); + EXPECT_FALSE(address1.is_local()); + EXPECT_THROW(address1.as(), std::bad_cast); + EXPECT_NO_THROW(address2.as()); + + address2 = local; + CHECK_EQUAL(address2, local); + CHECK_LESS(address1, address2); + EXPECT_TRUE(address2.is_same_host(local)); + EXPECT_TRUE(local.is_same_host(address2)); + EXPECT_FALSE(address2.is_same_host(address1)); + EXPECT_FALSE(address1.is_same_host(address2)); + + { + std::stringstream stream; + { + boost::archive::portable_binary_oarchive ostream{stream}; + ostream << address2; + } + { + boost::archive::portable_binary_iarchive istream{stream}; + istream >> address1; + } + } + CHECK_EQUAL(address1, address2); + EXPECT_TRUE(address1.is_same_host(address2)); + EXPECT_TRUE(address2.is_same_host(address1)); + EXPECT_NO_THROW(address1.as()); + + address1 = custom_address{}; + CHECK_EQUAL(address1, address1); + CHECK_LESS(address2, address1); + EXPECT_FALSE(address1.is_same_host(loopback)); + EXPECT_FALSE(loopback.is_same_host(address1)); + EXPECT_THROW(address1.as(), std::bad_cast); + EXPECT_NO_THROW(address1.as()); +} diff --git a/tests/unit_tests/test_peerlist.cpp b/tests/unit_tests/test_peerlist.cpp index 4a546b50d..849020d25 100644 --- a/tests/unit_tests/test_peerlist.cpp +++ b/tests/unit_tests/test_peerlist.cpp @@ -38,7 +38,7 @@ TEST(peer_list, peer_list_general) { nodetool::peerlist_manager plm; plm.init(false); -#define MAKE_IPV4_ADDRESS(a,b,c,d,e) new epee::net_utils::ipv4_network_address(MAKE_IP(a,b,c,d),e) +#define MAKE_IPV4_ADDRESS(a,b,c,d,e) epee::net_utils::ipv4_network_address{MAKE_IP(a,b,c,d),e} #define ADD_GRAY_NODE(addr_, id_, last_seen_) { nodetool::peerlist_entry ple; ple.last_seen=last_seen_;ple.adr = addr_; ple.id = id_;plm.append_with_peer_gray(ple);} #define ADD_WHITE_NODE(addr_, id_, last_seen_) { nodetool::peerlist_entry ple;ple.last_seen=last_seen_; ple.adr = addr_; ple.id = id_;plm.append_with_peer_white(ple);}