From 08382d866b116e3ec9d50193bcf45caea8ca09a2 Mon Sep 17 00:00:00 2001 From: Mike Ellery Date: Wed, 8 Nov 2017 10:10:24 -0800 Subject: [PATCH] Support ipv6 for peer and RPC comms: Fixes: RIPD-1574 Alias beast address classes to the asio equivalents. Adjust users of address classes accordingly. Fix resolver class so that it can support ipv6 addresses. Make unit tests use ipv6 localhost network. Extend endpoint peer message to support string endpoint representations while also supporting the existing fields (both are optional/repeated types). Expand test for Livecache and Endpoint. Workaround some false positive ipaddr tests on windows (asio bug?) Replaced usage of address::from_string(deprecated) with free function make_address. Identified a remaining use of v4 address type and replaced with the more appropriate IPEndpoint type (rpc_ip cmdline option). Add CLI flag for using ipv4 with unit tests. Release Notes ------------- The optional rpc_port command line flag is deprecated. The rpc_ip parameter now works as documented and accepts ip and port combined. --- src/ripple/app/main/Main.cpp | 58 ++- src/ripple/basics/impl/ResolverAsio.cpp | 16 + src/ripple/basics/impl/StringUtilities.cpp | 30 +- .../beast/insight/impl/StatsDCollector.cpp | 14 +- src/ripple/beast/net/IPAddress.h | 273 ++--------- src/ripple/beast/net/IPAddressV4.h | 166 +------ src/ripple/beast/net/IPAddressV6.h | 71 +-- src/ripple/beast/net/IPEndpoint.h | 5 +- src/ripple/beast/net/detail/Parse.h | 103 ----- .../beast/net/impl/IPAddressConversion.cpp | 29 +- src/ripple/beast/net/impl/IPAddressV4.cpp | 145 +----- src/ripple/beast/net/impl/IPAddressV6.cpp | 55 +-- src/ripple/beast/net/impl/IPEndpoint.cpp | 160 +++---- src/ripple/core/Config.h | 4 +- src/ripple/net/impl/RPCCall.cpp | 7 +- src/ripple/overlay/impl/OverlayImpl.cpp | 8 +- src/ripple/overlay/impl/PeerImp.cpp | 94 ++-- src/ripple/overlay/impl/PeerImp.h | 12 +- src/ripple/overlay/impl/TMHello.cpp | 118 ++--- src/ripple/peerfinder/impl/Logic.h | 9 +- src/ripple/peerfinder/impl/SourceStrings.cpp | 2 +- src/ripple/proto/ripple.proto | 18 +- src/ripple/rpc/impl/Role.cpp | 2 +- src/ripple/rpc/impl/ServerHandlerImp.cpp | 9 +- src/ripple/server/impl/Port.cpp | 6 +- src/test/app/ValidatorSite_test.cpp | 23 +- src/test/basics/StringUtilities_test.cpp | 3 + src/test/beast/IPEndpointCommon.h | 64 +++ src/test/beast/IPEndpoint_test.cpp | 425 +++++++++++------- src/test/jtx/TrustedPublisherServer.h | 5 +- src/test/jtx/envconfig.h | 9 + src/test/jtx/impl/JSONRPCClient.cpp | 8 +- src/test/jtx/impl/WSClient.cpp | 6 +- src/test/jtx/impl/envconfig.cpp | 14 +- src/test/overlay/short_read_test.cpp | 4 +- src/test/peerfinder/Livecache_test.cpp | 188 +++++++- src/test/resource/Logic_test.cpp | 15 +- src/test/rpc/NoRippleCheck_test.cpp | 3 +- src/test/rpc/ValidatorRPC_test.cpp | 16 +- src/test/server/ServerStatus_test.cpp | 4 +- src/test/server/Server_test.cpp | 20 +- 41 files changed, 968 insertions(+), 1253 deletions(-) delete mode 100644 src/ripple/beast/net/detail/Parse.h create mode 100644 src/test/beast/IPEndpointCommon.h diff --git a/src/ripple/app/main/Main.cpp b/src/ripple/app/main/Main.cpp index 56c69c285f..7ad220ebfc 100644 --- a/src/ripple/app/main/Main.cpp +++ b/src/ripple/app/main/Main.cpp @@ -216,12 +216,15 @@ public: } }; +namespace test{ extern std::atomic envUseIPv4; } + static int runUnitTests( std::string const& pattern, std::string const& argument, bool quiet, bool log, bool child, + bool ipv4, std::size_t num_jobs, int argc, char** argv) @@ -229,6 +232,9 @@ static int runUnitTests( using namespace beast::unit_test; using namespace ripple::test; + if (ipv4) + ripple::test::envUseIPv4 = true; + #if HAS_BOOST_PROCESS if (!child && num_jobs == 1) #endif @@ -365,6 +371,7 @@ int run (int argc, char** argv) "Specify the IP address for RPC command. " "Format: [':']") ("rpc_port", po::value (), + "DEPRECATED: include with rpc_ip instead. " "Specify the port number for RPC command.") ; @@ -384,6 +391,7 @@ int run (int argc, char** argv) "argument is handled individually by any suite that accesses it -- " "as such, it typically only make sense to provide this when running " "a single suite.") + ("unittest-ipv4", "Use IPv4 localhost when running unittests (default is IPv6).") ("unittest-log", "Force unit test log message output. Only useful in combination with " "--quiet, in which case log messages will print but suite/case names " @@ -468,6 +476,7 @@ int run (int argc, char** argv) bool (vm.count ("quiet")), bool (vm.count ("unittest-log")), unittestChild, + bool (vm.count ("unittest-ipv4")), numJobs, argc, argv); @@ -553,37 +562,38 @@ int run (int argc, char** argv) // happen after the config file is loaded. if (vm.count ("rpc_ip")) { - try - { - config->rpc_ip.emplace ( - boost::asio::ip::address_v4::from_string( - vm["rpc_ip"].as())); - } - catch(std::exception const&) + auto res = beast::IP::Endpoint::from_string_checked( + vm["rpc_ip"].as()); + if (! res.second) { std::cerr << "Invalid rpc_ip = " << - vm["rpc_ip"].as() << std::endl; + vm["rpc_ip"].as() << "\n"; return -1; } - } - // Override the RPC destination port number - // - if (vm.count ("rpc_port")) - { - try + if (res.first.port() == 0) { - config->rpc_port.emplace ( - vm["rpc_port"].as()); + std::cerr << "No port specified in rpc_ip.\n"; + if (vm.count ("rpc_port")) + { + std::cerr << "WARNING: using deprecated rpc_port param.\n"; + try + { + res.first.at_port(vm["rpc_port"].as()); + if (res.first.port() == 0) + throw std::domain_error("0"); + } + catch(std::exception const& e) + { + std::cerr << "Invalid rpc_port = " << e.what() << "\n"; + return -1; + } + } + else + return -1; + } - if (*config->rpc_port == 0) - throw std::domain_error("0"); - } - catch(std::exception const& e) - { - std::cerr << "Invalid rpc_port = " << e.what() << "\n"; - return -1; - } + config->rpc_ip = std::move(res.first); } if (vm.count ("quorum")) diff --git a/src/ripple/basics/impl/ResolverAsio.cpp b/src/ripple/basics/impl/ResolverAsio.cpp index 4f187642e8..4b85af7857 100644 --- a/src/ripple/basics/impl/ResolverAsio.cpp +++ b/src/ripple/basics/impl/ResolverAsio.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -259,6 +260,21 @@ public: HostAndPort parseName(std::string const& str) { + // first attempt to parse as an endpoint (IP addr + port). + // If that doesn't succeed, fall back to generic name + port parsing + + auto result {beast::IP::Endpoint::from_string_checked (str)}; + if (result.second) + { + return make_pair ( + result.first.address().to_string(), + std::to_string(result.first.port())); + } + + // generic name/port parsing, which doesn't work for + // IPv6 addresses in particular because it considers a colon + // a port separator + // Attempt to find the first and last non-whitespace auto const find_whitespace = std::bind ( &std::isspace , diff --git a/src/ripple/basics/impl/StringUtilities.cpp b/src/ripple/basics/impl/StringUtilities.cpp index 6d83586632..503177fe65 100644 --- a/src/ripple/basics/impl/StringUtilities.cpp +++ b/src/ripple/basics/impl/StringUtilities.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -93,24 +93,38 @@ uint64_t uintFromHex (std::string const& strSrc) bool parseUrl (parsedURL& pUrl, std::string const& strUrl) { // scheme://username:password@hostname:port/rest - static boost::regex reUrl ("(?i)\\`\\s*([[:alpha:]][-+.[:alpha:][:digit:]]*)://([^:/]+)(?::(\\d+))?(/.*)?\\s*?\\'"); + static boost::regex reUrl ("(?i)\\`\\s*([[:alpha:]][-+.[:alpha:][:digit:]]*)://([^/]+)(/.*)?\\s*?\\'"); boost::smatch smMatch; bool bMatch = boost::regex_match (strUrl, smMatch, reUrl); // Match status code. if (bMatch) { - std::string strPort; - pUrl.scheme = smMatch[1]; boost::algorithm::to_lower (pUrl.scheme); + pUrl.path = smMatch[3]; pUrl.domain = smMatch[2]; - if (smMatch[3].length ()) + + // now consider the domain/port fragment + auto colonPos = pUrl.domain.find_last_of(':'); + if (colonPos != std::string::npos) { - pUrl.port = beast::lexicalCast ( - std::string (smMatch[3])); + // use Endpoint class to see if this thing looks + // like an IP addr... + auto result {beast::IP::Endpoint::from_string_checked (pUrl.domain)}; + if (result.second) + { + pUrl.domain = result.first.address().to_string(); + pUrl.port = result.first.port(); + } + else // otherwise we are DNS name + port + { + pUrl.port = beast::lexicalCast ( + pUrl.domain.substr(colonPos+1)); + pUrl.domain = pUrl.domain.substr(0, colonPos); + } } - pUrl.path = smMatch[4]; + //else, the whole thing is domain, not port } return bMatch; diff --git a/src/ripple/beast/insight/impl/StatsDCollector.cpp b/src/ripple/beast/insight/impl/StatsDCollector.cpp index c2a30cbf05..52646bb7d3 100644 --- a/src/ripple/beast/insight/impl/StatsDCollector.cpp +++ b/src/ripple/beast/insight/impl/StatsDCollector.cpp @@ -217,19 +217,9 @@ private: std::thread m_thread; static boost::asio::ip::udp::endpoint to_endpoint ( - IP::Endpoint const &address) + IP::Endpoint const &ep) { - if (address.is_v4 ()) - { - return boost::asio::ip::udp::endpoint ( - boost::asio::ip::address_v4 ( - address.to_v4().value), address.port ()); - } - - // VFALCO TODO IPv6 support - assert(false); - return boost::asio::ip::udp::endpoint ( - boost::asio::ip::address_v6 (), 0); + return boost::asio::ip::udp::endpoint (ep.address(), ep.port()); } public: diff --git a/src/ripple/beast/net/IPAddress.h b/src/ripple/beast/net/IPAddress.h index 1139ee87e8..ee473ba1a1 100644 --- a/src/ripple/beast/net/IPAddress.h +++ b/src/ripple/beast/net/IPAddress.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -37,213 +38,22 @@ namespace beast { namespace IP { -/** A version-independent IP address. - This object can represent either an IPv4 or IPv6 address. -*/ -class Address +using Address = boost::asio::ip::address; + +/** Returns the address represented as a string. */ +inline +std::string +to_string (Address const& addr) { -public: - /** Create an unspecified IPv4 address. */ - Address () - : m_type (ipv4) - { - } - - /** Create an IPv4 address. */ - Address (AddressV4 const& addr) - : m_type (ipv4) - , m_v4 (addr) - { - } - - /** Create an IPv6 address. */ - Address (AddressV6 const& addr) - : m_type (ipv6) - , m_v6 (addr) - { - } - - /** Assign a copy from another address in any format. */ - /** @{ */ - Address& - operator= (AddressV4 const& addr) - { - m_type = ipv4; - m_v6 = AddressV6(); - m_v4 = addr; - return *this; - } - - Address& - operator= (AddressV6 const& addr) - { - m_type = ipv6; - m_v4 = AddressV4(); - m_v6 = addr; - return *this; - } - /** @} */ - - /** Create an Address from a string. - @return A pair with the address, and bool set to `true` on success. - */ - static - std::pair - from_string (std::string const& s); - - /** Returns a string representing the address. */ - std::string - to_string () const - { - return (is_v4 ()) - ? IP::to_string (to_v4()) - : IP::to_string (to_v6()); - } - - /** Returns `true` if this address represents an IPv4 address. */ - bool - is_v4 () const noexcept - { - return m_type == ipv4; - } - - /** Returns `true` if this address represents an IPv6 address. */ - bool - is_v6() const noexcept - { - return m_type == ipv6; - } - - /** Returns the IPv4 address. - Precondition: - is_v4() == `true` - */ - AddressV4 const& - to_v4 () const - { - if (!is_v4 ()) - throw std::bad_cast(); - return m_v4; - } - - /** Returns the IPv6 address. - Precondition: - is_v6() == `true` - */ - AddressV6 const& - to_v6 () const - { - if (!is_v6 ()) - throw std::bad_cast(); - return m_v6; - } - - /** Returns `true` if this address represents 0.0.0.0 */ - bool - is_any () const - { - return is_v4 () ? m_v4 == IP::AddressV4::any () - : false; // m_v6 == IP::AddressV6::any(); - } - - template - friend - void - hash_append(Hasher& h, Address const& addr) noexcept - { - using beast::hash_append; - if (addr.is_v4 ()) - hash_append(h, addr.to_v4 ()); - else if (addr.is_v6 ()) - hash_append(h, addr.to_v6 ()); - else - assert (false); - } - - /** Arithmetic comparison. */ - /** @{ */ - friend - bool - operator== (Address const& lhs, Address const& rhs) - { - if (lhs.is_v4 ()) - { - if (rhs.is_v4 ()) - return lhs.to_v4() == rhs.to_v4(); - } - else - { - if (rhs.is_v6 ()) - return lhs.to_v6() == rhs.to_v6(); - } - - return false; - } - - friend - bool - operator< (Address const& lhs, Address const& rhs) - { - if (lhs.m_type < rhs.m_type) - return true; - if (lhs.is_v4 ()) - return lhs.to_v4() < rhs.to_v4(); - return lhs.to_v6() < rhs.to_v6(); - } - - friend - bool - operator!= (Address const& lhs, Address const& rhs) - { - return ! (lhs == rhs); - } - - friend - bool - operator> (Address const& lhs, Address const& rhs) - { - return rhs < lhs; - } - - friend - bool - operator<= (Address const& lhs, Address const& rhs) - { - return ! (lhs > rhs); - } - - friend - bool - operator>= (Address const& lhs, Address const& rhs) - { - return ! (rhs > lhs); - } - /** @} */ - -private: - enum Type - { - ipv4, - ipv6 - }; - - Type m_type; - AddressV4 m_v4; - AddressV6 m_v6; -}; - -//------------------------------------------------------------------------------ - -// Properties + return addr.to_string (); +} /** Returns `true` if this is a loopback address. */ inline bool is_loopback (Address const& addr) { - return (addr.is_v4 ()) - ? is_loopback (addr.to_v4 ()) - : is_loopback (addr.to_v6 ()); + return addr.is_loopback(); } /** Returns `true` if the address is unspecified. */ @@ -251,9 +61,7 @@ inline bool is_unspecified (Address const& addr) { - return (addr.is_v4 ()) - ? is_unspecified (addr.to_v4 ()) - : is_unspecified (addr.to_v6 ()); + return addr.is_unspecified(); } /** Returns `true` if the address is a multicast address. */ @@ -261,9 +69,7 @@ inline bool is_multicast (Address const& addr) { - return (addr.is_v4 ()) - ? is_multicast (addr.to_v4 ()) - : is_multicast (addr.to_v6 ()); + return addr.is_multicast(); } /** Returns `true` if the address is a private unroutable address. */ @@ -286,51 +92,24 @@ is_public (Address const& addr) : is_public (addr.to_v6 ()); } -//------------------------------------------------------------------------------ - -/** Returns the address represented as a string. */ -inline std::string to_string (Address const& addr) -{ - return addr.to_string (); -} - -/** Output stream conversion. */ -template -OutputStream& -operator<< (OutputStream& os, Address const& addr) -{ - return os << to_string (addr); -} - -/** Input stream conversion. */ -inline -std::istream& -operator>> (std::istream& is, Address& addr) -{ - // VFALCO TODO Support ipv6! - AddressV4 addrv4; - is >> addrv4; - addr = Address (addrv4); - return is; -} - -inline -std::pair -Address::from_string (std::string const& s) -{ - std::stringstream is (s); - Address addr; - is >> addr; - if (! is.fail() && is.rdbuf()->in_avail() == 0) - return std::make_pair (addr, true); - return std::make_pair (Address (), false); -} - -} } //------------------------------------------------------------------------------ +template +void +hash_append(Hasher& h, beast::IP::Address const& addr) noexcept +{ + using beast::hash_append; + if (addr.is_v4 ()) + hash_append(h, addr.to_v4().to_bytes()); + else if (addr.is_v6 ()) + hash_append(h, addr.to_v6().to_bytes()); + else + assert (false); +} +} + namespace std { template <> struct hash diff --git a/src/ripple/beast/net/IPAddressV4.h b/src/ripple/beast/net/IPAddressV4.h index 2e5532ac32..428d100369 100644 --- a/src/ripple/beast/net/IPAddressV4.h +++ b/src/ripple/beast/net/IPAddressV4.h @@ -21,141 +21,17 @@ #define BEAST_NET_IPADDRESSV4_H_INCLUDED #include - #include #include #include #include #include +#include namespace beast { namespace IP { -/** Represents a version 4 IP address. */ -struct AddressV4 -{ - /** Default constructor represents the 'any' address. */ - AddressV4 (); - - /** Construct from a 32-bit unsigned. - @note Octets are formed in order from the MSB to the LSB. - */ - explicit AddressV4 (std::uint32_t value_); - - /** Construct from four individual octets.. - @note The resulting address is a.b.c.d - */ - AddressV4 (std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d); - - /** Create an address from an IPv4 address string in dotted decimal form. - @return A pair with the address, and bool set to `true` on success. - */ - static std::pair from_string (std::string const& s); - - /** Returns an address that represents 'any' address. */ - static AddressV4 any () - { return AddressV4(); } - - /** Returns an address that represents the loopback address. */ - static AddressV4 loopback () - { return AddressV4 (0x7f000001); } - - /** Returns an address that represents the broadcast address. */ - static AddressV4 broadcast () - { return AddressV4 (0xffffffff); } - - /** Returns the broadcast address for the specified address. */ - static AddressV4 broadcast (AddressV4 const& address); - - /** Returns the broadcast address corresponding to the address and mask. */ - static AddressV4 broadcast ( - AddressV4 const& address, AddressV4 const& mask); - - /** Returns `true` if this is a broadcast address. */ - bool is_broadcast () const - { return *this == broadcast (*this); } - - /** Returns the address class for the given address. - @note Class 'D' represents multicast addresses (224.*.*.*). - */ - static char get_class (AddressV4 const& address); - - /** Returns the netmask for the address class or address. */ - /** @{ */ - static AddressV4 netmask (char address_class); - static AddressV4 netmask (AddressV4 const& v); - /** @} */ - - /** Arithmetic comparison. */ - /** @{ */ - friend bool operator== (AddressV4 const& lhs, AddressV4 const& rhs) - { return lhs.value == rhs.value; } - friend bool operator< (AddressV4 const& lhs, AddressV4 const& rhs) - { return lhs.value < rhs.value; } - - friend bool operator!= (AddressV4 const& lhs, AddressV4 const& rhs) - { return ! (lhs == rhs); } - friend bool operator> (AddressV4 const& lhs, AddressV4 const& rhs) - { return rhs < lhs; } - friend bool operator<= (AddressV4 const& lhs, AddressV4 const& rhs) - { return ! (lhs > rhs); } - friend bool operator>= (AddressV4 const& lhs, AddressV4 const& rhs) - { return ! (rhs > lhs); } - /** @} */ - - /** Array indexing for reading and writing indiviual octets. */ - /** @{ */ - template - class Proxy - { - public: - using Pointer = typename std::conditional < - IsConst, std::uint32_t const*, std::uint32_t*>::type; - - Proxy (int shift, Pointer value) - : m_shift (shift) - , m_value (value) - { - } - - operator std::uint8_t() const - { - return ((*m_value)>>m_shift) & 0xff; - } - - template - Proxy& operator= (IntegralType v) - { - (*m_value) = - ( (*m_value) & (~((0xff)< operator[] (std::size_t index) const; - Proxy operator[] (std::size_t index); - /** @} */ - - /** The value as a 32 bit unsigned. */ - std::uint32_t value; -}; - -//------------------------------------------------------------------------------ - -/** Returns `true` if this is a loopback address. */ -bool is_loopback (AddressV4 const& addr); - -/** Returns `true` if the address is unspecified. */ -bool is_unspecified (AddressV4 const& addr); - -/** Returns `true` if the address is a multicast address. */ -bool is_multicast (AddressV4 const& addr); +using AddressV4 = boost::asio::ip::address_v4; /** Returns `true` if the address is a private unroutable address. */ bool is_private (AddressV4 const& addr); @@ -163,42 +39,12 @@ bool is_private (AddressV4 const& addr); /** Returns `true` if the address is a public routable address. */ bool is_public (AddressV4 const& addr); -//------------------------------------------------------------------------------ - -/** Returns the address represented as a string. */ -std::string to_string (AddressV4 const& addr); - -/** Output stream conversion. */ -template -OutputStream& operator<< (OutputStream& os, AddressV4 const& addr) - { return os << to_string (addr); } - -/** Input stream conversion. */ -std::istream& operator>> (std::istream& is, AddressV4& addr); +/** Returns the address class for the given address. + @note Class 'D' represents multicast addresses (224.*.*.*). +*/ +char get_class (AddressV4 const& address); } - -template -struct is_contiguously_hashable - : public std::integral_constant -{ - explicit is_contiguously_hashable() = default; -}; - -} - -//------------------------------------------------------------------------------ - -namespace std { -/** std::hash support. */ -template <> -struct hash -{ - explicit hash() = default; - - std::size_t operator() (beast::IP::AddressV4 const& addr) const - { return addr.value; } -}; } #endif diff --git a/src/ripple/beast/net/IPAddressV6.h b/src/ripple/beast/net/IPAddressV6.h index c655d1088b..9e541a5000 100644 --- a/src/ripple/beast/net/IPAddressV6.h +++ b/src/ripple/beast/net/IPAddressV6.h @@ -26,45 +26,12 @@ #include #include #include +#include namespace beast { namespace IP { -/** Represents a version 4 IP address. */ -struct AddressV6 -{ - explicit AddressV6() = default; - - // VFALCO TODO - - /** Arithmetic comparison. */ - /** @{ */ - friend bool operator== (AddressV6 const&, AddressV6 const&) - { assert(false); return false; } - friend bool operator< (AddressV6 const&, AddressV6 const&) - { assert(false); return false; } - - friend bool operator!= (AddressV6 const& lhs, AddressV6 const& rhs) - { return ! (lhs == rhs); } - friend bool operator> (AddressV6 const& lhs, AddressV6 const& rhs) - { return rhs < lhs; } - friend bool operator<= (AddressV6 const& lhs, AddressV6 const& rhs) - { return ! (lhs > rhs); } - friend bool operator>= (AddressV6 const& lhs, AddressV6 const& rhs) - { return ! (rhs > lhs); } - /** @} */ -}; - -//------------------------------------------------------------------------------ - -/** Returns `true` if this is a loopback address. */ -bool is_loopback (AddressV6 const& addr); - -/** Returns `true` if the address is unspecified. */ -bool is_unspecified (AddressV6 const& addr); - -/** Returns `true` if the address is a multicast address. */ -bool is_multicast (AddressV6 const& addr); +using AddressV6 = boost::asio::ip::address_v6; /** Returns `true` if the address is a private unroutable address. */ bool is_private (AddressV6 const& addr); @@ -72,41 +39,7 @@ bool is_private (AddressV6 const& addr); /** Returns `true` if the address is a public routable address. */ bool is_public (AddressV6 const& addr); -//------------------------------------------------------------------------------ - -template -void -hash_append(Hasher&, AddressV6 const&) -{ - assert(false); } - -/** Returns the address represented as a string. */ -std::string to_string (AddressV6 const& addr); - -/** Output stream conversion. */ -template -OutputStream& operator<< (OutputStream& os, AddressV6 const& addr) - { return os << to_string (addr); } - -/** Input stream conversion. */ -std::istream& operator>> (std::istream& is, AddressV6& addr); - -} -} - -//------------------------------------------------------------------------------ - -namespace std { -/** std::hash support. */ -template <> -struct hash -{ - explicit hash() = default; - - std::size_t operator() (beast::IP::AddressV6 const& addr) const - { assert(false); return 0; } -}; } #endif diff --git a/src/ripple/beast/net/IPEndpoint.h b/src/ripple/beast/net/IPEndpoint.h index 6803629175..e4ced026d4 100644 --- a/src/ripple/beast/net/IPEndpoint.h +++ b/src/ripple/beast/net/IPEndpoint.h @@ -48,7 +48,6 @@ public: */ static std::pair from_string_checked (std::string const& s); static Endpoint from_string (std::string const& s); - static Endpoint from_string_altform (std::string const& s); /** Returns a string representing the endpoint. */ std::string to_string () const; @@ -71,9 +70,9 @@ public: { return m_addr.is_v4(); } bool is_v6 () const { return m_addr.is_v6(); } - AddressV4 const& to_v4 () const + AddressV4 const to_v4 () const { return m_addr.to_v4 (); } - AddressV6 const& to_v6 () const + AddressV6 const to_v6 () const { return m_addr.to_v6 (); } /** @} */ diff --git a/src/ripple/beast/net/detail/Parse.h b/src/ripple/beast/net/detail/Parse.h deleted file mode 100644 index 33c72e25ae..0000000000 --- a/src/ripple/beast/net/detail/Parse.h +++ /dev/null @@ -1,103 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of Beast: https://github.com/vinniefalco/Beast - Copyright 2013, Vinnie Falco - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef BEAST_NET_DETAIL_PARSE_H_INCLUDED -#define BEAST_NET_DETAIL_PARSE_H_INCLUDED - -#include -#include - -namespace beast { -namespace IP { - -namespace detail { - -/** Require and consume the specified character from the input. - @return `true` if the character matched. -*/ -template -bool expect(InputStream& is, char v) -{ - char c; - if (is.get(c) && v == c) - return true; - is.unget(); - is.setstate (std::ios_base::failbit); - return false; -} - -/** Require and consume whitespace from the input. - @return `true` if the character matched. -*/ -template -bool expect_whitespace (InputStream& is) -{ - char c; - if (is.get(c) && isspace(static_cast(c))) - return true; - is.unget(); - is.setstate (std::ios_base::failbit); - return false; -} - -/** Used to disambiguate 8-bit integers from characters. */ -template -struct integer_holder -{ - IntType* pi; - explicit integer_holder (IntType& i) - : pi (&i) - { - } - template - IntType& operator= (OtherIntType o) const - { - *pi = o; - return *pi; - } -}; - -/** Parse 8-bit unsigned integer. */ -template -InputStream& operator>> (InputStream& is, integer_holder const& i) -{ - std::uint16_t v; - is >> v; - if (! (v>=0 && v<=255)) - { - is.setstate (std::ios_base::failbit); - return is; - } - i = std::uint8_t(v); - return is; -} - -/** Free function for template argument deduction. */ -template -integer_holder integer (IntType& i) -{ - return integer_holder (i); -} - -} - -} -} - -#endif diff --git a/src/ripple/beast/net/impl/IPAddressConversion.cpp b/src/ripple/beast/net/impl/IPAddressConversion.cpp index c6278da652..3e6193daed 100644 --- a/src/ripple/beast/net/impl/IPAddressConversion.cpp +++ b/src/ripple/beast/net/impl/IPAddressConversion.cpp @@ -24,43 +24,22 @@ namespace IP { Endpoint from_asio (boost::asio::ip::address const& address) { - if (address.is_v4 ()) - { - boost::asio::ip::address_v4::bytes_type const bytes ( - address.to_v4().to_bytes()); - return Endpoint (AddressV4 ( - bytes [0], bytes [1], bytes [2], bytes [3])); - } - - // VFALCO TODO IPv6 support - assert(false); - return Endpoint(); + return Endpoint {address}; } Endpoint from_asio (boost::asio::ip::tcp::endpoint const& endpoint) { - return from_asio (endpoint.address()).at_port (endpoint.port()); + return Endpoint {endpoint.address(), endpoint.port()}; } boost::asio::ip::address to_asio_address (Endpoint const& endpoint) { - if (endpoint.address().is_v4()) - { - return boost::asio::ip::address ( - boost::asio::ip::address_v4 ( - endpoint.address().to_v4().value)); - } - - // VFALCO TODO IPv6 support - assert(false); - return boost::asio::ip::address ( - boost::asio::ip::address_v6 ()); + return endpoint.address(); } boost::asio::ip::tcp::endpoint to_asio_endpoint (Endpoint const& endpoint) { - return boost::asio::ip::tcp::endpoint ( - to_asio_address (endpoint), endpoint.port()); + return boost::asio::ip::tcp::endpoint {endpoint.address(), endpoint.port()}; } } diff --git a/src/ripple/beast/net/impl/IPAddressV4.cpp b/src/ripple/beast/net/impl/IPAddressV4.cpp index ed75b0bafd..d437d7b6e2 100644 --- a/src/ripple/beast/net/impl/IPAddressV4.cpp +++ b/src/ripple/beast/net/impl/IPAddressV4.cpp @@ -21,7 +21,6 @@ #endif #include -#include #include #include @@ -29,154 +28,26 @@ namespace beast { namespace IP { -AddressV4::AddressV4 () - : value (0) -{ -} - -AddressV4::AddressV4 (std::uint32_t value_) - : value (value_) -{ -} - -AddressV4::AddressV4 (std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d) - : value ((a<<24)|(b<<16)|(c<<8)|d) -{ -} - -std::pair AddressV4::from_string (std::string const& s) -{ - std::stringstream is (s); - AddressV4 addr; - is >> addr; - if (! is.fail() && is.rdbuf()->in_avail() == 0) - return std::make_pair (addr, true); - return std::make_pair (AddressV4 (), false); -} - -AddressV4 AddressV4::broadcast (AddressV4 const& address) -{ - return broadcast (address, netmask (address)); -} - -AddressV4 AddressV4::broadcast ( - AddressV4 const& address, AddressV4 const& mask) -{ - return AddressV4 (address.value | (mask.value ^ 0xffffffff)); -} - -char AddressV4::get_class (AddressV4 const& addr) -{ - static char const* table = "AAAABBCD"; - return table [(addr.value & 0xE0000000) >> 29]; -} - -AddressV4 AddressV4::netmask (char address_class) -{ - switch (address_class) - { - case 'A': return AddressV4 (0xff000000); - case 'B': return AddressV4 (0xffff0000); - case 'C': return AddressV4 (0xffffff00); - case 'D': - default: - break; - } - return AddressV4 (0xffffffff); -} - -AddressV4 AddressV4::netmask (AddressV4 const& addr) -{ - return netmask (get_class (addr)); -} - -AddressV4::Proxy AddressV4::operator[] (std::size_t index) const -{ - switch (index) - { - default: - throw std::out_of_range ("bad array index"); - case 0: return Proxy (24, &value); - case 1: return Proxy (16, &value); - case 2: return Proxy ( 8, &value); - case 3: return Proxy ( 0, &value); - }; -}; - -AddressV4::Proxy AddressV4::operator[] (std::size_t index) -{ - switch (index) - { - default: - throw std::out_of_range ("bad array index"); - case 0: return Proxy (24, &value); - case 1: return Proxy (16, &value); - case 2: return Proxy ( 8, &value); - case 3: return Proxy ( 0, &value); - }; -}; - -//------------------------------------------------------------------------------ - -bool is_loopback (AddressV4 const& addr) -{ - return (addr.value & 0xff000000) == 0x7f000000; -} - -bool is_unspecified (AddressV4 const& addr) -{ - return addr.value == 0; -} - -bool is_multicast (AddressV4 const& addr) -{ - return (addr.value & 0xf0000000) == 0xe0000000; -} - bool is_private (AddressV4 const& addr) { return - ((addr.value & 0xff000000) == 0x0a000000) || // Prefix /8, 10. #.#.# - ((addr.value & 0xfff00000) == 0xac100000) || // Prefix /12 172. 16.#.# - 172.31.#.# - ((addr.value & 0xffff0000) == 0xc0a80000) || // Prefix /16 192.168.#.# - is_loopback (addr); + ((addr.to_ulong() & 0xff000000) == 0x0a000000) || // Prefix /8, 10. #.#.# + ((addr.to_ulong() & 0xfff00000) == 0xac100000) || // Prefix /12 172. 16.#.# - 172.31.#.# + ((addr.to_ulong() & 0xffff0000) == 0xc0a80000) || // Prefix /16 192.168.#.# + addr.is_loopback(); } bool is_public (AddressV4 const& addr) { return ! is_private (addr) && - ! is_multicast (addr); + ! addr.is_multicast(); } -//------------------------------------------------------------------------------ - -std::string to_string (AddressV4 const& addr) +char get_class (AddressV4 const& addr) { - std::string s; - s.reserve (15); - s = - std::to_string (addr[0]) + "." + - std::to_string (addr[1]) + "." + - std::to_string (addr[2]) + "." + - std::to_string (addr[3]); - return s; -} - -std::istream& operator>> (std::istream& is, AddressV4& addr) -{ - std::uint8_t octet [4]; - is >> IP::detail::integer (octet [0]); - for (int i = 1; i < 4; ++i) - { - if (!is || !IP::detail::expect(is, '.')) - return is; - is >> IP::detail::integer (octet [i]); - if (!is) - return is; - } - addr = AddressV4 (octet[0], octet[1], octet[2], octet[3]); - return is; + static char const* table = "AAAABBCD"; + return table [(addr.to_ulong() & 0xE0000000) >> 29]; } } diff --git a/src/ripple/beast/net/impl/IPAddressV6.cpp b/src/ripple/beast/net/impl/IPAddressV6.cpp index 1a286488ef..0e8d88714c 100644 --- a/src/ripple/beast/net/impl/IPAddressV6.cpp +++ b/src/ripple/beast/net/impl/IPAddressV6.cpp @@ -21,62 +21,25 @@ #endif #include +#include namespace beast { namespace IP { -//------------------------------------------------------------------------------ - -bool is_loopback (AddressV6 const&) +bool is_private (AddressV6 const& addr) { - // VFALCO TODO - assert(false); - return false; + return ((addr.to_bytes()[0] & 0xfd) || // TODO fc00::/8 too ? + (addr.is_v4_mapped() && is_private(addr.to_v4())) ); } -bool is_unspecified (AddressV6 const&) +bool is_public (AddressV6 const& addr) { - // VFALCO TODO - assert(false); - return false; + // TODO is this correct? + return + ! is_private (addr) && + ! addr.is_multicast(); } -bool is_multicast (AddressV6 const&) -{ - // VFALCO TODO - assert(false); - return false; -} - -bool is_private (AddressV6 const&) -{ - // VFALCO TODO - assert(false); - return false; -} - -bool is_public (AddressV6 const&) -{ - // VFALCO TODO - assert(false); - return false; -} - -//------------------------------------------------------------------------------ - -std::string to_string (AddressV6 const&) -{ - // VFALCO TODO - assert(false); - return ""; -} - -std::istream& operator>> (std::istream& is, AddressV6&) -{ - // VFALCO TODO - assert(false); - return is; -} } } diff --git a/src/ripple/beast/net/impl/IPEndpoint.cpp b/src/ripple/beast/net/impl/IPEndpoint.cpp index 9f3cec9f80..d757abac41 100644 --- a/src/ripple/beast/net/impl/IPEndpoint.cpp +++ b/src/ripple/beast/net/impl/IPEndpoint.cpp @@ -21,7 +21,6 @@ #endif #include -#include namespace beast { namespace IP { @@ -44,7 +43,7 @@ std::pair Endpoint::from_string_checked (std::string const& s) is >> endpoint; if (! is.fail() && is.rdbuf()->in_avail() == 0) return std::make_pair (endpoint, true); - return std::make_pair (Endpoint (), false); + return std::make_pair (Endpoint {}, false); } Endpoint Endpoint::from_string (std::string const& s) @@ -53,69 +52,26 @@ Endpoint Endpoint::from_string (std::string const& s) from_string_checked (s)); if (result.second) return result.first; - return Endpoint(); -} - -// VFALCO NOTE This is a hack to support legacy data format -// -Endpoint Endpoint::from_string_altform (std::string const& s) -{ - // Accept the regular form if it parses - { - Endpoint ep (Endpoint::from_string (s)); - if (! is_unspecified (ep)) - return ep; - } - - // Now try the alt form - std::stringstream is (s); - - AddressV4 v4; - is >> v4; - if (! is.fail()) - { - Endpoint ep (v4); - - if (is.rdbuf()->in_avail()>0) - { - if (! IP::detail::expect_whitespace (is)) - return Endpoint(); - - while (is.rdbuf()->in_avail()>0) - { - char c; - is.get(c); - if (!isspace (static_cast(c))) - { - is.unget(); - break; - } - } - - Port port; - is >> port; - if (is.fail()) - return Endpoint(); - - return ep.at_port (port); - } - else - { - // Just an address with no port - return ep; - } - } - - // Could be V6 here... - - return Endpoint(); + return Endpoint {}; } std::string Endpoint::to_string () const { - std::string s (address ().to_string ()); - if (port() != 0) - s = s + ":" + std::to_string (port()); + std::string s; + s.reserve( + (address().is_v6() ? INET6_ADDRSTRLEN-1 : 15) + + (port() == 0 ? 0 : 6 + (address().is_v6() ? 2 : 0))); + + if (port() != 0 && address().is_v6()) + s += '['; + s += address ().to_string(); + if (port()) + { + if (address().is_v6()) + s += ']'; + s += ":" + std::to_string (port()); + } + return s; } @@ -138,34 +94,88 @@ bool operator< (Endpoint const& lhs, Endpoint const& rhs) std::istream& operator>> (std::istream& is, Endpoint& endpoint) { - // VFALCO TODO Support ipv6! + std::string addrStr; + // valid addresses only need INET6_ADDRSTRLEN-1 chars, but allow the extra + // char to check for invalid lengths + addrStr.reserve(INET6_ADDRSTRLEN); + char i {0}; + char readTo {0}; + is.get(i); + if (i == '[') // we are an IPv6 endpoint + readTo = ']'; + else + addrStr+=i; - Address addr; - is >> addr; - if (is.fail()) - return is; - - if (is.rdbuf()->in_avail()>0) + while (is && is.rdbuf()->in_avail() > 0 && is.get(i)) { - char c; - is.get(c); - if (c != ':') + // NOTE: There is a legacy data format + // that allowed space to be used as address / port separator + // so we continue to honor that here by assuming we are at the end + // of the address portion if we hit a space (or the separator + // we were expecting to see) + if (isspace(static_cast(i)) || (readTo && i == readTo)) + break; + + if ((i == '.') || + (i >= '0' && i <= ':') || + (i >= 'a' && i <= 'f') || + (i >= 'A' && i <= 'F')) + { + addrStr+=i; + + // don't exceed a reasonable length... + if ( addrStr.size() == INET6_ADDRSTRLEN || + (readTo && readTo == ':' && addrStr.size() > 15)) + { + is.setstate (std::ios_base::failbit); + return is; + } + + if (! readTo && (i == '.' || i == ':')) + { + // if we see a dot first, must be IPv4 + // otherwise must be non-bracketed IPv6 + readTo = (i == '.') ? ':' : ' '; + } + } + else // invalid char { is.unget(); - endpoint = Endpoint (addr); + is.setstate (std::ios_base::failbit); return is; } + } + if (readTo == ']' && is.rdbuf()->in_avail() > 0) + { + is.get(i); + if (! (isspace(static_cast(i)) || i == ':')) + { + is.unget(); + is.setstate (std::ios_base::failbit); + return is; + } + } + + boost::system::error_code ec; + auto addr = Address::from_string(addrStr, ec); + if (ec) + { + is.setstate (std::ios_base::failbit); + return is; + } + + if (is.rdbuf()->in_avail() > 0) + { Port port; is >> port; if (is.fail()) return is; - endpoint = Endpoint (addr, port); - return is; } + else + endpoint = Endpoint (addr); - endpoint = Endpoint (addr); return is; } diff --git a/src/ripple/core/Config.h b/src/ripple/core/Config.h index 6cc3593956..5157a3d515 100644 --- a/src/ripple/core/Config.h +++ b/src/ripple/core/Config.h @@ -26,7 +26,6 @@ #include #include #include -#include // VFALCO FIX: This include should not be here #include // VFALCO FIX: This include should not be here #include #include @@ -174,8 +173,7 @@ public: std::size_t WORKERS = 0; // These override the command line client settings - boost::optional rpc_ip; - boost::optional rpc_port; + boost::optional rpc_ip; std::unordered_set> features; diff --git a/src/ripple/net/impl/RPCCall.cpp b/src/ripple/net/impl/RPCCall.cpp index 11eff54446..a91bbd6d95 100644 --- a/src/ripple/net/impl/RPCCall.cpp +++ b/src/ripple/net/impl/RPCCall.cpp @@ -1355,9 +1355,10 @@ rpcClient(std::vector const& args, } if (config.rpc_ip) - setup.client.ip = config.rpc_ip->to_string(); - if (config.rpc_port) - setup.client.port = *config.rpc_port; + { + setup.client.ip = config.rpc_ip->address().to_string(); + setup.client.port = config.rpc_ip->port(); + } Json::Value jvParams (Json::arrayValue); diff --git a/src/ripple/overlay/impl/OverlayImpl.cpp b/src/ripple/overlay/impl/OverlayImpl.cpp index 8ea11175ce..679582425a 100644 --- a/src/ripple/overlay/impl/OverlayImpl.cpp +++ b/src/ripple/overlay/impl/OverlayImpl.cpp @@ -1048,11 +1048,9 @@ setup_Overlay (BasicConfig const& config) set (ip, "public_ip", section); if (! ip.empty ()) { - bool valid; - std::tie (setup.public_ip, valid) = - beast::IP::Address::from_string (ip); - if (! valid || ! setup.public_ip.is_v4() || - is_private (setup.public_ip)) + boost::system::error_code ec; + setup.public_ip = beast::IP::Address::from_string (ip, ec); + if (ec || beast::IP::is_private (setup.public_ip)) Throw ("Configured public IP is invalid"); } return setup; diff --git a/src/ripple/overlay/impl/PeerImp.cpp b/src/ripple/overlay/impl/PeerImp.cpp index 33c03ad042..22ac787cb1 100644 --- a/src/ripple/overlay/impl/PeerImp.cpp +++ b/src/ripple/overlay/impl/PeerImp.cpp @@ -1019,37 +1019,75 @@ PeerImp::onMessage (std::shared_ptr const& m) std::vector endpoints; - endpoints.reserve (m->endpoints().size()); - - for (int i = 0; i < m->endpoints ().size (); ++i) + if (m->endpoints_v2().size()) { - PeerFinder::Endpoint endpoint; - protocol::TMEndpoint const& tm (m->endpoints(i)); - - // hops - endpoint.hops = tm.hops(); - - // ipv4 - if (endpoint.hops > 0) + endpoints.reserve (m->endpoints_v2().size()); + for (auto const& tm : m->endpoints_v2 ()) { - in_addr addr; - addr.s_addr = tm.ipv4().ipv4(); - beast::IP::AddressV4 v4 (ntohl (addr.s_addr)); - endpoint.address = beast::IP::Endpoint (v4, tm.ipv4().ipv4port ()); - } - else - { - // This Endpoint describes the peer we are connected to. - // We will take the remote address seen on the socket and - // store that in the IP::Endpoint. If this is the first time, - // then we'll verify that their listener can receive incoming - // by performing a connectivity test. - // - endpoint.address = remote_address_.at_port ( - tm.ipv4().ipv4port ()); - } + // these endpoint strings support ipv4 and ipv6 + auto result = beast::IP::Endpoint::from_string_checked(tm.endpoint()); + if (! result.second) + { + JLOG(p_journal_.error()) << + "failed to parse incoming endpoint: {" << + tm.endpoint() << "}"; + continue; + } - endpoints.push_back (endpoint); + // If hops == 0, this Endpoint describes the peer we are connected + // to -- in that case, we take the remote address seen on the + // socket and store that in the IP::Endpoint. If this is the first + // time, then we'll verify that their listener can receive incoming + // by performing a connectivity test. if hops > 0, then we just + // take the address/port we were given + + endpoints.emplace_back( + tm.hops() > 0 ? + result.first : + remote_address_.at_port(result.first.port()), + tm.hops()); + JLOG(p_journal_.trace()) << + "got v2 EP: " << endpoints.back().address << + ", hops = " << endpoints.back().hops; + } + } + else + { + // this branch can be removed once the entire network is operating with + // endpoint_v2() items (strings) + endpoints.reserve (m->endpoints().size()); + for (int i = 0; i < m->endpoints ().size (); ++i) + { + PeerFinder::Endpoint endpoint; + protocol::TMEndpoint const& tm (m->endpoints(i)); + + // hops + endpoint.hops = tm.hops(); + + // ipv4 + if (endpoint.hops > 0) + { + in_addr addr; + addr.s_addr = tm.ipv4().ipv4(); + beast::IP::AddressV4 v4 (ntohl (addr.s_addr)); + endpoint.address = beast::IP::Endpoint (v4, tm.ipv4().ipv4port ()); + } + else + { + // This Endpoint describes the peer we are connected to. + // We will take the remote address seen on the socket and + // store that in the IP::Endpoint. If this is the first time, + // then we'll verify that their listener can receive incoming + // by performing a connectivity test. + // + endpoint.address = remote_address_.at_port ( + tm.ipv4().ipv4port ()); + } + endpoints.push_back (endpoint); + JLOG(p_journal_.trace()) << + "got v1 EP: " << endpoints.back().address << + ", hops = " << endpoints.back().hops; + } } if (! endpoints.empty()) diff --git a/src/ripple/overlay/impl/PeerImp.h b/src/ripple/overlay/impl/PeerImp.h index 67e118a444..3e3377e88b 100644 --- a/src/ripple/overlay/impl/PeerImp.h +++ b/src/ripple/overlay/impl/PeerImp.h @@ -514,16 +514,24 @@ PeerImp::sendEndpoints (FwdIt first, FwdIt last) for (;first != last; ++first) { auto const& ep = *first; + // eventually remove endpoints and just keep endpoints_v2 + // (once we are sure the entire network understands endpoints_v2) protocol::TMEndpoint& tme (*tm.add_endpoints()); if (ep.address.is_v4()) tme.mutable_ipv4()->set_ipv4( - beast::toNetworkByteOrder (ep.address.to_v4().value)); + beast::toNetworkByteOrder ( + ep.address.to_v4().to_ulong())); else tme.mutable_ipv4()->set_ipv4(0); tme.mutable_ipv4()->set_ipv4port (ep.address.port()); tme.set_hops (ep.hops); + + // add v2 endpoints (strings) + auto& tme2 (*tm.add_endpoints_v2()); + tme2.set_endpoint(ep.address.to_string()); + tme2.set_hops (ep.hops); } - tm.set_version (1); + tm.set_version (2); send (std::make_shared (tm, protocol::mtENDPOINTS)); } diff --git a/src/ripple/overlay/impl/TMHello.cpp b/src/ripple/overlay/impl/TMHello.cpp index c3fca6d81a..e8bf4a85c8 100644 --- a/src/ripple/overlay/impl/TMHello.cpp +++ b/src/ripple/overlay/impl/TMHello.cpp @@ -114,19 +114,14 @@ buildHello ( TokenType::NodePublic, app.nodeIdentity().first)); h.set_nodeproof (sig.data(), sig.size()); - // h.set_ipv4port (portNumber); // ignored now h.set_testnet (false); - if (remote.is_v4()) + if (beast::IP::is_public (remote)) { - auto addr = remote.to_v4 (); - if (is_public (addr)) - { - // Connection is to a public IP - h.set_remote_ip (addr.value); - if (public_ip != beast::IP::Address()) - h.set_local_ip (public_ip.to_v4().value); - } + // Connection is to a public IP + h.set_remote_ip_str (remote.to_string()); + if (! public_ip.is_unspecified()) + h.set_local_ip_str (public_ip.to_string()); } // We always advertise ourselves as private in the HELLO message. This @@ -174,13 +169,11 @@ appendHello (boost::beast::http::fields& h, h.insert ("Previous-Ledger", boost::beast::detail::base64_encode ( hello.ledgerprevious())); - if (hello.has_local_ip()) - h.insert ("Local-IP", beast::IP::to_string ( - beast::IP::AddressV4(hello.local_ip()))); + if (hello.has_local_ip_str()) + h.insert ("Local-IP", hello.local_ip_str()); if (hello.has_remote_ip()) - h.insert ("Remote-IP", beast::IP::to_string ( - beast::IP::AddressV4(hello.remote_ip()))); + h.insert ("Remote-IP", hello.remote_ip_str()); } std::vector @@ -306,14 +299,16 @@ parseHello (bool request, boost::beast::http::fields const& h, beast::Journal jo auto const iter = h.find ("Local-IP"); if (iter != h.end()) { - bool valid; - beast::IP::Address address; - std::tie (address, valid) = - beast::IP::Address::from_string (iter->value().to_string()); - if (!valid) + boost::system::error_code ec; + auto address = + beast::IP::Address::from_string (iter->value().to_string(), ec); + if (ec) + { + JLOG(journal.warn()) << "invalid Local-IP: " + << iter->value().to_string(); return boost::none; - if (address.is_v4()) - hello.set_local_ip(address.to_v4().value); + } + hello.set_local_ip_str(address.to_string()); } } @@ -321,14 +316,16 @@ parseHello (bool request, boost::beast::http::fields const& h, beast::Journal jo auto const iter = h.find ("Remote-IP"); if (iter != h.end()) { - bool valid; - beast::IP::Address address; - std::tie (address, valid) = - beast::IP::Address::from_string (iter->value().to_string()); - if (!valid) + boost::system::error_code ec; + auto address = + beast::IP::Address::from_string (iter->value().to_string(), ec); + if (ec) + { + JLOG(journal.warn()) << "invalid Remote-IP: " + << iter->value().to_string(); return boost::none; - if (address.is_v4()) - hello.set_remote_ip(address.to_v4().value); + } + hello.set_remote_ip_str(address.to_string()); } } @@ -412,33 +409,50 @@ verifyHello (protocol::TMHello const& h, return boost::none; } - if (h.has_local_ip () && - is_public (remote) && - remote.is_v4 () && - (remote.to_v4().value != h.local_ip ())) + if (h.has_local_ip_str () && + is_public (remote)) { - // Remote asked us to confirm connection is from - // correct IP - JLOG(journal.info()) << - "Hello: Disconnect: Peer IP is " << - beast::IP::to_string (remote.to_v4()) - << " not " << - beast::IP::to_string (beast::IP::AddressV4 (h.local_ip())); - return boost::none; + boost::system::error_code ec; + auto local_ip = + beast::IP::Address::from_string (h.local_ip_str(), ec); + if (ec) + { + JLOG(journal.warn()) << "invalid local-ip: " << h.local_ip_str(); + return boost::none; + } + + if (remote.address() != local_ip) + { + // Remote asked us to confirm connection is from correct IP + JLOG(journal.info()) << + "Hello: Disconnect: Peer IP is " << remote.address().to_string() + << " not " << local_ip.to_string(); + return boost::none; + } } - if (h.has_remote_ip() && is_public (remote) && - (public_ip != beast::IP::Address()) && - (h.remote_ip() != public_ip.to_v4().value)) + if (h.has_remote_ip_str () && + is_public (remote) && + (! beast::IP::is_unspecified(public_ip))) { - // We know our public IP and peer reports connection - // from some other IP - JLOG(journal.info()) << - "Hello: Disconnect: Our IP is " << - beast::IP::to_string (public_ip.to_v4()) - << " not " << - beast::IP::to_string (beast::IP::AddressV4 (h.remote_ip())); - return boost::none; + boost::system::error_code ec; + auto remote_ip = + beast::IP::Address::from_string (h.remote_ip_str(), ec); + if (ec) + { + JLOG(journal.warn()) << "invalid remote-ip: " << h.remote_ip_str(); + return boost::none; + } + + if (remote_ip != public_ip) + { + // We know our public IP and peer reports connection from some + // other IP + JLOG(journal.info()) << + "Hello: Disconnect: Our IP is " << public_ip.to_string() + << " not " << remote_ip.to_string(); + return boost::none; + } } return publicKey; diff --git a/src/ripple/peerfinder/impl/Logic.h b/src/ripple/peerfinder/impl/Logic.h index 34734311dc..ff929649d0 100644 --- a/src/ripple/peerfinder/impl/Logic.h +++ b/src/ripple/peerfinder/impl/Logic.h @@ -618,8 +618,15 @@ public: { Endpoint ep; ep.hops = 0; + // we use the unspecified (0) address here because the value is + // irrelevant to recipients. When peers receive an endpoint + // with 0 hops, they use the socket remote_addr instead of the + // value in the message. Furthermore, since the address value + // is ignored, the type/version (ipv4 vs ipv6) doesn't matter + // either. ipv6 has a slightly more compact string + // representation of 0, so use that for self entries. ep.address = beast::IP::Endpoint ( - beast::IP::AddressV4 ()).at_port ( + beast::IP::AddressV6 ()).at_port ( config_.listeningPort); for (auto& t : targets) t.insert (ep); diff --git a/src/ripple/peerfinder/impl/SourceStrings.cpp b/src/ripple/peerfinder/impl/SourceStrings.cpp index 39337fabcb..dafce633f4 100644 --- a/src/ripple/peerfinder/impl/SourceStrings.cpp +++ b/src/ripple/peerfinder/impl/SourceStrings.cpp @@ -48,7 +48,7 @@ public: { beast::IP::Endpoint ep (beast::IP::Endpoint::from_string (m_strings [i])); if (is_unspecified (ep)) - ep = beast::IP::Endpoint::from_string_altform (m_strings [i]); + ep = beast::IP::Endpoint::from_string (m_strings [i]); if (! is_unspecified (ep)) results.addresses.push_back (ep); } diff --git a/src/ripple/proto/ripple.proto b/src/ripple/proto/ripple.proto index e14090772b..4f8b9b1172 100644 --- a/src/ripple/proto/ripple.proto +++ b/src/ripple/proto/ripple.proto @@ -89,15 +89,17 @@ message TMHello required bytes nodeProof = 4; optional string fullVersion = 5; optional uint64 netTime = 6; - optional uint32 ipv4Port = 7; + optional uint32 ipv4Port = 7; // NOT USED optional uint32 ledgerIndex = 8; optional bytes ledgerClosed = 9; // our last closed ledger optional bytes ledgerPrevious = 10; // the ledger before the last closed ledger optional bool nodePrivate = 11; // Request to not forward IP. optional TMProofWork proofOfWork = 12; // request/provide proof of work optional bool testNet = 13; // Running as testnet. - optional uint32 local_ip = 14; // our public IP - optional uint32 remote_ip = 15; // IP we see connection from + optional uint32 local_ip = 14; // NOT USED -- our public IP + optional uint32 remote_ip = 15; // NOT USED -- IP we see connection from + optional string local_ip_str = 16; // our public IP + optional string remote_ip_str = 17; // IP we see connection from } // The status of a node in our cluster @@ -234,6 +236,7 @@ message TMIPv4Endpoint required uint32 ipv4Port = 2; } +// this message is obsolete/no longer procesed message TMPeers { repeated TMIPv4Endpoint nodes = 1; @@ -254,6 +257,15 @@ message TMEndpoints required uint32 version = 1; repeated TMEndpoint endpoints = 2; + + // An update to the Endpoint type that uses a string + // to represent endpoints, thus allowing ipv6 or ipv4 addresses + message TMEndpointv2 + { + required string endpoint = 1; + required uint32 hops = 2; + } + repeated TMEndpointv2 endpoints_v2 = 3; }; message TMIndexedObject diff --git a/src/ripple/rpc/impl/Role.cpp b/src/ripple/rpc/impl/Role.cpp index 512a66f80d..b515ef08b3 100644 --- a/src/ripple/rpc/impl/Role.cpp +++ b/src/ripple/rpc/impl/Role.cpp @@ -41,7 +41,7 @@ ipAllowed (beast::IP::Address const& remoteIp, std::vector const& adminIp) { return std::find_if (adminIp.begin (), adminIp.end (), - [&remoteIp](beast::IP::Address const& ip) { return ip.is_any () || + [&remoteIp](beast::IP::Address const& ip) { return ip.is_unspecified () || ip == remoteIp; }) != adminIp.end (); } diff --git a/src/ripple/rpc/impl/ServerHandlerImp.cpp b/src/ripple/rpc/impl/ServerHandlerImp.cpp index cc3ddf6452..a7bcaee463 100644 --- a/src/ripple/rpc/impl/ServerHandlerImp.cpp +++ b/src/ripple/rpc/impl/ServerHandlerImp.cpp @@ -1041,10 +1041,11 @@ setup_Client (ServerHandler::Setup& setup) return; setup.client.secure = iter->protocol.count("https") > 0; - setup.client.ip = iter->ip.to_string(); - // VFALCO HACK! to make localhost work - if (setup.client.ip == "0.0.0.0") - setup.client.ip = "127.0.0.1"; + setup.client.ip = + beast::IP::is_unspecified(iter->ip) ? + // VFALCO HACK! to make localhost work + (iter->ip.is_v6() ? "::1" : "127.0.0.1") : + iter->ip.to_string(); setup.client.port = iter->port; setup.client.user = iter->user; setup.client.password = iter->password; diff --git a/src/ripple/server/impl/Port.cpp b/src/ripple/server/impl/Port.cpp index 0e7bcea9aa..7848ded24e 100644 --- a/src/ripple/server/impl/Port.cpp +++ b/src/ripple/server/impl/Port.cpp @@ -95,7 +95,7 @@ populate (Section const& section, std::string const& field, std::ostream& log, { if (! allowAllIps) { - log << "0.0.0.0 not allowed'" << + log << addr.first.address() << " not allowed'" << "' for key '" << field << "' in [" << section.name () << "]"; Throw (); @@ -108,8 +108,8 @@ populate (Section const& section, std::string const& field, std::ostream& log, if (has_any && ! ips->empty ()) { - log << "IP specified along with 0.0.0.0 '" << ip << - "' for key '" << field << "' in [" << + log << "IP specified along with " << addr.first.address() << + " '" << ip << "' for key '" << field << "' in [" << section.name () << "]"; Throw (); } diff --git a/src/test/app/ValidatorSite_test.cpp b/src/test/app/ValidatorSite_test.cpp index b46ee196d6..07322fe753 100644 --- a/src/test/app/ValidatorSite_test.cpp +++ b/src/test/app/ValidatorSite_test.cpp @@ -172,21 +172,12 @@ private: while (list2.size () < listSize) list2.push_back (randomValidator()); - - using endpoint_type = boost::asio::ip::tcp::endpoint; - using address_type = boost::asio::ip::address; - - // Use ports of 0 to allow OS selection - endpoint_type ep1{address_type::from_string("127.0.0.1"), 0}; - endpoint_type ep2{address_type::from_string("127.0.0.1"), 0}; - auto const sequence = 1; auto const version = 1; NetClock::time_point const expiration = env.timeKeeper().now() + 3600s; TrustedPublisherServer server1( - ep1, env.app().getIOService(), pubSigningKeys1, manifest1, @@ -196,7 +187,6 @@ private: list1); TrustedPublisherServer server2( - ep2, env.app().getIOService(), pubSigningKeys2, manifest2, @@ -205,14 +195,13 @@ private: version, list2); - std::uint16_t const port1 = server1.local_endpoint().port(); - std::uint16_t const port2 = server2.local_endpoint().port(); - + std::stringstream url1, url2; + url1 << "http://" << server1.local_endpoint() << "/validators"; + url2 << "http://" << server2.local_endpoint() << "/validators"; { // fetch single site - std::vector cfgSites( - {"http://127.0.0.1:" + std::to_string(port1) + "/validators"}); + std::vector cfgSites({ url1.str() }); auto sites = std::make_unique ( env.app().getIOService(), env.app().validators(), journal); @@ -229,9 +218,7 @@ private: } { // fetch multiple sites - std::vector cfgSites({ - "http://127.0.0.1:" + std::to_string(port1) + "/validators", - "http://127.0.0.1:" + std::to_string(port2) + "/validators"}); + std::vector cfgSites({ url1.str(), url2.str() }); auto sites = std::make_unique ( env.app().getIOService(), env.app().validators(), journal); diff --git a/src/test/basics/StringUtilities_test.cpp b/src/test/basics/StringUtilities_test.cpp index d7a0d90491..eebe51f411 100644 --- a/src/test/basics/StringUtilities_test.cpp +++ b/src/test/basics/StringUtilities_test.cpp @@ -77,6 +77,9 @@ public: BEAST_EXPECT(parseUrl (pUrl, "Mixed://domain/path")); BEAST_EXPECT(pUrl.scheme == "mixed"); BEAST_EXPECT(pUrl.path == "/path"); + BEAST_EXPECT(parseUrl (pUrl, "scheme://[::1]:123/path")); + BEAST_EXPECT(*pUrl.port == 123); + BEAST_EXPECT(pUrl.domain == "::1"); } void testToString () diff --git a/src/test/beast/IPEndpointCommon.h b/src/test/beast/IPEndpointCommon.h new file mode 100644 index 0000000000..dc1361654c --- /dev/null +++ b/src/test/beast/IPEndpointCommon.h @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2017 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include + +namespace beast { +namespace IP { + +inline Endpoint randomEP (bool v4 = true) +{ + using namespace ripple; + auto dv4 = []() -> AddressV4::bytes_type { + return {{ + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)) + }}; + }; + auto dv6 = []() -> AddressV6::bytes_type { + return {{ + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)), + static_cast(rand_int(1, UINT8_MAX)) + }}; + }; + return Endpoint { + v4 ? Address { AddressV4 {dv4()} } : Address{ AddressV6 {dv6()} }, + rand_int(1, UINT16_MAX) + }; +} + +} +} diff --git a/src/test/beast/IPEndpoint_test.cpp b/src/test/beast/IPEndpoint_test.cpp index 5e486f8b9e..e671280dc2 100644 --- a/src/test/beast/IPEndpoint_test.cpp +++ b/src/test/beast/IPEndpoint_test.cpp @@ -23,10 +23,12 @@ #endif #include -#include - #include - +#include +#include +#include +#include +#include #include namespace beast { @@ -37,91 +39,111 @@ namespace IP { class IPEndpoint_test : public unit_test::suite { public: - void shouldParseV4 (std::string const& s, std::uint32_t value) + void shouldParseAddrV4 ( + std::string const& s, + std::uint32_t value, + std::string const& normal = "") { - std::pair const result ( - AddressV4::from_string (s)); - - if (BEAST_EXPECT(result.second)) - { - if (BEAST_EXPECT(result.first.value == value)) - { - BEAST_EXPECT(to_string (result.first) == s); - } - } + boost::system::error_code ec; + Address const result {Address::from_string (s, ec)}; + if (! BEAST_EXPECTS(! ec, ec.message())) + return; + if (! BEAST_EXPECTS(result.is_v4(), s + " not v4")) + return; + if (! BEAST_EXPECTS(result.to_v4().to_ulong() == value, + s + " value mismatch")) + return; + BEAST_EXPECTS(result.to_string () == (normal.empty() ? s : normal), + s + " as string"); } - void failParseV4 (std::string const& s) + void failParseAddr (std::string const& s) { - unexpected (AddressV4::from_string (s).second); + boost::system::error_code ec; + auto a = Address::from_string (s, ec); + BEAST_EXPECTS(ec, s + " parses as " + a.to_string()); } void testAddressV4 () { testcase ("AddressV4"); - BEAST_EXPECT(AddressV4().value == 0); - BEAST_EXPECT(is_unspecified (AddressV4())); - BEAST_EXPECT(AddressV4(0x01020304).value == 0x01020304); - BEAST_EXPECT(AddressV4(1, 2, 3, 4).value == 0x01020304); + BEAST_EXPECT(AddressV4{}.to_ulong() == 0); + BEAST_EXPECT(is_unspecified (AddressV4{})); + BEAST_EXPECT(AddressV4{0x01020304}.to_ulong() == 0x01020304); + AddressV4::bytes_type d = {{1,2,3,4}}; + BEAST_EXPECT(AddressV4{d}.to_ulong() == 0x01020304); - unexpected (is_unspecified (AddressV4(1, 2, 3, 4))); + unexpected (is_unspecified (AddressV4{d})); - AddressV4 const v1 (1); - BEAST_EXPECT(AddressV4(v1).value == 1); + AddressV4 const v1 {1}; + BEAST_EXPECT(AddressV4{v1}.to_ulong() == 1); { AddressV4 v; v = v1; - BEAST_EXPECT(v.value == v1.value); + BEAST_EXPECT(v.to_ulong() == v1.to_ulong()); } { AddressV4 v; - v [0] = 1; - v [1] = 2; - v [2] = 3; - v [3] = 4; - BEAST_EXPECT(v.value == 0x01020304); + auto d = v.to_bytes(); + d[0] = 1; + d[1] = 2; + d[2] = 3; + d[3] = 4; + v = AddressV4{d}; + BEAST_EXPECT(v.to_ulong() == 0x01020304); } - BEAST_EXPECT(to_string (AddressV4(0x01020304)) == "1.2.3.4"); + BEAST_EXPECT(AddressV4(0x01020304).to_string() == "1.2.3.4"); - shouldParseV4 ("1.2.3.4", 0x01020304); - shouldParseV4 ("255.255.255.255", 0xffffffff); - shouldParseV4 ("0.0.0.0", 0); + shouldParseAddrV4 ("1.2.3.4", 0x01020304); + shouldParseAddrV4 ("255.255.255.255", 0xffffffff); + shouldParseAddrV4 ("0.0.0.0", 0); - failParseV4 ("."); - failParseV4 (".."); - failParseV4 ("..."); - failParseV4 ("...."); - failParseV4 ("1"); - failParseV4 ("1."); - failParseV4 ("1.2"); - failParseV4 ("1.2."); - failParseV4 ("1.2.3"); - failParseV4 ("1.2.3."); - failParseV4 ("256.0.0.0"); - failParseV4 ("-1.2.3.4"); + failParseAddr ("."); + failParseAddr (".."); + failParseAddr ("..."); + failParseAddr ("...."); +#if BOOST_OS_WINDOWS + // WINDOWS bug in asio - I don't think these should parse + // at all, and in-fact they do not on mac/linux + shouldParseAddrV4 ("1", 0x00000001, "0.0.0.1"); + shouldParseAddrV4 ("1.2", 0x01000002, "1.0.0.2"); + shouldParseAddrV4 ("1.2.3", 0x01020003, "1.2.0.3"); +#else + failParseAddr ("1"); + failParseAddr ("1.2"); + failParseAddr ("1.2.3"); +#endif + failParseAddr ("1."); + failParseAddr ("1.2."); + failParseAddr ("1.2.3."); + failParseAddr ("256.0.0.0"); + failParseAddr ("-1.2.3.4"); } void testAddressV4Proxy () { - testcase ("AddressV4::Proxy"); + testcase ("AddressV4::Bytes"); - AddressV4 v4 (10, 0, 0, 1); - BEAST_EXPECT(v4[0]==10); - BEAST_EXPECT(v4[1]==0); - BEAST_EXPECT(v4[2]==0); - BEAST_EXPECT(v4[3]==1); + AddressV4::bytes_type d1 = {{10,0,0,1}}; + AddressV4 v4 {d1}; + BEAST_EXPECT(v4.to_bytes()[0]==10); + BEAST_EXPECT(v4.to_bytes()[1]==0); + BEAST_EXPECT(v4.to_bytes()[2]==0); + BEAST_EXPECT(v4.to_bytes()[3]==1); BEAST_EXPECT((~((0xff)<<16)) == 0xff00ffff); - v4[1] = 10; - BEAST_EXPECT(v4[0]==10); - BEAST_EXPECT(v4[1]==10); - BEAST_EXPECT(v4[2]==0); - BEAST_EXPECT(v4[3]==1); + auto d2 = v4.to_bytes(); + d2[1] = 10; + v4 = AddressV4{d2}; + BEAST_EXPECT(v4.to_bytes()[0]==10); + BEAST_EXPECT(v4.to_bytes()[1]==10); + BEAST_EXPECT(v4.to_bytes()[2]==0); + BEAST_EXPECT(v4.to_bytes()[3]==1); } //-------------------------------------------------------------------------- @@ -130,76 +152,160 @@ public: { testcase ("Address"); - std::pair result ( - Address::from_string ("1.2.3.4")); - BEAST_EXPECT(result.second); - if (BEAST_EXPECT(result.first.is_v4 ())) - BEAST_EXPECT(result.first.to_v4() == AddressV4 (1, 2, 3, 4)); + boost::system::error_code ec; + Address result {Address::from_string ("1.2.3.4", ec)}; + AddressV4::bytes_type d = {{1,2,3,4}}; + BEAST_EXPECT(! ec); + BEAST_EXPECT( + result.is_v4 () && + result.to_v4() == AddressV4{d}); } //-------------------------------------------------------------------------- + void shouldParseEPV4 ( + std::string const& s, + AddressV4::bytes_type const& value, + std::uint16_t p, + std::string const& normal = "") + { + auto result {Endpoint::from_string_checked (s)}; + if (! BEAST_EXPECT(result.second)) + return; + if (! BEAST_EXPECT(result.first.address().is_v4 ())) + return; + if (! BEAST_EXPECT(result.first.address().to_v4() == AddressV4 {value})) + return; + + BEAST_EXPECT(result.first.port() == p); + BEAST_EXPECT(to_string (result.first) == (normal.empty() ? s : normal)); + } + + void shouldParseEPV6 ( + std::string const& s, + AddressV6::bytes_type const& value, + std::uint16_t p, + std::string const& normal = "") + { + auto result {Endpoint::from_string_checked (s)}; + if (! BEAST_EXPECT(result.second)) + return; + if (! BEAST_EXPECT(result.first.address().is_v6 ())) + return; + if (! BEAST_EXPECT(result.first.address().to_v6() == AddressV6 {value})) + return; + + BEAST_EXPECT(result.first.port() == p); + BEAST_EXPECT(to_string (result.first) == (normal.empty() ? s : normal)); + } + + void failParseEP (std::string s) + { + auto a1 = Endpoint::from_string(s); + BEAST_EXPECTS(is_unspecified (a1), s + " parses as " + a1.to_string()); + + auto a2 = Endpoint::from_string(s); + BEAST_EXPECTS(is_unspecified (a2), s + " parses as " + a2.to_string()); + + boost::replace_last(s, ":", " "); + auto a3 = Endpoint::from_string(s); + BEAST_EXPECTS(is_unspecified (a3), s + " parses as " + a3.to_string()); + } + void testEndpoint () { testcase ("Endpoint"); - { - std::pair result ( - Endpoint::from_string_checked ("1.2.3.4")); - BEAST_EXPECT(result.second); - if (BEAST_EXPECT(result.first.address().is_v4 ())) - { - BEAST_EXPECT(result.first.address().to_v4() == - AddressV4 (1, 2, 3, 4)); - BEAST_EXPECT(result.first.port() == 0); - BEAST_EXPECT(to_string (result.first) == "1.2.3.4"); - } - } - - { - std::pair result ( - Endpoint::from_string_checked ("1.2.3.4:5")); - BEAST_EXPECT(result.second); - if (BEAST_EXPECT(result.first.address().is_v4 ())) - { - BEAST_EXPECT(result.first.address().to_v4() == - AddressV4 (1, 2, 3, 4)); - BEAST_EXPECT(result.first.port() == 5); - BEAST_EXPECT(to_string (result.first) == "1.2.3.4:5"); - } - } + shouldParseEPV4("1.2.3.4", {{1,2,3,4}}, 0); + shouldParseEPV4("1.2.3.4:5", {{1,2,3,4}}, 5); + shouldParseEPV4("1.2.3.4 5", {{1,2,3,4}}, 5, "1.2.3.4:5"); + shouldParseEPV6( + "2001:db8:a0b:12f0::1", + {{32, 01, 13, 184, 10, 11, 18, 240, 0, 0, 0, 0, 0, 0, 0, 1}}, + 0); + shouldParseEPV6( + "[2001:db8:a0b:12f0::1]:8", + {{32, 01, 13, 184, 10, 11, 18, 240, 0, 0, 0, 0, 0, 0, 0, 1}}, + 8); + shouldParseEPV6( + "[2001:2002:2003:2004:2005:2006:2007:2008]:65535", + {{32, 1, 32, 2, 32, 3, 32, 4, 32, 5, 32, 6, 32, 7, 32, 8}}, + 65535); + shouldParseEPV6( + "2001:2002:2003:2004:2005:2006:2007:2008 65535", + {{32, 1, 32, 2, 32, 3, 32, 4, 32, 5, 32, 6, 32, 7, 32, 8}}, + 65535, + "[2001:2002:2003:2004:2005:2006:2007:2008]:65535"); Endpoint ep; - ep = Endpoint (AddressV4 (127,0,0,1), 80); + AddressV4::bytes_type d = {{127,0,0,1}}; + ep = Endpoint (AddressV4 {d}, 80); BEAST_EXPECT(! is_unspecified (ep)); BEAST_EXPECT(! is_public (ep)); BEAST_EXPECT( is_private (ep)); BEAST_EXPECT(! is_multicast (ep)); BEAST_EXPECT( is_loopback (ep)); BEAST_EXPECT(to_string (ep) == "127.0.0.1:80"); + // same address as v4 mapped in ipv6 + ep = Endpoint (AddressV6::v4_mapped(AddressV4 {d}), 80); + BEAST_EXPECT(! is_unspecified (ep)); + BEAST_EXPECT(! is_public (ep)); + BEAST_EXPECT( is_private (ep)); + BEAST_EXPECT(! is_multicast (ep)); + BEAST_EXPECT(! is_loopback (ep)); //mapped loopback is not a loopback + BEAST_EXPECTS(to_string (ep) == "[::ffff:127.0.0.1]:80", to_string (ep)); - ep = Endpoint (AddressV4 (10,0,0,1)); - BEAST_EXPECT(AddressV4::get_class (ep.to_v4()) == 'A'); + d = {{10,0,0,1}}; + ep = Endpoint (AddressV4 {d}); + BEAST_EXPECT(get_class (ep.to_v4()) == 'A'); BEAST_EXPECT(! is_unspecified (ep)); BEAST_EXPECT(! is_public (ep)); BEAST_EXPECT( is_private (ep)); BEAST_EXPECT(! is_multicast (ep)); BEAST_EXPECT(! is_loopback (ep)); BEAST_EXPECT(to_string (ep) == "10.0.0.1"); + // same address as v4 mapped in ipv6 + ep = Endpoint (AddressV6::v4_mapped(AddressV4 {d})); + BEAST_EXPECT(get_class (ep.to_v6().to_v4()) == 'A'); + BEAST_EXPECT(! is_unspecified (ep)); + BEAST_EXPECT(! is_public (ep)); + BEAST_EXPECT( is_private (ep)); + BEAST_EXPECT(! is_multicast (ep)); + BEAST_EXPECT(! is_loopback (ep)); + BEAST_EXPECTS(to_string (ep) == "::ffff:10.0.0.1", to_string(ep)); - ep = Endpoint (AddressV4 (166,78,151,147)); + d = {{166,78,151,147}}; + ep = Endpoint (AddressV4 {d}); BEAST_EXPECT(! is_unspecified (ep)); BEAST_EXPECT( is_public (ep)); BEAST_EXPECT(! is_private (ep)); BEAST_EXPECT(! is_multicast (ep)); BEAST_EXPECT(! is_loopback (ep)); BEAST_EXPECT(to_string (ep) == "166.78.151.147"); + // same address as v4 mapped in ipv6 + ep = Endpoint (AddressV6::v4_mapped(AddressV4 {d})); + BEAST_EXPECT(! is_unspecified (ep)); + BEAST_EXPECT( is_public (ep)); + BEAST_EXPECT(! is_private (ep)); + BEAST_EXPECT(! is_multicast (ep)); + BEAST_EXPECT(! is_loopback (ep)); + BEAST_EXPECTS(to_string (ep) == "::ffff:166.78.151.147", to_string(ep)); + + // a private IPv6 + AddressV6::bytes_type d2 = {{253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}}; + ep = Endpoint (AddressV6 {d2}); + BEAST_EXPECT(! is_unspecified (ep)); + BEAST_EXPECT(! is_public (ep)); + BEAST_EXPECT( is_private (ep)); + BEAST_EXPECT(! is_multicast (ep)); + BEAST_EXPECT(! is_loopback (ep)); + BEAST_EXPECTS(to_string (ep) == "fd00::1", to_string(ep)); { ep = Endpoint::from_string ("192.0.2.112"); BEAST_EXPECT(! is_unspecified (ep)); - BEAST_EXPECT(ep == Endpoint::from_string_altform ("192.0.2.112")); + BEAST_EXPECT(ep == Endpoint::from_string ("192.0.2.112")); auto const ep1 = Endpoint::from_string ("192.0.2.112:2016"); BEAST_EXPECT(! is_unspecified (ep1)); @@ -207,21 +313,21 @@ public: BEAST_EXPECT(ep1.port() == 2016); auto const ep2 = - Endpoint::from_string_altform ("192.0.2.112:2016"); + Endpoint::from_string ("192.0.2.112:2016"); BEAST_EXPECT(! is_unspecified (ep2)); BEAST_EXPECT(ep.address() == ep2.address()); BEAST_EXPECT(ep2.port() == 2016); BEAST_EXPECT(ep1 == ep2); auto const ep3 = - Endpoint::from_string_altform ("192.0.2.112 2016"); + Endpoint::from_string ("192.0.2.112 2016"); BEAST_EXPECT(! is_unspecified (ep3)); BEAST_EXPECT(ep.address() == ep3.address()); BEAST_EXPECT(ep3.port() == 2016); BEAST_EXPECT(ep2 == ep3); auto const ep4 = - Endpoint::from_string_altform ("192.0.2.112 2016"); + Endpoint::from_string ("192.0.2.112 2016"); BEAST_EXPECT(! is_unspecified (ep4)); BEAST_EXPECT(ep.address() == ep4.address()); BEAST_EXPECT(ep4.port() == 2016); @@ -232,87 +338,79 @@ public: BEAST_EXPECT(to_string(ep1) == to_string(ep4)); } + { + ep = Endpoint::from_string("[::]:2017"); + BEAST_EXPECT(is_unspecified (ep)); + BEAST_EXPECT(ep.port() == 2017); + BEAST_EXPECT(ep.address() == AddressV6{}); + } + // Failures: - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string ("192.0.2.112:port"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform ("192.0.2.112:port"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform ("192.0.2.112 port"))); + failParseEP ("192.0.2.112:port"); + failParseEP ("ip:port"); + failParseEP (""); + failParseEP ("1.2.3.256"); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string ("ip:port"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform ("ip:port"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform ("ip port"))); +#if BOOST_OS_WINDOWS + // windows asio bugs...false positives + shouldParseEPV4 ("255", {{0,0,0,255}}, 0, "0.0.0.255"); + shouldParseEPV4 ("512", {{0,0,2,0}}, 0, "0.0.2.0"); + shouldParseEPV4 ("1.2.3:80", {{1,2,0,3}}, 80, "1.2.0.3:80"); +#else + failParseEP ("255"); + failParseEP ("512"); + failParseEP ("1.2.3:80"); +#endif - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string(""))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform(""))); + failParseEP ("1.2.3.4:65536"); + failParseEP ("1.2.3.4:89119"); + failParseEP ("1.2.3:89119"); + failParseEP ("[::1]:89119"); + failParseEP ("[::az]:1"); + failParseEP ("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:1"); + failParseEP ("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:12345"); + failParseEP ("abcdef:12345"); + failParseEP ("[abcdef]:12345"); + failParseEP ("foo.org 12345"); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string("255"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform("255"))); - - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string("512"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform("512"))); - - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string("1.2.3.256"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform("1.2.3.256"))); - - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string("1.2.3:80"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform("1.2.3:80"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform("1.2.3 80"))); - - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string("1.2.3.4:65536"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform("1.2.3:65536"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform("1.2.3 65536"))); - - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string("1.2.3.4:89119"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform("1.2.3:89119"))); - BEAST_EXPECT(is_unspecified ( - Endpoint::from_string_altform("1.2.3 89119"))); + // test with hashed container + std::unordered_set eps; + constexpr auto items {100}; + float max_lf {0}; + for (auto i = 0; i < items; ++i) + { + eps.insert(randomEP(ripple::rand_int(0,1) == 1)); + max_lf = std::max(max_lf, eps.load_factor()); + } + BEAST_EXPECT(eps.bucket_count() >= items); + BEAST_EXPECT(max_lf > 0.90); } //-------------------------------------------------------------------------- template - bool parse (char const* text, T& t) + bool parse (std::string const& text, T& t) { - std::string input (text); - std::istringstream stream (input); + std::istringstream stream {text}; stream >> t; return !stream.fail(); } template - void shouldPass (char const* text) + void shouldPass (std::string const& text, std::string const& normal="") { + using namespace std::literals; T t; BEAST_EXPECT(parse (text, t)); - BEAST_EXPECT(to_string (t) == std::string (text)); + BEAST_EXPECTS(to_string (t) == (normal.empty() ? text : normal), + "string mismatch for "s + text); } template - void shouldFail (char const* text) + void shouldFail (std::string const& text) { T t; - unexpected (parse (text, t)); + unexpected (parse (text, t), text + " should not parse"); } template @@ -325,14 +423,30 @@ public: shouldPass ("168.127.149.132"); shouldPass ("168.127.149.132:80"); shouldPass ("168.127.149.132:54321"); + shouldPass ("2001:db8:a0b:12f0::1"); + shouldPass ("[2001:db8:a0b:12f0::1]:8"); + shouldPass ("2001:db8:a0b:12f0::1 8", "[2001:db8:a0b:12f0::1]:8"); + shouldPass ("[::1]:8"); + shouldPass ("[2001:2002:2003:2004:2005:2006:2007:2008]:65535"); - shouldFail (""); - shouldFail ("255"); - shouldFail ("512"); shouldFail ("1.2.3.256"); + shouldFail (""); +#if BOOST_OS_WINDOWS + // windows asio bugs...false positives + shouldPass ("512", "0.0.2.0"); + shouldPass ("255", "0.0.0.255"); + shouldPass ("1.2.3:80", "1.2.0.3:80"); +#else + shouldFail ("512"); + shouldFail ("255"); shouldFail ("1.2.3:80"); +#endif shouldFail ("1.2.3:65536"); shouldFail ("1.2.3:72131"); + shouldFail ("[::1]:89119"); + shouldFail ("[::az]:1"); + shouldFail ("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:1"); + shouldFail ("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:12345"); } void run () override @@ -341,7 +455,6 @@ public: testAddressV4Proxy(); testAddress (); testEndpoint (); - testParse ("Parse Endpoint"); } }; diff --git a/src/test/jtx/TrustedPublisherServer.h b/src/test/jtx/TrustedPublisherServer.h index 3288127f75..ccc0ac1bae 100644 --- a/src/test/jtx/TrustedPublisherServer.h +++ b/src/test/jtx/TrustedPublisherServer.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,6 @@ public: }; TrustedPublisherServer( - endpoint_type const& ep, boost::asio::io_service& ios, std::pair keys, std::string const& manifest, @@ -65,6 +65,9 @@ public: std::vector const& validators) : sock_(ios), acceptor_(ios) { + endpoint_type const& ep { + beast::IP::Address::from_string (ripple::test::getEnvLocalhostAddr()), + 0}; // 0 means let OS pick the port based on what's available std::string data = "{\"sequence\":" + std::to_string(sequence) + ",\"expiration\":" + std::to_string(expiration.time_since_epoch().count()) + diff --git a/src/test/jtx/envconfig.h b/src/test/jtx/envconfig.h index dd9d5be2b1..d265678a8c 100644 --- a/src/test/jtx/envconfig.h +++ b/src/test/jtx/envconfig.h @@ -25,6 +25,15 @@ namespace ripple { namespace test { +extern std::atomic envUseIPv4; + +inline +const char * +getEnvLocalhostAddr() +{ + return envUseIPv4 ? "127.0.0.1" : "::1"; +} + /// @brief initializes a config object for use with jtx::Env /// /// @param config the configuration object to be initialized diff --git a/src/test/jtx/impl/JSONRPCClient.cpp b/src/test/jtx/impl/JSONRPCClient.cpp index f32d5cf042..2b142363f0 100644 --- a/src/test/jtx/impl/JSONRPCClient.cpp +++ b/src/test/jtx/impl/JSONRPCClient.cpp @@ -49,9 +49,9 @@ class JSONRPCClient : public AbstractClient parse_Port(pp, cfg[name], log); if(pp.protocol.count("http") == 0) continue; - using boost::asio::ip::address_v4; - if(*pp.ip == address_v4{0x00000000}) - *pp.ip = address_v4{0x7f000001}; + using namespace boost::asio::ip; + if(pp.ip && pp.ip->is_unspecified()) + *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} : address{address_v4::loopback()}; return { *pp.ip, *pp.port }; } Throw("Missing HTTP port"); @@ -83,7 +83,7 @@ public: : ep_(getEndpoint(cfg)) , stream_(ios_) , rpc_version_(rpc_version) - { + { stream_.connect(ep_); } diff --git a/src/test/jtx/impl/WSClient.cpp b/src/test/jtx/impl/WSClient.cpp index f239b9c36c..71dd46fd7f 100644 --- a/src/test/jtx/impl/WSClient.cpp +++ b/src/test/jtx/impl/WSClient.cpp @@ -64,9 +64,9 @@ class WSClientImpl : public WSClient parse_Port(pp, cfg[name], log); if(pp.protocol.count(ps) == 0) continue; - using boost::asio::ip::address_v4; - if(*pp.ip == address_v4{0x00000000}) - *pp.ip = address_v4{0x7f000001}; + using namespace boost::asio::ip; + if(pp.ip && pp.ip->is_unspecified()) + *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} : address{address_v4::loopback()}; return { *pp.ip, *pp.port }; } Throw("Missing WebSocket port"); diff --git a/src/test/jtx/impl/envconfig.cpp b/src/test/jtx/impl/envconfig.cpp index 3868725ef7..4de1066da1 100644 --- a/src/test/jtx/impl/envconfig.cpp +++ b/src/test/jtx/impl/envconfig.cpp @@ -30,6 +30,8 @@ void incPorts() port_base += 3; } +std::atomic envUseIPv4 {false}; + void setupConfigForUnitTests (Config& cfg) { @@ -43,19 +45,21 @@ setupConfigForUnitTests (Config& cfg) cfg.legacy("database_path", ""); cfg.setupControl(true, true, true); cfg["server"].append("port_peer"); - cfg["port_peer"].set("ip", "127.0.0.1"); + cfg["port_peer"].set("ip", getEnvLocalhostAddr()); cfg["port_peer"].set("port", port_peer); cfg["port_peer"].set("protocol", "peer"); + cfg["server"].append("port_rpc"); - cfg["port_rpc"].set("ip", "127.0.0.1"); + cfg["port_rpc"].set("ip", getEnvLocalhostAddr()); + cfg["port_rpc"].set("admin", getEnvLocalhostAddr()); cfg["port_rpc"].set("port", port_rpc); cfg["port_rpc"].set("protocol", "http,ws2"); - cfg["port_rpc"].set("admin", "127.0.0.1"); + cfg["server"].append("port_ws"); - cfg["port_ws"].set("ip", "127.0.0.1"); + cfg["port_ws"].set("ip", getEnvLocalhostAddr()); + cfg["port_ws"].set("admin", getEnvLocalhostAddr()); cfg["port_ws"].set("port", port_ws); cfg["port_ws"].set("protocol", "ws"); - cfg["port_ws"].set("admin", "127.0.0.1"); } namespace jtx { diff --git a/src/test/overlay/short_read_test.cpp b/src/test/overlay/short_read_test.cpp index 688a7e65ac..ce5f133396 100644 --- a/src/test/overlay/short_read_test.cpp +++ b/src/test/overlay/short_read_test.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -183,7 +184,8 @@ private: , server_(server) , test_(server_.test_) , acceptor_(test_.io_service_, - endpoint_type(address_type::from_string("127.0.0.1"), 0)) + endpoint_type(beast::IP::Address::from_string( + test::getEnvLocalhostAddr()), 0)) , socket_(test_.io_service_) , strand_(socket_.get_io_service()) { diff --git a/src/test/peerfinder/Livecache_test.cpp b/src/test/peerfinder/Livecache_test.cpp index 70124cd375..b67f5bfc81 100644 --- a/src/test/peerfinder/Livecache_test.cpp +++ b/src/test/peerfinder/Livecache_test.cpp @@ -21,10 +21,18 @@ #include #include #include +#include +#include namespace ripple { namespace PeerFinder { +bool operator== (Endpoint const& a, Endpoint const& b) +{ + return (a.hops == b.hops && + a.address == b.address); +} + class Livecache_test : public beast::unit_test::suite { public: @@ -32,38 +40,178 @@ public: // Add the address as an endpoint template - void add (std::uint32_t index, std::uint16_t port, C& c) + inline void add (beast::IP::Endpoint ep, C& c, int hops = 0) { - Endpoint ep; - ep.hops = 0; - ep.address = beast::IP::Endpoint ( - beast::IP::AddressV4 (index), port); - c.insert (ep); + Endpoint cep {ep, hops}; + c.insert (cep); } - void testFetch () + void testBasicInsert () { + testcase ("Basic Insert"); + Livecache <> c (m_clock, beast::Journal()); + BEAST_EXPECT(c.empty()); + + for (auto i = 0; i < 10; ++i) + add(beast::IP::randomEP(true), c); + + BEAST_EXPECT(! c.empty()); + BEAST_EXPECT(c.size() == 10); + + for (auto i = 0; i < 10; ++i) + add(beast::IP::randomEP(false), c); + + BEAST_EXPECT(! c.empty()); + BEAST_EXPECT(c.size() == 20); + } + + void testInsertUpdate () + { + testcase ("Insert/Update"); Livecache <> c (m_clock, beast::Journal()); - add (1, 1, c); - add (2, 1, c); - add (3, 1, c); - add (4, 1, c); - add (4, 2, c); - add (4, 3, c); - add (5, 1, c); - add (6, 1, c); - add (6, 2, c); - add (7, 1, c); + auto ep1 = Endpoint {beast::IP::randomEP(), 2}; + c.insert(ep1); + BEAST_EXPECT(c.size() == 1); + // third position list will contain the entry + BEAST_EXPECT((c.hops.begin()+2)->begin()->hops == 2); - // VFALCO TODO! + auto ep2 = Endpoint {ep1.address, 4}; + // this will not change the entry has higher hops + c.insert(ep2); + BEAST_EXPECT(c.size() == 1); + // still in third position list + BEAST_EXPECT((c.hops.begin()+2)->begin()->hops == 2); - pass(); + auto ep3 = Endpoint {ep1.address, 2}; + // this will not change the entry has the same hops as existing + c.insert(ep3); + BEAST_EXPECT(c.size() == 1); + // still in third position list + BEAST_EXPECT((c.hops.begin()+2)->begin()->hops == 2); + + auto ep4 = Endpoint {ep1.address, 1}; + c.insert(ep4); + BEAST_EXPECT(c.size() == 1); + // now at second position list + BEAST_EXPECT((c.hops.begin()+1)->begin()->hops == 1); + } + + void testExpire () + { + testcase ("Expire"); + using namespace std::chrono_literals; + Livecache <> c (m_clock, beast::Journal()); + + auto ep1 = Endpoint {beast::IP::randomEP(), 1}; + c.insert(ep1); + BEAST_EXPECT(c.size() == 1); + c.expire(); + BEAST_EXPECT(c.size() == 1); + // verify that advancing to 1 sec before expiration + // leaves our entry intact + m_clock.advance(Tuning::liveCacheSecondsToLive - 1s); + c.expire(); + BEAST_EXPECT(c.size() == 1); + // now advance to the point of expiration + m_clock.advance(1s); + c.expire(); + BEAST_EXPECT(c.empty()); + } + + void testHistogram () + { + testcase ("Histogram"); + constexpr auto num_eps = 40; + Livecache <> c (m_clock, beast::Journal()); + for (auto i = 0; i < num_eps; ++i) + add( + beast::IP::randomEP(true), + c, + ripple::rand_int(0, static_cast(Tuning::maxHops + 1))); + auto h = c.hops.histogram(); + if(! BEAST_EXPECT(! h.empty())) + return; + std::vector v; + boost::split (v, h, boost::algorithm::is_any_of (",")); + auto sum = 0; + for (auto const& n : v) + { + auto val = boost::lexical_cast(boost::trim_copy(n)); + sum += val; + BEAST_EXPECT(val >= 0); + } + BEAST_EXPECT(sum == num_eps); + } + + + void testShuffle () + { + testcase ("Shuffle"); + Livecache <> c (m_clock, beast::Journal()); + for (auto i = 0; i < 100; ++i) + add( + beast::IP::randomEP(true), + c, + ripple::rand_int(0, static_cast(Tuning::maxHops + 1))); + + using at_hop = std::vector ; + using all_hops = std::array ; + + auto cmp_EP = [](Endpoint const& a, Endpoint const& b) { + return (b.hops < a.hops || (b.hops == a.hops && b.address < a.address)); + }; + all_hops before; + all_hops before_sorted; + for (auto i = std::make_pair(0, c.hops.begin()); + i.second != c.hops.end(); ++i.first, ++i.second) + { + std::copy ((*i.second).begin(), (*i.second).end(), + std::back_inserter (before[i.first])); + std::copy ((*i.second).begin(), (*i.second).end(), + std::back_inserter (before_sorted[i.first])); + std::sort( + before_sorted[i.first].begin(), + before_sorted[i.first].end(), + cmp_EP); + } + + c.hops.shuffle(); + + all_hops after; + all_hops after_sorted; + for (auto i = std::make_pair(0, c.hops.begin()); + i.second != c.hops.end(); ++i.first, ++i.second) + { + std::copy ((*i.second).begin(), (*i.second).end(), + std::back_inserter (after[i.first])); + std::copy ((*i.second).begin(), (*i.second).end(), + std::back_inserter (after_sorted[i.first])); + std::sort( + after_sorted[i.first].begin(), + after_sorted[i.first].end(), + cmp_EP); + } + + // each hop bucket should contain the same items + // before and after sort, albeit in different order + bool all_match = true; + for (auto i = 0; i < before.size(); ++i) + { + BEAST_EXPECT(before[i].size() == after[i].size()); + all_match = all_match && (before[i] == after[i]); + BEAST_EXPECT(before_sorted[i] == after_sorted[i]); + } + BEAST_EXPECT(! all_match); } void run () override { - testFetch (); + testBasicInsert (); + testInsertUpdate (); + testExpire (); + testHistogram (); + testShuffle (); } }; diff --git a/src/test/resource/Logic_test.cpp b/src/test/resource/Logic_test.cpp index 6d6b4ccaa8..d730c8d1c7 100644 --- a/src/test/resource/Logic_test.cpp +++ b/src/test/resource/Logic_test.cpp @@ -63,15 +63,16 @@ public: void createGossip (Gossip& gossip) { - int const v (10 + rand_int(9)); - int const n (10 + rand_int(9)); + std::uint8_t const v (10 + rand_int(9)); + std::uint8_t const n (10 + rand_int(9)); gossip.items.reserve (n); - for (int i = 0; i < n; ++i) + for (std::uint8_t i = 0; i < n; ++i) { Gossip::Item item; item.balance = 100 + rand_int(499); - item.address = beast::IP::Endpoint ( - beast::IP::AddressV4 (192, 0, 2, v + i)); + beast::IP::AddressV4::bytes_type d = + {{192,0,2,static_cast(v + i)}}; + item.address = beast::IP::Endpoint { beast::IP::AddressV4 {d} }; gossip.items.push_back (item); } } @@ -193,8 +194,8 @@ public: Gossip g; Gossip::Item item; item.balance = 100; - item.address = beast::IP::Endpoint ( - beast::IP::AddressV4 (192, 0, 2, 1)); + beast::IP::AddressV4::bytes_type d = {{192, 0, 2, 1}}; + item.address = beast::IP::Endpoint { beast::IP::AddressV4 {d} }; g.items.push_back (item); logic.importConsumers ("g", g); diff --git a/src/test/rpc/NoRippleCheck_test.cpp b/src/test/rpc/NoRippleCheck_test.cpp index 5c367c6e82..a630a4bf1c 100644 --- a/src/test/rpc/NoRippleCheck_test.cpp +++ b/src/test/rpc/NoRippleCheck_test.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -265,7 +266,7 @@ class NoRippleCheckLimits_test : public beast::unit_test::suite using namespace std::chrono; using namespace beast::IP; auto c = env.app().getResourceManager() - .newInboundEndpoint (Endpoint::from_string ("127.0.0.1")); + .newInboundEndpoint (Endpoint::from_string (test::getEnvLocalhostAddr())); if (dropThreshold - c.balance() <= 20) { using clock_type = beast::abstract_clock ; diff --git a/src/test/rpc/ValidatorRPC_test.cpp b/src/test/rpc/ValidatorRPC_test.cpp index 4966bd8f03..d6ca23b2d1 100644 --- a/src/test/rpc/ValidatorRPC_test.cpp +++ b/src/test/rpc/ValidatorRPC_test.cpp @@ -178,8 +178,6 @@ public: testDynamicUNL() { using namespace test::jtx; - using endpoint_type = boost::asio::ip::tcp::endpoint; - using address_type = boost::asio::ip::address; auto toStr = [](PublicKey const& publicKey) { return toBase58(TokenType::NodePublic, publicKey); @@ -208,7 +206,9 @@ public: // Publisher list site unavailable { // Publisher site information - std::string siteURI = "http://127.0.0.1:1234/validators"; + using namespace std::string_literals; + std::string siteURI = + "http://"s + getEnvLocalhostAddr() + ":1234/validators"; Env env{ *this, @@ -271,9 +271,6 @@ public: { NetClock::time_point const expiration{3600s}; - // 0 port means to use OS port selection - endpoint_type ep{address_type::from_string("127.0.0.1"), 0}; - // Manage single thread io_service for server struct Worker : BasicApp { @@ -282,7 +279,6 @@ public: Worker w; TrustedPublisherServer server( - ep, w.get_io_service(), publisherSigningKeys, manifest, @@ -291,9 +287,9 @@ public: 1, validators); - endpoint_type const & local_ep = server.local_endpoint(); - std::string siteURI = "http://127.0.0.1:" + - std::to_string(local_ep.port()) + "/validators"; + std::stringstream uri; + uri << "http://" << server.local_endpoint() << "/validators"; + auto siteURI = uri.str(); Env env{ *this, diff --git a/src/test/server/ServerStatus_test.cpp b/src/test/server/ServerStatus_test.cpp index 33be5b655d..8bc81f13bc 100644 --- a/src/test/server/ServerStatus_test.cpp +++ b/src/test/server/ServerStatus_test.cpp @@ -73,10 +73,10 @@ class ServerStatus_test : // which requires an http endpoint to talk to. In the connection // failure test, this endpoint should never be used (*p)["server"].append("port_alt"); - (*p)["port_alt"].set("ip", "127.0.0.1"); + (*p)["port_alt"].set("ip", getEnvLocalhostAddr()); (*p)["port_alt"].set("port", "8099"); (*p)["port_alt"].set("protocol", "http"); - (*p)["port_alt"].set("admin", "127.0.0.1"); + (*p)["port_alt"].set("admin", getEnvLocalhostAddr()); } return p; diff --git a/src/test/server/Server_test.cpp b/src/test/server/Server_test.cpp index c2857c200d..5856c9205f 100644 --- a/src/test/server/Server_test.cpp +++ b/src/test/server/Server_test.cpp @@ -283,7 +283,7 @@ public: thread.get_io_service(), journal); std::vector serverPort(1); serverPort.back().ip = - boost::asio::ip::address::from_string ("127.0.0.1"), + beast::IP::Address::from_string (getEnvLocalhostAddr()), serverPort.back().port = 0; serverPort.back().protocol.insert("http"); auto eps = s->ports (serverPort); @@ -355,7 +355,7 @@ public: thread.get_io_service(), {}); std::vector serverPort(1); serverPort.back().ip = - boost::asio::ip::address::from_string ("127.0.0.1"), + beast::IP::Address::from_string (getEnvLocalhostAddr()), serverPort.back().port = 0; serverPort.back().protocol.insert("http"); s->ports (serverPort); @@ -441,7 +441,7 @@ public: Env env {*this, envconfig([](std::unique_ptr cfg) { (*cfg).deprecatedClearSection("port_rpc"); - (*cfg)["port_rpc"].set("ip", "127.0.0.1"); + (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr()); return cfg; }), std::make_unique(messages)}; @@ -455,7 +455,7 @@ public: Env env {*this, envconfig([](std::unique_ptr cfg) { (*cfg).deprecatedClearSection("port_rpc"); - (*cfg)["port_rpc"].set("ip", "127.0.0.1"); + (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr()); (*cfg)["port_rpc"].set("port", "0"); return cfg; }), @@ -470,7 +470,7 @@ public: Env env {*this, envconfig([](std::unique_ptr cfg) { (*cfg).deprecatedClearSection("port_rpc"); - (*cfg)["port_rpc"].set("ip", "127.0.0.1"); + (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr()); (*cfg)["port_rpc"].set("port", "8081"); (*cfg)["port_rpc"].set("protocol", ""); return cfg; @@ -495,17 +495,17 @@ public: ConfigSection::importNodeDatabase ()); cfg->legacy("database_path", ""); cfg->setupControl(true, true, true); - (*cfg)["port_peer"].set("ip", "127.0.0.1"); + (*cfg)["port_peer"].set("ip", getEnvLocalhostAddr()); (*cfg)["port_peer"].set("port", "8080"); (*cfg)["port_peer"].set("protocol", "peer"); - (*cfg)["port_rpc"].set("ip", "127.0.0.1"); + (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr()); (*cfg)["port_rpc"].set("port", "8081"); (*cfg)["port_rpc"].set("protocol", "http,ws2"); - (*cfg)["port_rpc"].set("admin", "127.0.0.1"); - (*cfg)["port_ws"].set("ip", "127.0.0.1"); + (*cfg)["port_rpc"].set("admin", getEnvLocalhostAddr()); + (*cfg)["port_ws"].set("ip", getEnvLocalhostAddr()); (*cfg)["port_ws"].set("port", "8082"); (*cfg)["port_ws"].set("protocol", "ws"); - (*cfg)["port_ws"].set("admin", "127.0.0.1"); + (*cfg)["port_ws"].set("admin", getEnvLocalhostAddr()); return cfg; }), std::make_unique(messages)};