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.
This commit is contained in:
Mike Ellery
2017-11-08 10:10:24 -08:00
parent fd4636b056
commit 08382d866b
41 changed files with 968 additions and 1253 deletions

View File

@@ -216,12 +216,15 @@ public:
} }
}; };
namespace test{ extern std::atomic<bool> envUseIPv4; }
static int runUnitTests( static int runUnitTests(
std::string const& pattern, std::string const& pattern,
std::string const& argument, std::string const& argument,
bool quiet, bool quiet,
bool log, bool log,
bool child, bool child,
bool ipv4,
std::size_t num_jobs, std::size_t num_jobs,
int argc, int argc,
char** argv) char** argv)
@@ -229,6 +232,9 @@ static int runUnitTests(
using namespace beast::unit_test; using namespace beast::unit_test;
using namespace ripple::test; using namespace ripple::test;
if (ipv4)
ripple::test::envUseIPv4 = true;
#if HAS_BOOST_PROCESS #if HAS_BOOST_PROCESS
if (!child && num_jobs == 1) if (!child && num_jobs == 1)
#endif #endif
@@ -365,6 +371,7 @@ int run (int argc, char** argv)
"Specify the IP address for RPC command. " "Specify the IP address for RPC command. "
"Format: <ip-address>[':'<port-number>]") "Format: <ip-address>[':'<port-number>]")
("rpc_port", po::value <std::uint16_t> (), ("rpc_port", po::value <std::uint16_t> (),
"DEPRECATED: include with rpc_ip instead. "
"Specify the port number for RPC command.") "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 -- " "argument is handled individually by any suite that accesses it -- "
"as such, it typically only make sense to provide this when running " "as such, it typically only make sense to provide this when running "
"a single suite.") "a single suite.")
("unittest-ipv4", "Use IPv4 localhost when running unittests (default is IPv6).")
("unittest-log", ("unittest-log",
"Force unit test log message output. Only useful in combination with " "Force unit test log message output. Only useful in combination with "
"--quiet, in which case log messages will print but suite/case names " "--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 ("quiet")),
bool (vm.count ("unittest-log")), bool (vm.count ("unittest-log")),
unittestChild, unittestChild,
bool (vm.count ("unittest-ipv4")),
numJobs, numJobs,
argc, argc,
argv); argv);
@@ -553,37 +562,38 @@ int run (int argc, char** argv)
// happen after the config file is loaded. // happen after the config file is loaded.
if (vm.count ("rpc_ip")) if (vm.count ("rpc_ip"))
{ {
try auto res = beast::IP::Endpoint::from_string_checked(
{ vm["rpc_ip"].as<std::string>());
config->rpc_ip.emplace ( if (! res.second)
boost::asio::ip::address_v4::from_string(
vm["rpc_ip"].as<std::string>()));
}
catch(std::exception const&)
{ {
std::cerr << "Invalid rpc_ip = " << std::cerr << "Invalid rpc_ip = " <<
vm["rpc_ip"].as<std::string>() << std::endl; vm["rpc_ip"].as<std::string>() << "\n";
return -1; return -1;
} }
}
// Override the RPC destination port number if (res.first.port() == 0)
//
if (vm.count ("rpc_port"))
{
try
{ {
config->rpc_port.emplace ( std::cerr << "No port specified in rpc_ip.\n";
vm["rpc_port"].as<std::uint16_t>()); if (vm.count ("rpc_port"))
{
std::cerr << "WARNING: using deprecated rpc_port param.\n";
try
{
res.first.at_port(vm["rpc_port"].as<std::uint16_t>());
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) config->rpc_ip = std::move(res.first);
throw std::domain_error("0");
}
catch(std::exception const& e)
{
std::cerr << "Invalid rpc_port = " << e.what() << "\n";
return -1;
}
} }
if (vm.count ("quorum")) if (vm.count ("quorum"))

View File

@@ -20,6 +20,7 @@
#include <ripple/basics/ResolverAsio.h> #include <ripple/basics/ResolverAsio.h>
#include <ripple/basics/Log.h> #include <ripple/basics/Log.h>
#include <ripple/beast/net/IPAddressConversion.h> #include <ripple/beast/net/IPAddressConversion.h>
#include <ripple/beast/net/IPEndpoint.h>
#include <ripple/beast/core/WaitableEvent.h> #include <ripple/beast/core/WaitableEvent.h>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <atomic> #include <atomic>
@@ -259,6 +260,21 @@ public:
HostAndPort parseName(std::string const& str) 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 // Attempt to find the first and last non-whitespace
auto const find_whitespace = std::bind ( auto const find_whitespace = std::bind (
&std::isspace <std::string::value_type>, &std::isspace <std::string::value_type>,

View File

@@ -23,7 +23,7 @@
#include <ripple/basics/ToString.h> #include <ripple/basics/ToString.h>
#include <ripple/beast/core/LexicalCast.h> #include <ripple/beast/core/LexicalCast.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/asio/ip/address.hpp> #include <ripple/beast/net/IPEndpoint.h>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <algorithm> #include <algorithm>
#include <cstdarg> #include <cstdarg>
@@ -93,24 +93,38 @@ uint64_t uintFromHex (std::string const& strSrc)
bool parseUrl (parsedURL& pUrl, std::string const& strUrl) bool parseUrl (parsedURL& pUrl, std::string const& strUrl)
{ {
// scheme://username:password@hostname:port/rest // 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; boost::smatch smMatch;
bool bMatch = boost::regex_match (strUrl, smMatch, reUrl); // Match status code. bool bMatch = boost::regex_match (strUrl, smMatch, reUrl); // Match status code.
if (bMatch) if (bMatch)
{ {
std::string strPort;
pUrl.scheme = smMatch[1]; pUrl.scheme = smMatch[1];
boost::algorithm::to_lower (pUrl.scheme); boost::algorithm::to_lower (pUrl.scheme);
pUrl.path = smMatch[3];
pUrl.domain = smMatch[2]; 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::uint16_t> ( // use Endpoint class to see if this thing looks
std::string (smMatch[3])); // 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 <std::uint16_t> (
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; return bMatch;

View File

@@ -217,19 +217,9 @@ private:
std::thread m_thread; std::thread m_thread;
static boost::asio::ip::udp::endpoint to_endpoint ( 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 (ep.address(), ep.port());
{
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);
} }
public: public:

View File

@@ -25,6 +25,7 @@
#include <ripple/beast/hash/hash_append.h> #include <ripple/beast/hash/hash_append.h>
#include <ripple/beast/hash/uhash.h> #include <ripple/beast/hash/uhash.h>
#include <boost/functional/hash.hpp> #include <boost/functional/hash.hpp>
#include <boost/asio/ip/address.hpp>
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <ios> #include <ios>
@@ -37,213 +38,22 @@
namespace beast { namespace beast {
namespace IP { namespace IP {
/** A version-independent IP address. using Address = boost::asio::ip::address;
This object can represent either an IPv4 or IPv6 address.
*/ /** Returns the address represented as a string. */
class Address inline
std::string
to_string (Address const& addr)
{ {
public: return addr.to_string ();
/** 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 <Address, bool>
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 <class Hasher>
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
/** Returns `true` if this is a loopback address. */ /** Returns `true` if this is a loopback address. */
inline inline
bool bool
is_loopback (Address const& addr) is_loopback (Address const& addr)
{ {
return (addr.is_v4 ()) return addr.is_loopback();
? is_loopback (addr.to_v4 ())
: is_loopback (addr.to_v6 ());
} }
/** Returns `true` if the address is unspecified. */ /** Returns `true` if the address is unspecified. */
@@ -251,9 +61,7 @@ inline
bool bool
is_unspecified (Address const& addr) is_unspecified (Address const& addr)
{ {
return (addr.is_v4 ()) return addr.is_unspecified();
? is_unspecified (addr.to_v4 ())
: is_unspecified (addr.to_v6 ());
} }
/** Returns `true` if the address is a multicast address. */ /** Returns `true` if the address is a multicast address. */
@@ -261,9 +69,7 @@ inline
bool bool
is_multicast (Address const& addr) is_multicast (Address const& addr)
{ {
return (addr.is_v4 ()) return addr.is_multicast();
? is_multicast (addr.to_v4 ())
: is_multicast (addr.to_v6 ());
} }
/** Returns `true` if the address is a private unroutable address. */ /** Returns `true` if the address is a private unroutable address. */
@@ -286,51 +92,24 @@ is_public (Address const& addr)
: is_public (addr.to_v6 ()); : 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 <typename OutputStream>
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, bool>
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 <class Hasher>
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 { namespace std {
template <> template <>
struct hash <beast::IP::Address> struct hash <beast::IP::Address>

View File

@@ -21,141 +21,17 @@
#define BEAST_NET_IPADDRESSV4_H_INCLUDED #define BEAST_NET_IPADDRESSV4_H_INCLUDED
#include <ripple/beast/hash/hash_append.h> #include <ripple/beast/hash/hash_append.h>
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <ios> #include <ios>
#include <string> #include <string>
#include <utility> #include <utility>
#include <boost/asio/ip/address_v4.hpp>
namespace beast { namespace beast {
namespace IP { namespace IP {
/** Represents a version 4 IP address. */ using AddressV4 = boost::asio::ip::address_v4;
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 <AddressV4, bool> 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 <bool IsConst>
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 <typename IntegralType>
Proxy& operator= (IntegralType v)
{
(*m_value) =
( (*m_value) & (~((0xff)<<m_shift)) )
| ((v&0xff) << m_shift);
return *this;
}
private:
int m_shift;
Pointer m_value;
};
Proxy <true> operator[] (std::size_t index) const;
Proxy <false> 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);
/** Returns `true` if the address is a private unroutable address. */ /** Returns `true` if the address is a private unroutable address. */
bool is_private (AddressV4 const& addr); 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. */ /** Returns `true` if the address is a public routable address. */
bool is_public (AddressV4 const& addr); bool is_public (AddressV4 const& addr);
//------------------------------------------------------------------------------ /** Returns the address class for the given address.
@note Class 'D' represents multicast addresses (224.*.*.*).
/** Returns the address represented as a string. */ */
std::string to_string (AddressV4 const& addr); char get_class (AddressV4 const& address);
/** Output stream conversion. */
template <typename OutputStream>
OutputStream& operator<< (OutputStream& os, AddressV4 const& addr)
{ return os << to_string (addr); }
/** Input stream conversion. */
std::istream& operator>> (std::istream& is, AddressV4& addr);
} }
template <class HashAlgorithm>
struct is_contiguously_hashable<IP::AddressV4, HashAlgorithm>
: public std::integral_constant<bool, sizeof(IP::AddressV4) == sizeof(std::uint32_t)>
{
explicit is_contiguously_hashable() = default;
};
}
//------------------------------------------------------------------------------
namespace std {
/** std::hash support. */
template <>
struct hash <beast::IP::AddressV4>
{
explicit hash() = default;
std::size_t operator() (beast::IP::AddressV4 const& addr) const
{ return addr.value; }
};
} }
#endif #endif

View File

@@ -26,45 +26,12 @@
#include <ios> #include <ios>
#include <string> #include <string>
#include <utility> #include <utility>
#include <boost/asio/ip/address_v6.hpp>
namespace beast { namespace beast {
namespace IP { namespace IP {
/** Represents a version 4 IP address. */ using AddressV6 = boost::asio::ip::address_v6;
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);
/** Returns `true` if the address is a private unroutable address. */ /** Returns `true` if the address is a private unroutable address. */
bool is_private (AddressV6 const& addr); 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. */ /** Returns `true` if the address is a public routable address. */
bool is_public (AddressV6 const& addr); bool is_public (AddressV6 const& addr);
//------------------------------------------------------------------------------
template <class Hasher>
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 <typename OutputStream>
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 <beast::IP::AddressV6>
{
explicit hash() = default;
std::size_t operator() (beast::IP::AddressV6 const& addr) const
{ assert(false); return 0; }
};
} }
#endif #endif

View File

@@ -48,7 +48,6 @@ public:
*/ */
static std::pair <Endpoint, bool> from_string_checked (std::string const& s); static std::pair <Endpoint, bool> from_string_checked (std::string const& s);
static Endpoint from_string (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. */ /** Returns a string representing the endpoint. */
std::string to_string () const; std::string to_string () const;
@@ -71,9 +70,9 @@ public:
{ return m_addr.is_v4(); } { return m_addr.is_v4(); }
bool is_v6 () const bool is_v6 () const
{ return m_addr.is_v6(); } { return m_addr.is_v6(); }
AddressV4 const& to_v4 () const AddressV4 const to_v4 () const
{ return m_addr.to_v4 (); } { return m_addr.to_v4 (); }
AddressV6 const& to_v6 () const AddressV6 const to_v6 () const
{ return m_addr.to_v6 (); } { return m_addr.to_v6 (); }
/** @} */ /** @} */

View File

@@ -1,103 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
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 <ios>
#include <string>
namespace beast {
namespace IP {
namespace detail {
/** Require and consume the specified character from the input.
@return `true` if the character matched.
*/
template <typename InputStream>
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 <typename InputStream>
bool expect_whitespace (InputStream& is)
{
char c;
if (is.get(c) && isspace(static_cast<unsigned char>(c)))
return true;
is.unget();
is.setstate (std::ios_base::failbit);
return false;
}
/** Used to disambiguate 8-bit integers from characters. */
template <typename IntType>
struct integer_holder
{
IntType* pi;
explicit integer_holder (IntType& i)
: pi (&i)
{
}
template <typename OtherIntType>
IntType& operator= (OtherIntType o) const
{
*pi = o;
return *pi;
}
};
/** Parse 8-bit unsigned integer. */
template <typename InputStream>
InputStream& operator>> (InputStream& is, integer_holder <std::uint8_t> 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 <typename IntType>
integer_holder <IntType> integer (IntType& i)
{
return integer_holder <IntType> (i);
}
}
}
}
#endif

View File

@@ -24,43 +24,22 @@ namespace IP {
Endpoint from_asio (boost::asio::ip::address const& address) Endpoint from_asio (boost::asio::ip::address const& address)
{ {
if (address.is_v4 ()) return Endpoint {address};
{
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();
} }
Endpoint from_asio (boost::asio::ip::tcp::endpoint const& endpoint) 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) boost::asio::ip::address to_asio_address (Endpoint const& endpoint)
{ {
if (endpoint.address().is_v4()) return endpoint.address();
{
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 ());
} }
boost::asio::ip::tcp::endpoint to_asio_endpoint (Endpoint const& endpoint) boost::asio::ip::tcp::endpoint to_asio_endpoint (Endpoint const& endpoint)
{ {
return boost::asio::ip::tcp::endpoint ( return boost::asio::ip::tcp::endpoint {endpoint.address(), endpoint.port()};
to_asio_address (endpoint), endpoint.port());
} }
} }

View File

@@ -21,7 +21,6 @@
#endif #endif
#include <ripple/beast/net/IPAddressV4.h> #include <ripple/beast/net/IPAddressV4.h>
#include <ripple/beast/net/detail/Parse.h>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
@@ -29,154 +28,26 @@
namespace beast { namespace beast {
namespace IP { 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, bool> 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 <true> AddressV4::operator[] (std::size_t index) const
{
switch (index)
{
default:
throw std::out_of_range ("bad array index");
case 0: return Proxy <true> (24, &value);
case 1: return Proxy <true> (16, &value);
case 2: return Proxy <true> ( 8, &value);
case 3: return Proxy <true> ( 0, &value);
};
};
AddressV4::Proxy <false> AddressV4::operator[] (std::size_t index)
{
switch (index)
{
default:
throw std::out_of_range ("bad array index");
case 0: return Proxy <false> (24, &value);
case 1: return Proxy <false> (16, &value);
case 2: return Proxy <false> ( 8, &value);
case 3: return Proxy <false> ( 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) bool is_private (AddressV4 const& addr)
{ {
return return
((addr.value & 0xff000000) == 0x0a000000) || // Prefix /8, 10. #.#.# ((addr.to_ulong() & 0xff000000) == 0x0a000000) || // Prefix /8, 10. #.#.#
((addr.value & 0xfff00000) == 0xac100000) || // Prefix /12 172. 16.#.# - 172.31.#.# ((addr.to_ulong() & 0xfff00000) == 0xac100000) || // Prefix /12 172. 16.#.# - 172.31.#.#
((addr.value & 0xffff0000) == 0xc0a80000) || // Prefix /16 192.168.#.# ((addr.to_ulong() & 0xffff0000) == 0xc0a80000) || // Prefix /16 192.168.#.#
is_loopback (addr); addr.is_loopback();
} }
bool is_public (AddressV4 const& addr) bool is_public (AddressV4 const& addr)
{ {
return return
! is_private (addr) && ! is_private (addr) &&
! is_multicast (addr); ! addr.is_multicast();
} }
//------------------------------------------------------------------------------ char get_class (AddressV4 const& addr)
std::string to_string (AddressV4 const& addr)
{ {
std::string s; static char const* table = "AAAABBCD";
s.reserve (15); return table [(addr.to_ulong() & 0xE0000000) >> 29];
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;
} }
} }

View File

@@ -21,62 +21,25 @@
#endif #endif
#include <ripple/beast/net/IPAddressV6.h> #include <ripple/beast/net/IPAddressV6.h>
#include <ripple/beast/net/IPAddressV4.h>
namespace beast { namespace beast {
namespace IP { namespace IP {
//------------------------------------------------------------------------------ bool is_private (AddressV6 const& addr)
bool is_loopback (AddressV6 const&)
{ {
// VFALCO TODO return ((addr.to_bytes()[0] & 0xfd) || // TODO fc00::/8 too ?
assert(false); (addr.is_v4_mapped() && is_private(addr.to_v4())) );
return false;
} }
bool is_unspecified (AddressV6 const&) bool is_public (AddressV6 const& addr)
{ {
// VFALCO TODO // TODO is this correct?
assert(false); return
return false; ! 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;
}
} }
} }

View File

@@ -21,7 +21,6 @@
#endif #endif
#include <ripple/beast/net/IPEndpoint.h> #include <ripple/beast/net/IPEndpoint.h>
#include <ripple/beast/net/detail/Parse.h>
namespace beast { namespace beast {
namespace IP { namespace IP {
@@ -44,7 +43,7 @@ std::pair <Endpoint, bool> Endpoint::from_string_checked (std::string const& s)
is >> endpoint; is >> endpoint;
if (! is.fail() && is.rdbuf()->in_avail() == 0) if (! is.fail() && is.rdbuf()->in_avail() == 0)
return std::make_pair (endpoint, true); 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) Endpoint Endpoint::from_string (std::string const& s)
@@ -53,69 +52,26 @@ Endpoint Endpoint::from_string (std::string const& s)
from_string_checked (s)); from_string_checked (s));
if (result.second) if (result.second)
return result.first; return result.first;
return Endpoint(); 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<unsigned char>(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();
} }
std::string Endpoint::to_string () const std::string Endpoint::to_string () const
{ {
std::string s (address ().to_string ()); std::string s;
if (port() != 0) s.reserve(
s = s + ":" + std::to_string (port()); (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; return s;
} }
@@ -138,34 +94,88 @@ bool operator< (Endpoint const& lhs, Endpoint const& rhs)
std::istream& operator>> (std::istream& is, Endpoint& endpoint) 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; while (is && is.rdbuf()->in_avail() > 0 && is.get(i))
is >> addr;
if (is.fail())
return is;
if (is.rdbuf()->in_avail()>0)
{ {
char c; // NOTE: There is a legacy data format
is.get(c); // that allowed space to be used as address / port separator
if (c != ':') // 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<unsigned char>(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(); is.unget();
endpoint = Endpoint (addr); is.setstate (std::ios_base::failbit);
return is; return is;
} }
}
if (readTo == ']' && is.rdbuf()->in_avail() > 0)
{
is.get(i);
if (! (isspace(static_cast<unsigned char>(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; Port port;
is >> port; is >> port;
if (is.fail()) if (is.fail())
return is; return is;
endpoint = Endpoint (addr, port); endpoint = Endpoint (addr, port);
return is;
} }
else
endpoint = Endpoint (addr);
endpoint = Endpoint (addr);
return is; return is;
} }

View File

@@ -26,7 +26,6 @@
#include <ripple/beast/net/IPEndpoint.h> #include <ripple/beast/net/IPEndpoint.h>
#include <boost/beast/core/string.hpp> #include <boost/beast/core/string.hpp>
#include <ripple/beast/utility/Journal.h> #include <ripple/beast/utility/Journal.h>
#include <boost/asio/ip/tcp.hpp> // VFALCO FIX: This include should not be here
#include <boost/filesystem.hpp> // VFALCO FIX: This include should not be here #include <boost/filesystem.hpp> // VFALCO FIX: This include should not be here
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
@@ -174,8 +173,7 @@ public:
std::size_t WORKERS = 0; std::size_t WORKERS = 0;
// These override the command line client settings // These override the command line client settings
boost::optional<boost::asio::ip::address_v4> rpc_ip; boost::optional<beast::IP::Endpoint> rpc_ip;
boost::optional<std::uint16_t> rpc_port;
std::unordered_set<uint256, beast::uhash<>> features; std::unordered_set<uint256, beast::uhash<>> features;

View File

@@ -1355,9 +1355,10 @@ rpcClient(std::vector<std::string> const& args,
} }
if (config.rpc_ip) if (config.rpc_ip)
setup.client.ip = config.rpc_ip->to_string(); {
if (config.rpc_port) setup.client.ip = config.rpc_ip->address().to_string();
setup.client.port = *config.rpc_port; setup.client.port = config.rpc_ip->port();
}
Json::Value jvParams (Json::arrayValue); Json::Value jvParams (Json::arrayValue);

View File

@@ -1048,11 +1048,9 @@ setup_Overlay (BasicConfig const& config)
set (ip, "public_ip", section); set (ip, "public_ip", section);
if (! ip.empty ()) if (! ip.empty ())
{ {
bool valid; boost::system::error_code ec;
std::tie (setup.public_ip, valid) = setup.public_ip = beast::IP::Address::from_string (ip, ec);
beast::IP::Address::from_string (ip); if (ec || beast::IP::is_private (setup.public_ip))
if (! valid || ! setup.public_ip.is_v4() ||
is_private (setup.public_ip))
Throw<std::runtime_error> ("Configured public IP is invalid"); Throw<std::runtime_error> ("Configured public IP is invalid");
} }
return setup; return setup;

View File

@@ -1019,37 +1019,75 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMEndpoints> const& m)
std::vector <PeerFinder::Endpoint> endpoints; std::vector <PeerFinder::Endpoint> endpoints;
endpoints.reserve (m->endpoints().size()); if (m->endpoints_v2().size())
for (int i = 0; i < m->endpoints ().size (); ++i)
{ {
PeerFinder::Endpoint endpoint; endpoints.reserve (m->endpoints_v2().size());
protocol::TMEndpoint const& tm (m->endpoints(i)); for (auto const& tm : m->endpoints_v2 ())
// hops
endpoint.hops = tm.hops();
// ipv4
if (endpoint.hops > 0)
{ {
in_addr addr; // these endpoint strings support ipv4 and ipv6
addr.s_addr = tm.ipv4().ipv4(); auto result = beast::IP::Endpoint::from_string_checked(tm.endpoint());
beast::IP::AddressV4 v4 (ntohl (addr.s_addr)); if (! result.second)
endpoint.address = beast::IP::Endpoint (v4, tm.ipv4().ipv4port ()); {
} JLOG(p_journal_.error()) <<
else "failed to parse incoming endpoint: {" <<
{ tm.endpoint() << "}";
// This Endpoint describes the peer we are connected to. continue;
// 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); // 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()) if (! endpoints.empty())

View File

@@ -514,16 +514,24 @@ PeerImp::sendEndpoints (FwdIt first, FwdIt last)
for (;first != last; ++first) for (;first != last; ++first)
{ {
auto const& ep = *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()); protocol::TMEndpoint& tme (*tm.add_endpoints());
if (ep.address.is_v4()) if (ep.address.is_v4())
tme.mutable_ipv4()->set_ipv4( tme.mutable_ipv4()->set_ipv4(
beast::toNetworkByteOrder (ep.address.to_v4().value)); beast::toNetworkByteOrder<std::uint32_t> (
ep.address.to_v4().to_ulong()));
else else
tme.mutable_ipv4()->set_ipv4(0); tme.mutable_ipv4()->set_ipv4(0);
tme.mutable_ipv4()->set_ipv4port (ep.address.port()); tme.mutable_ipv4()->set_ipv4port (ep.address.port());
tme.set_hops (ep.hops); 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 <Message> (tm, protocol::mtENDPOINTS)); send (std::make_shared <Message> (tm, protocol::mtENDPOINTS));
} }

View File

@@ -114,19 +114,14 @@ buildHello (
TokenType::NodePublic, TokenType::NodePublic,
app.nodeIdentity().first)); app.nodeIdentity().first));
h.set_nodeproof (sig.data(), sig.size()); h.set_nodeproof (sig.data(), sig.size());
// h.set_ipv4port (portNumber); // ignored now
h.set_testnet (false); h.set_testnet (false);
if (remote.is_v4()) if (beast::IP::is_public (remote))
{ {
auto addr = remote.to_v4 (); // Connection is to a public IP
if (is_public (addr)) h.set_remote_ip_str (remote.to_string());
{ if (! public_ip.is_unspecified())
// Connection is to a public IP h.set_local_ip_str (public_ip.to_string());
h.set_remote_ip (addr.value);
if (public_ip != beast::IP::Address())
h.set_local_ip (public_ip.to_v4().value);
}
} }
// We always advertise ourselves as private in the HELLO message. This // 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 ( h.insert ("Previous-Ledger", boost::beast::detail::base64_encode (
hello.ledgerprevious())); hello.ledgerprevious()));
if (hello.has_local_ip()) if (hello.has_local_ip_str())
h.insert ("Local-IP", beast::IP::to_string ( h.insert ("Local-IP", hello.local_ip_str());
beast::IP::AddressV4(hello.local_ip())));
if (hello.has_remote_ip()) if (hello.has_remote_ip())
h.insert ("Remote-IP", beast::IP::to_string ( h.insert ("Remote-IP", hello.remote_ip_str());
beast::IP::AddressV4(hello.remote_ip())));
} }
std::vector<ProtocolVersion> std::vector<ProtocolVersion>
@@ -306,14 +299,16 @@ parseHello (bool request, boost::beast::http::fields const& h, beast::Journal jo
auto const iter = h.find ("Local-IP"); auto const iter = h.find ("Local-IP");
if (iter != h.end()) if (iter != h.end())
{ {
bool valid; boost::system::error_code ec;
beast::IP::Address address; auto address =
std::tie (address, valid) = beast::IP::Address::from_string (iter->value().to_string(), ec);
beast::IP::Address::from_string (iter->value().to_string()); if (ec)
if (!valid) {
JLOG(journal.warn()) << "invalid Local-IP: "
<< iter->value().to_string();
return boost::none; 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"); auto const iter = h.find ("Remote-IP");
if (iter != h.end()) if (iter != h.end())
{ {
bool valid; boost::system::error_code ec;
beast::IP::Address address; auto address =
std::tie (address, valid) = beast::IP::Address::from_string (iter->value().to_string(), ec);
beast::IP::Address::from_string (iter->value().to_string()); if (ec)
if (!valid) {
JLOG(journal.warn()) << "invalid Remote-IP: "
<< iter->value().to_string();
return boost::none; 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; return boost::none;
} }
if (h.has_local_ip () && if (h.has_local_ip_str () &&
is_public (remote) && is_public (remote))
remote.is_v4 () &&
(remote.to_v4().value != h.local_ip ()))
{ {
// Remote asked us to confirm connection is from boost::system::error_code ec;
// correct IP auto local_ip =
JLOG(journal.info()) << beast::IP::Address::from_string (h.local_ip_str(), ec);
"Hello: Disconnect: Peer IP is " << if (ec)
beast::IP::to_string (remote.to_v4()) {
<< " not " << JLOG(journal.warn()) << "invalid local-ip: " << h.local_ip_str();
beast::IP::to_string (beast::IP::AddressV4 (h.local_ip())); return boost::none;
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) && if (h.has_remote_ip_str () &&
(public_ip != beast::IP::Address()) && is_public (remote) &&
(h.remote_ip() != public_ip.to_v4().value)) (! beast::IP::is_unspecified(public_ip)))
{ {
// We know our public IP and peer reports connection boost::system::error_code ec;
// from some other IP auto remote_ip =
JLOG(journal.info()) << beast::IP::Address::from_string (h.remote_ip_str(), ec);
"Hello: Disconnect: Our IP is " << if (ec)
beast::IP::to_string (public_ip.to_v4()) {
<< " not " << JLOG(journal.warn()) << "invalid remote-ip: " << h.remote_ip_str();
beast::IP::to_string (beast::IP::AddressV4 (h.remote_ip())); return boost::none;
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; return publicKey;

View File

@@ -618,8 +618,15 @@ public:
{ {
Endpoint ep; Endpoint ep;
ep.hops = 0; 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 ( ep.address = beast::IP::Endpoint (
beast::IP::AddressV4 ()).at_port ( beast::IP::AddressV6 ()).at_port (
config_.listeningPort); config_.listeningPort);
for (auto& t : targets) for (auto& t : targets)
t.insert (ep); t.insert (ep);

View File

@@ -48,7 +48,7 @@ public:
{ {
beast::IP::Endpoint ep (beast::IP::Endpoint::from_string (m_strings [i])); beast::IP::Endpoint ep (beast::IP::Endpoint::from_string (m_strings [i]));
if (is_unspecified (ep)) 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)) if (! is_unspecified (ep))
results.addresses.push_back (ep); results.addresses.push_back (ep);
} }

View File

@@ -89,15 +89,17 @@ message TMHello
required bytes nodeProof = 4; required bytes nodeProof = 4;
optional string fullVersion = 5; optional string fullVersion = 5;
optional uint64 netTime = 6; optional uint64 netTime = 6;
optional uint32 ipv4Port = 7; optional uint32 ipv4Port = 7; // NOT USED
optional uint32 ledgerIndex = 8; optional uint32 ledgerIndex = 8;
optional bytes ledgerClosed = 9; // our last closed ledger optional bytes ledgerClosed = 9; // our last closed ledger
optional bytes ledgerPrevious = 10; // the ledger before the last closed ledger optional bytes ledgerPrevious = 10; // the ledger before the last closed ledger
optional bool nodePrivate = 11; // Request to not forward IP. optional bool nodePrivate = 11; // Request to not forward IP.
optional TMProofWork proofOfWork = 12; // request/provide proof of work optional TMProofWork proofOfWork = 12; // request/provide proof of work
optional bool testNet = 13; // Running as testnet. optional bool testNet = 13; // Running as testnet.
optional uint32 local_ip = 14; // our public IP optional uint32 local_ip = 14; // NOT USED -- our public IP
optional uint32 remote_ip = 15; // IP we see connection from 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 // The status of a node in our cluster
@@ -234,6 +236,7 @@ message TMIPv4Endpoint
required uint32 ipv4Port = 2; required uint32 ipv4Port = 2;
} }
// this message is obsolete/no longer procesed
message TMPeers message TMPeers
{ {
repeated TMIPv4Endpoint nodes = 1; repeated TMIPv4Endpoint nodes = 1;
@@ -254,6 +257,15 @@ message TMEndpoints
required uint32 version = 1; required uint32 version = 1;
repeated TMEndpoint endpoints = 2; 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 message TMIndexedObject

View File

@@ -41,7 +41,7 @@ ipAllowed (beast::IP::Address const& remoteIp,
std::vector<beast::IP::Address> const& adminIp) std::vector<beast::IP::Address> const& adminIp)
{ {
return std::find_if (adminIp.begin (), adminIp.end (), 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 (); ip == remoteIp; }) != adminIp.end ();
} }

View File

@@ -1041,10 +1041,11 @@ setup_Client (ServerHandler::Setup& setup)
return; return;
setup.client.secure = setup.client.secure =
iter->protocol.count("https") > 0; iter->protocol.count("https") > 0;
setup.client.ip = iter->ip.to_string(); setup.client.ip =
// VFALCO HACK! to make localhost work beast::IP::is_unspecified(iter->ip) ?
if (setup.client.ip == "0.0.0.0") // VFALCO HACK! to make localhost work
setup.client.ip = "127.0.0.1"; (iter->ip.is_v6() ? "::1" : "127.0.0.1") :
iter->ip.to_string();
setup.client.port = iter->port; setup.client.port = iter->port;
setup.client.user = iter->user; setup.client.user = iter->user;
setup.client.password = iter->password; setup.client.password = iter->password;

View File

@@ -95,7 +95,7 @@ populate (Section const& section, std::string const& field, std::ostream& log,
{ {
if (! allowAllIps) if (! allowAllIps)
{ {
log << "0.0.0.0 not allowed'" << log << addr.first.address() << " not allowed'" <<
"' for key '" << field << "' in [" << "' for key '" << field << "' in [" <<
section.name () << "]"; section.name () << "]";
Throw<std::exception> (); Throw<std::exception> ();
@@ -108,8 +108,8 @@ populate (Section const& section, std::string const& field, std::ostream& log,
if (has_any && ! ips->empty ()) if (has_any && ! ips->empty ())
{ {
log << "IP specified along with 0.0.0.0 '" << ip << log << "IP specified along with " << addr.first.address() <<
"' for key '" << field << "' in [" << " '" << ip << "' for key '" << field << "' in [" <<
section.name () << "]"; section.name () << "]";
Throw<std::exception> (); Throw<std::exception> ();
} }

View File

@@ -172,21 +172,12 @@ private:
while (list2.size () < listSize) while (list2.size () < listSize)
list2.push_back (randomValidator()); 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 sequence = 1;
auto const version = 1; auto const version = 1;
NetClock::time_point const expiration = NetClock::time_point const expiration =
env.timeKeeper().now() + 3600s; env.timeKeeper().now() + 3600s;
TrustedPublisherServer server1( TrustedPublisherServer server1(
ep1,
env.app().getIOService(), env.app().getIOService(),
pubSigningKeys1, pubSigningKeys1,
manifest1, manifest1,
@@ -196,7 +187,6 @@ private:
list1); list1);
TrustedPublisherServer server2( TrustedPublisherServer server2(
ep2,
env.app().getIOService(), env.app().getIOService(),
pubSigningKeys2, pubSigningKeys2,
manifest2, manifest2,
@@ -205,14 +195,13 @@ private:
version, version,
list2); list2);
std::uint16_t const port1 = server1.local_endpoint().port(); std::stringstream url1, url2;
std::uint16_t const port2 = server2.local_endpoint().port(); url1 << "http://" << server1.local_endpoint() << "/validators";
url2 << "http://" << server2.local_endpoint() << "/validators";
{ {
// fetch single site // fetch single site
std::vector<std::string> cfgSites( std::vector<std::string> cfgSites({ url1.str() });
{"http://127.0.0.1:" + std::to_string(port1) + "/validators"});
auto sites = std::make_unique<ValidatorSite> ( auto sites = std::make_unique<ValidatorSite> (
env.app().getIOService(), env.app().validators(), journal); env.app().getIOService(), env.app().validators(), journal);
@@ -229,9 +218,7 @@ private:
} }
{ {
// fetch multiple sites // fetch multiple sites
std::vector<std::string> cfgSites({ std::vector<std::string> cfgSites({ url1.str(), url2.str() });
"http://127.0.0.1:" + std::to_string(port1) + "/validators",
"http://127.0.0.1:" + std::to_string(port2) + "/validators"});
auto sites = std::make_unique<ValidatorSite> ( auto sites = std::make_unique<ValidatorSite> (
env.app().getIOService(), env.app().validators(), journal); env.app().getIOService(), env.app().validators(), journal);

View File

@@ -77,6 +77,9 @@ public:
BEAST_EXPECT(parseUrl (pUrl, "Mixed://domain/path")); BEAST_EXPECT(parseUrl (pUrl, "Mixed://domain/path"));
BEAST_EXPECT(pUrl.scheme == "mixed"); BEAST_EXPECT(pUrl.scheme == "mixed");
BEAST_EXPECT(pUrl.path == "/path"); 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 () void testToString ()

View File

@@ -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 <ripple/beast/net/IPEndpoint.h>
#include <ripple/basics/random.h>
namespace beast {
namespace IP {
inline Endpoint randomEP (bool v4 = true)
{
using namespace ripple;
auto dv4 = []() -> AddressV4::bytes_type {
return {{
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX))
}};
};
auto dv6 = []() -> AddressV6::bytes_type {
return {{
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX))
}};
};
return Endpoint {
v4 ? Address { AddressV4 {dv4()} } : Address{ AddressV6 {dv6()} },
rand_int<std::uint16_t>(1, UINT16_MAX)
};
}
}
}

View File

@@ -23,10 +23,12 @@
#endif #endif
#include <ripple/beast/net/IPEndpoint.h> #include <ripple/beast/net/IPEndpoint.h>
#include <ripple/beast/net/detail/Parse.h>
#include <ripple/beast/unit_test.h> #include <ripple/beast/unit_test.h>
#include <ripple/basics/random.h>
#include <boost/algorithm/string.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/predef.h>
#include <test/beast/IPEndpointCommon.h>
#include <typeinfo> #include <typeinfo>
namespace beast { namespace beast {
@@ -37,91 +39,111 @@ namespace IP {
class IPEndpoint_test : public unit_test::suite class IPEndpoint_test : public unit_test::suite
{ {
public: 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 <AddressV4, bool> const result ( boost::system::error_code ec;
AddressV4::from_string (s)); Address const result {Address::from_string (s, ec)};
if (! BEAST_EXPECTS(! ec, ec.message()))
if (BEAST_EXPECT(result.second)) return;
{ if (! BEAST_EXPECTS(result.is_v4(), s + " not v4"))
if (BEAST_EXPECT(result.first.value == value)) return;
{ if (! BEAST_EXPECTS(result.to_v4().to_ulong() == value,
BEAST_EXPECT(to_string (result.first) == s); 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 () void testAddressV4 ()
{ {
testcase ("AddressV4"); testcase ("AddressV4");
BEAST_EXPECT(AddressV4().value == 0); BEAST_EXPECT(AddressV4{}.to_ulong() == 0);
BEAST_EXPECT(is_unspecified (AddressV4())); BEAST_EXPECT(is_unspecified (AddressV4{}));
BEAST_EXPECT(AddressV4(0x01020304).value == 0x01020304); BEAST_EXPECT(AddressV4{0x01020304}.to_ulong() == 0x01020304);
BEAST_EXPECT(AddressV4(1, 2, 3, 4).value == 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); AddressV4 const v1 {1};
BEAST_EXPECT(AddressV4(v1).value == 1); BEAST_EXPECT(AddressV4{v1}.to_ulong() == 1);
{ {
AddressV4 v; AddressV4 v;
v = v1; v = v1;
BEAST_EXPECT(v.value == v1.value); BEAST_EXPECT(v.to_ulong() == v1.to_ulong());
} }
{ {
AddressV4 v; AddressV4 v;
v [0] = 1; auto d = v.to_bytes();
v [1] = 2; d[0] = 1;
v [2] = 3; d[1] = 2;
v [3] = 4; d[2] = 3;
BEAST_EXPECT(v.value == 0x01020304); 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); shouldParseAddrV4 ("1.2.3.4", 0x01020304);
shouldParseV4 ("255.255.255.255", 0xffffffff); shouldParseAddrV4 ("255.255.255.255", 0xffffffff);
shouldParseV4 ("0.0.0.0", 0); shouldParseAddrV4 ("0.0.0.0", 0);
failParseV4 ("."); failParseAddr (".");
failParseV4 (".."); failParseAddr ("..");
failParseV4 ("..."); failParseAddr ("...");
failParseV4 ("...."); failParseAddr ("....");
failParseV4 ("1"); #if BOOST_OS_WINDOWS
failParseV4 ("1."); // WINDOWS bug in asio - I don't think these should parse
failParseV4 ("1.2"); // at all, and in-fact they do not on mac/linux
failParseV4 ("1.2."); shouldParseAddrV4 ("1", 0x00000001, "0.0.0.1");
failParseV4 ("1.2.3"); shouldParseAddrV4 ("1.2", 0x01000002, "1.0.0.2");
failParseV4 ("1.2.3."); shouldParseAddrV4 ("1.2.3", 0x01020003, "1.2.0.3");
failParseV4 ("256.0.0.0"); #else
failParseV4 ("-1.2.3.4"); 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 () void testAddressV4Proxy ()
{ {
testcase ("AddressV4::Proxy"); testcase ("AddressV4::Bytes");
AddressV4 v4 (10, 0, 0, 1); AddressV4::bytes_type d1 = {{10,0,0,1}};
BEAST_EXPECT(v4[0]==10); AddressV4 v4 {d1};
BEAST_EXPECT(v4[1]==0); BEAST_EXPECT(v4.to_bytes()[0]==10);
BEAST_EXPECT(v4[2]==0); BEAST_EXPECT(v4.to_bytes()[1]==0);
BEAST_EXPECT(v4[3]==1); BEAST_EXPECT(v4.to_bytes()[2]==0);
BEAST_EXPECT(v4.to_bytes()[3]==1);
BEAST_EXPECT((~((0xff)<<16)) == 0xff00ffff); BEAST_EXPECT((~((0xff)<<16)) == 0xff00ffff);
v4[1] = 10; auto d2 = v4.to_bytes();
BEAST_EXPECT(v4[0]==10); d2[1] = 10;
BEAST_EXPECT(v4[1]==10); v4 = AddressV4{d2};
BEAST_EXPECT(v4[2]==0); BEAST_EXPECT(v4.to_bytes()[0]==10);
BEAST_EXPECT(v4[3]==1); 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"); testcase ("Address");
std::pair <Address, bool> result ( boost::system::error_code ec;
Address::from_string ("1.2.3.4")); Address result {Address::from_string ("1.2.3.4", ec)};
BEAST_EXPECT(result.second); AddressV4::bytes_type d = {{1,2,3,4}};
if (BEAST_EXPECT(result.first.is_v4 ())) BEAST_EXPECT(! ec);
BEAST_EXPECT(result.first.to_v4() == AddressV4 (1, 2, 3, 4)); 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 () void testEndpoint ()
{ {
testcase ("Endpoint"); testcase ("Endpoint");
{ shouldParseEPV4("1.2.3.4", {{1,2,3,4}}, 0);
std::pair <Endpoint, bool> result ( shouldParseEPV4("1.2.3.4:5", {{1,2,3,4}}, 5);
Endpoint::from_string_checked ("1.2.3.4")); shouldParseEPV4("1.2.3.4 5", {{1,2,3,4}}, 5, "1.2.3.4:5");
BEAST_EXPECT(result.second); shouldParseEPV6(
if (BEAST_EXPECT(result.first.address().is_v4 ())) "2001:db8:a0b:12f0::1",
{ {{32, 01, 13, 184, 10, 11, 18, 240, 0, 0, 0, 0, 0, 0, 0, 1}},
BEAST_EXPECT(result.first.address().to_v4() == 0);
AddressV4 (1, 2, 3, 4)); shouldParseEPV6(
BEAST_EXPECT(result.first.port() == 0); "[2001:db8:a0b:12f0::1]:8",
BEAST_EXPECT(to_string (result.first) == "1.2.3.4"); {{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}},
std::pair <Endpoint, bool> result ( 65535);
Endpoint::from_string_checked ("1.2.3.4:5")); shouldParseEPV6(
BEAST_EXPECT(result.second); "2001:2002:2003:2004:2005:2006:2007:2008 65535",
if (BEAST_EXPECT(result.first.address().is_v4 ())) {{32, 1, 32, 2, 32, 3, 32, 4, 32, 5, 32, 6, 32, 7, 32, 8}},
{ 65535,
BEAST_EXPECT(result.first.address().to_v4() == "[2001:2002:2003:2004:2005:2006:2007:2008]:65535");
AddressV4 (1, 2, 3, 4));
BEAST_EXPECT(result.first.port() == 5);
BEAST_EXPECT(to_string (result.first) == "1.2.3.4:5");
}
}
Endpoint ep; 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_unspecified (ep));
BEAST_EXPECT(! is_public (ep)); BEAST_EXPECT(! is_public (ep));
BEAST_EXPECT( is_private (ep)); BEAST_EXPECT( is_private (ep));
BEAST_EXPECT(! is_multicast (ep)); BEAST_EXPECT(! is_multicast (ep));
BEAST_EXPECT( is_loopback (ep)); BEAST_EXPECT( is_loopback (ep));
BEAST_EXPECT(to_string (ep) == "127.0.0.1:80"); 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)); d = {{10,0,0,1}};
BEAST_EXPECT(AddressV4::get_class (ep.to_v4()) == 'A'); ep = Endpoint (AddressV4 {d});
BEAST_EXPECT(get_class (ep.to_v4()) == 'A');
BEAST_EXPECT(! is_unspecified (ep)); BEAST_EXPECT(! is_unspecified (ep));
BEAST_EXPECT(! is_public (ep)); BEAST_EXPECT(! is_public (ep));
BEAST_EXPECT( is_private (ep)); BEAST_EXPECT( is_private (ep));
BEAST_EXPECT(! is_multicast (ep)); BEAST_EXPECT(! is_multicast (ep));
BEAST_EXPECT(! is_loopback (ep)); BEAST_EXPECT(! is_loopback (ep));
BEAST_EXPECT(to_string (ep) == "10.0.0.1"); 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_unspecified (ep));
BEAST_EXPECT( is_public (ep)); BEAST_EXPECT( is_public (ep));
BEAST_EXPECT(! is_private (ep)); BEAST_EXPECT(! is_private (ep));
BEAST_EXPECT(! is_multicast (ep)); BEAST_EXPECT(! is_multicast (ep));
BEAST_EXPECT(! is_loopback (ep)); BEAST_EXPECT(! is_loopback (ep));
BEAST_EXPECT(to_string (ep) == "166.78.151.147"); 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"); ep = Endpoint::from_string ("192.0.2.112");
BEAST_EXPECT(! is_unspecified (ep)); 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"); auto const ep1 = Endpoint::from_string ("192.0.2.112:2016");
BEAST_EXPECT(! is_unspecified (ep1)); BEAST_EXPECT(! is_unspecified (ep1));
@@ -207,21 +313,21 @@ public:
BEAST_EXPECT(ep1.port() == 2016); BEAST_EXPECT(ep1.port() == 2016);
auto const ep2 = 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(! is_unspecified (ep2));
BEAST_EXPECT(ep.address() == ep2.address()); BEAST_EXPECT(ep.address() == ep2.address());
BEAST_EXPECT(ep2.port() == 2016); BEAST_EXPECT(ep2.port() == 2016);
BEAST_EXPECT(ep1 == ep2); BEAST_EXPECT(ep1 == ep2);
auto const ep3 = 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(! is_unspecified (ep3));
BEAST_EXPECT(ep.address() == ep3.address()); BEAST_EXPECT(ep.address() == ep3.address());
BEAST_EXPECT(ep3.port() == 2016); BEAST_EXPECT(ep3.port() == 2016);
BEAST_EXPECT(ep2 == ep3); BEAST_EXPECT(ep2 == ep3);
auto const ep4 = 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(! is_unspecified (ep4));
BEAST_EXPECT(ep.address() == ep4.address()); BEAST_EXPECT(ep.address() == ep4.address());
BEAST_EXPECT(ep4.port() == 2016); BEAST_EXPECT(ep4.port() == 2016);
@@ -232,87 +338,79 @@ public:
BEAST_EXPECT(to_string(ep1) == to_string(ep4)); 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: // Failures:
BEAST_EXPECT(is_unspecified ( failParseEP ("192.0.2.112:port");
Endpoint::from_string ("192.0.2.112:port"))); failParseEP ("ip:port");
BEAST_EXPECT(is_unspecified ( failParseEP ("");
Endpoint::from_string_altform ("192.0.2.112:port"))); failParseEP ("1.2.3.256");
BEAST_EXPECT(is_unspecified (
Endpoint::from_string_altform ("192.0.2.112 port")));
BEAST_EXPECT(is_unspecified ( #if BOOST_OS_WINDOWS
Endpoint::from_string ("ip:port"))); // windows asio bugs...false positives
BEAST_EXPECT(is_unspecified ( shouldParseEPV4 ("255", {{0,0,0,255}}, 0, "0.0.0.255");
Endpoint::from_string_altform ("ip:port"))); shouldParseEPV4 ("512", {{0,0,2,0}}, 0, "0.0.2.0");
BEAST_EXPECT(is_unspecified ( shouldParseEPV4 ("1.2.3:80", {{1,2,0,3}}, 80, "1.2.0.3:80");
Endpoint::from_string_altform ("ip port"))); #else
failParseEP ("255");
failParseEP ("512");
failParseEP ("1.2.3:80");
#endif
BEAST_EXPECT(is_unspecified ( failParseEP ("1.2.3.4:65536");
Endpoint::from_string(""))); failParseEP ("1.2.3.4:89119");
BEAST_EXPECT(is_unspecified ( failParseEP ("1.2.3:89119");
Endpoint::from_string_altform(""))); 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 ( // test with hashed container
Endpoint::from_string("255"))); std::unordered_set<Endpoint> eps;
BEAST_EXPECT(is_unspecified ( constexpr auto items {100};
Endpoint::from_string_altform("255"))); float max_lf {0};
for (auto i = 0; i < items; ++i)
BEAST_EXPECT(is_unspecified ( {
Endpoint::from_string("512"))); eps.insert(randomEP(ripple::rand_int(0,1) == 1));
BEAST_EXPECT(is_unspecified ( max_lf = std::max(max_lf, eps.load_factor());
Endpoint::from_string_altform("512"))); }
BEAST_EXPECT(eps.bucket_count() >= items);
BEAST_EXPECT(is_unspecified ( BEAST_EXPECT(max_lf > 0.90);
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")));
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
template <typename T> template <typename T>
bool parse (char const* text, T& t) bool parse (std::string const& text, T& t)
{ {
std::string input (text); std::istringstream stream {text};
std::istringstream stream (input);
stream >> t; stream >> t;
return !stream.fail(); return !stream.fail();
} }
template <typename T> template <typename T>
void shouldPass (char const* text) void shouldPass (std::string const& text, std::string const& normal="")
{ {
using namespace std::literals;
T t; T t;
BEAST_EXPECT(parse (text, 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 <typename T> template <typename T>
void shouldFail (char const* text) void shouldFail (std::string const& text)
{ {
T t; T t;
unexpected (parse (text, t)); unexpected (parse (text, t), text + " should not parse");
} }
template <typename T> template <typename T>
@@ -325,14 +423,30 @@ public:
shouldPass <T> ("168.127.149.132"); shouldPass <T> ("168.127.149.132");
shouldPass <T> ("168.127.149.132:80"); shouldPass <T> ("168.127.149.132:80");
shouldPass <T> ("168.127.149.132:54321"); shouldPass <T> ("168.127.149.132:54321");
shouldPass <T> ("2001:db8:a0b:12f0::1");
shouldPass <T> ("[2001:db8:a0b:12f0::1]:8");
shouldPass <T> ("2001:db8:a0b:12f0::1 8", "[2001:db8:a0b:12f0::1]:8");
shouldPass <T> ("[::1]:8");
shouldPass <T> ("[2001:2002:2003:2004:2005:2006:2007:2008]:65535");
shouldFail <T> ("");
shouldFail <T> ("255");
shouldFail <T> ("512");
shouldFail <T> ("1.2.3.256"); shouldFail <T> ("1.2.3.256");
shouldFail <T> ("");
#if BOOST_OS_WINDOWS
// windows asio bugs...false positives
shouldPass <T> ("512", "0.0.2.0");
shouldPass <T> ("255", "0.0.0.255");
shouldPass <T> ("1.2.3:80", "1.2.0.3:80");
#else
shouldFail <T> ("512");
shouldFail <T> ("255");
shouldFail <T> ("1.2.3:80"); shouldFail <T> ("1.2.3:80");
#endif
shouldFail <T> ("1.2.3:65536"); shouldFail <T> ("1.2.3:65536");
shouldFail <T> ("1.2.3:72131"); shouldFail <T> ("1.2.3:72131");
shouldFail <T> ("[::1]:89119");
shouldFail <T> ("[::az]:1");
shouldFail <T> ("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:1");
shouldFail <T> ("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:12345");
} }
void run () override void run () override
@@ -341,7 +455,6 @@ public:
testAddressV4Proxy(); testAddressV4Proxy();
testAddress (); testAddress ();
testEndpoint (); testEndpoint ();
testParse <Endpoint> ("Parse Endpoint"); testParse <Endpoint> ("Parse Endpoint");
} }
}; };

View File

@@ -23,6 +23,7 @@
#include <ripple/protocol/SecretKey.h> #include <ripple/protocol/SecretKey.h>
#include <ripple/protocol/Sign.h> #include <ripple/protocol/Sign.h>
#include <ripple/basics/strHex.h> #include <ripple/basics/strHex.h>
#include <test/jtx/envconfig.h>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/beast/core/detail/base64.hpp> #include <boost/beast/core/detail/base64.hpp>
#include <boost/beast/http.hpp> #include <boost/beast/http.hpp>
@@ -55,7 +56,6 @@ public:
}; };
TrustedPublisherServer( TrustedPublisherServer(
endpoint_type const& ep,
boost::asio::io_service& ios, boost::asio::io_service& ios,
std::pair<PublicKey, SecretKey> keys, std::pair<PublicKey, SecretKey> keys,
std::string const& manifest, std::string const& manifest,
@@ -65,6 +65,9 @@ public:
std::vector<Validator> const& validators) std::vector<Validator> const& validators)
: sock_(ios), acceptor_(ios) : 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) + std::string data = "{\"sequence\":" + std::to_string(sequence) +
",\"expiration\":" + ",\"expiration\":" +
std::to_string(expiration.time_since_epoch().count()) + std::to_string(expiration.time_since_epoch().count()) +

View File

@@ -25,6 +25,15 @@
namespace ripple { namespace ripple {
namespace test { namespace test {
extern std::atomic<bool> envUseIPv4;
inline
const char *
getEnvLocalhostAddr()
{
return envUseIPv4 ? "127.0.0.1" : "::1";
}
/// @brief initializes a config object for use with jtx::Env /// @brief initializes a config object for use with jtx::Env
/// ///
/// @param config the configuration object to be initialized /// @param config the configuration object to be initialized

View File

@@ -49,9 +49,9 @@ class JSONRPCClient : public AbstractClient
parse_Port(pp, cfg[name], log); parse_Port(pp, cfg[name], log);
if(pp.protocol.count("http") == 0) if(pp.protocol.count("http") == 0)
continue; continue;
using boost::asio::ip::address_v4; using namespace boost::asio::ip;
if(*pp.ip == address_v4{0x00000000}) if(pp.ip && pp.ip->is_unspecified())
*pp.ip = address_v4{0x7f000001}; *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} : address{address_v4::loopback()};
return { *pp.ip, *pp.port }; return { *pp.ip, *pp.port };
} }
Throw<std::runtime_error>("Missing HTTP port"); Throw<std::runtime_error>("Missing HTTP port");
@@ -83,7 +83,7 @@ public:
: ep_(getEndpoint(cfg)) : ep_(getEndpoint(cfg))
, stream_(ios_) , stream_(ios_)
, rpc_version_(rpc_version) , rpc_version_(rpc_version)
{ {
stream_.connect(ep_); stream_.connect(ep_);
} }

View File

@@ -64,9 +64,9 @@ class WSClientImpl : public WSClient
parse_Port(pp, cfg[name], log); parse_Port(pp, cfg[name], log);
if(pp.protocol.count(ps) == 0) if(pp.protocol.count(ps) == 0)
continue; continue;
using boost::asio::ip::address_v4; using namespace boost::asio::ip;
if(*pp.ip == address_v4{0x00000000}) if(pp.ip && pp.ip->is_unspecified())
*pp.ip = address_v4{0x7f000001}; *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} : address{address_v4::loopback()};
return { *pp.ip, *pp.port }; return { *pp.ip, *pp.port };
} }
Throw<std::runtime_error>("Missing WebSocket port"); Throw<std::runtime_error>("Missing WebSocket port");

View File

@@ -30,6 +30,8 @@ void incPorts()
port_base += 3; port_base += 3;
} }
std::atomic<bool> envUseIPv4 {false};
void void
setupConfigForUnitTests (Config& cfg) setupConfigForUnitTests (Config& cfg)
{ {
@@ -43,19 +45,21 @@ setupConfigForUnitTests (Config& cfg)
cfg.legacy("database_path", ""); cfg.legacy("database_path", "");
cfg.setupControl(true, true, true); cfg.setupControl(true, true, true);
cfg["server"].append("port_peer"); 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("port", port_peer);
cfg["port_peer"].set("protocol", "peer"); cfg["port_peer"].set("protocol", "peer");
cfg["server"].append("port_rpc"); 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("port", port_rpc);
cfg["port_rpc"].set("protocol", "http,ws2"); cfg["port_rpc"].set("protocol", "http,ws2");
cfg["port_rpc"].set("admin", "127.0.0.1");
cfg["server"].append("port_ws"); 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("port", port_ws);
cfg["port_ws"].set("protocol", "ws"); cfg["port_ws"].set("protocol", "ws");
cfg["port_ws"].set("admin", "127.0.0.1");
} }
namespace jtx { namespace jtx {

View File

@@ -20,6 +20,7 @@
#include <ripple/basics/make_SSLContext.h> #include <ripple/basics/make_SSLContext.h>
#include <ripple/beast/core/CurrentThreadName.h> #include <ripple/beast/core/CurrentThreadName.h>
#include <ripple/beast/unit_test.h> #include <ripple/beast/unit_test.h>
#include <test/jtx/envconfig.h>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/asio/ssl.hpp> #include <boost/asio/ssl.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
@@ -183,7 +184,8 @@ private:
, server_(server) , server_(server)
, test_(server_.test_) , test_(server_.test_)
, acceptor_(test_.io_service_, , 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_) , socket_(test_.io_service_)
, strand_(socket_.get_io_service()) , strand_(socket_.get_io_service())
{ {

View File

@@ -21,10 +21,18 @@
#include <ripple/peerfinder/impl/Livecache.h> #include <ripple/peerfinder/impl/Livecache.h>
#include <ripple/beast/unit_test.h> #include <ripple/beast/unit_test.h>
#include <ripple/beast/clock/manual_clock.h> #include <ripple/beast/clock/manual_clock.h>
#include <test/beast/IPEndpointCommon.h>
#include <boost/algorithm/string.hpp>
namespace ripple { namespace ripple {
namespace PeerFinder { 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 class Livecache_test : public beast::unit_test::suite
{ {
public: public:
@@ -32,38 +40,178 @@ public:
// Add the address as an endpoint // Add the address as an endpoint
template <class C> template <class C>
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; Endpoint cep {ep, hops};
ep.hops = 0; c.insert (cep);
ep.address = beast::IP::Endpoint (
beast::IP::AddressV4 (index), port);
c.insert (ep);
} }
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()); Livecache <> c (m_clock, beast::Journal());
add (1, 1, c); auto ep1 = Endpoint {beast::IP::randomEP(), 2};
add (2, 1, c); c.insert(ep1);
add (3, 1, c); BEAST_EXPECT(c.size() == 1);
add (4, 1, c); // third position list will contain the entry
add (4, 2, c); BEAST_EXPECT((c.hops.begin()+2)->begin()->hops == 2);
add (4, 3, c);
add (5, 1, c);
add (6, 1, c);
add (6, 2, c);
add (7, 1, c);
// 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<int>(Tuning::maxHops + 1)));
auto h = c.hops.histogram();
if(! BEAST_EXPECT(! h.empty()))
return;
std::vector <std::string> v;
boost::split (v, h, boost::algorithm::is_any_of (","));
auto sum = 0;
for (auto const& n : v)
{
auto val = boost::lexical_cast<int>(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<int>(Tuning::maxHops + 1)));
using at_hop = std::vector <ripple::PeerFinder::Endpoint>;
using all_hops = std::array <at_hop, 1 + Tuning::maxHops + 1>;
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 void run () override
{ {
testFetch (); testBasicInsert ();
testInsertUpdate ();
testExpire ();
testHistogram ();
testShuffle ();
} }
}; };

View File

@@ -63,15 +63,16 @@ public:
void createGossip (Gossip& gossip) void createGossip (Gossip& gossip)
{ {
int const v (10 + rand_int(9)); std::uint8_t const v (10 + rand_int(9));
int const n (10 + rand_int(9)); std::uint8_t const n (10 + rand_int(9));
gossip.items.reserve (n); gossip.items.reserve (n);
for (int i = 0; i < n; ++i) for (std::uint8_t i = 0; i < n; ++i)
{ {
Gossip::Item item; Gossip::Item item;
item.balance = 100 + rand_int(499); item.balance = 100 + rand_int(499);
item.address = beast::IP::Endpoint ( beast::IP::AddressV4::bytes_type d =
beast::IP::AddressV4 (192, 0, 2, v + i)); {{192,0,2,static_cast<std::uint8_t>(v + i)}};
item.address = beast::IP::Endpoint { beast::IP::AddressV4 {d} };
gossip.items.push_back (item); gossip.items.push_back (item);
} }
} }
@@ -193,8 +194,8 @@ public:
Gossip g; Gossip g;
Gossip::Item item; Gossip::Item item;
item.balance = 100; item.balance = 100;
item.address = beast::IP::Endpoint ( beast::IP::AddressV4::bytes_type d = {{192, 0, 2, 1}};
beast::IP::AddressV4 (192, 0, 2, 1)); item.address = beast::IP::Endpoint { beast::IP::AddressV4 {d} };
g.items.push_back (item); g.items.push_back (item);
logic.importConsumers ("g", g); logic.importConsumers ("g", g);

View File

@@ -21,6 +21,7 @@
#include <ripple/protocol/Feature.h> #include <ripple/protocol/Feature.h>
#include <ripple/protocol/JsonFields.h> #include <ripple/protocol/JsonFields.h>
#include <test/jtx.h> #include <test/jtx.h>
#include <test/jtx/envconfig.h>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <ripple/beast/utility/temp_dir.h> #include <ripple/beast/utility/temp_dir.h>
#include <ripple/resource/ResourceManager.h> #include <ripple/resource/ResourceManager.h>
@@ -265,7 +266,7 @@ class NoRippleCheckLimits_test : public beast::unit_test::suite
using namespace std::chrono; using namespace std::chrono;
using namespace beast::IP; using namespace beast::IP;
auto c = env.app().getResourceManager() auto c = env.app().getResourceManager()
.newInboundEndpoint (Endpoint::from_string ("127.0.0.1")); .newInboundEndpoint (Endpoint::from_string (test::getEnvLocalhostAddr()));
if (dropThreshold - c.balance() <= 20) if (dropThreshold - c.balance() <= 20)
{ {
using clock_type = beast::abstract_clock <steady_clock>; using clock_type = beast::abstract_clock <steady_clock>;

View File

@@ -178,8 +178,6 @@ public:
testDynamicUNL() testDynamicUNL()
{ {
using namespace test::jtx; using namespace test::jtx;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
auto toStr = [](PublicKey const& publicKey) { auto toStr = [](PublicKey const& publicKey) {
return toBase58(TokenType::NodePublic, publicKey); return toBase58(TokenType::NodePublic, publicKey);
@@ -208,7 +206,9 @@ public:
// Publisher list site unavailable // Publisher list site unavailable
{ {
// Publisher site information // 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{ Env env{
*this, *this,
@@ -271,9 +271,6 @@ public:
{ {
NetClock::time_point const expiration{3600s}; 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 // Manage single thread io_service for server
struct Worker : BasicApp struct Worker : BasicApp
{ {
@@ -282,7 +279,6 @@ public:
Worker w; Worker w;
TrustedPublisherServer server( TrustedPublisherServer server(
ep,
w.get_io_service(), w.get_io_service(),
publisherSigningKeys, publisherSigningKeys,
manifest, manifest,
@@ -291,9 +287,9 @@ public:
1, 1,
validators); validators);
endpoint_type const & local_ep = server.local_endpoint(); std::stringstream uri;
std::string siteURI = "http://127.0.0.1:" + uri << "http://" << server.local_endpoint() << "/validators";
std::to_string(local_ep.port()) + "/validators"; auto siteURI = uri.str();
Env env{ Env env{
*this, *this,

View File

@@ -73,10 +73,10 @@ class ServerStatus_test :
// which requires an http endpoint to talk to. In the connection // which requires an http endpoint to talk to. In the connection
// failure test, this endpoint should never be used // failure test, this endpoint should never be used
(*p)["server"].append("port_alt"); (*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("port", "8099");
(*p)["port_alt"].set("protocol", "http"); (*p)["port_alt"].set("protocol", "http");
(*p)["port_alt"].set("admin", "127.0.0.1"); (*p)["port_alt"].set("admin", getEnvLocalhostAddr());
} }
return p; return p;

View File

@@ -283,7 +283,7 @@ public:
thread.get_io_service(), journal); thread.get_io_service(), journal);
std::vector<Port> serverPort(1); std::vector<Port> serverPort(1);
serverPort.back().ip = 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().port = 0;
serverPort.back().protocol.insert("http"); serverPort.back().protocol.insert("http");
auto eps = s->ports (serverPort); auto eps = s->ports (serverPort);
@@ -355,7 +355,7 @@ public:
thread.get_io_service(), {}); thread.get_io_service(), {});
std::vector<Port> serverPort(1); std::vector<Port> serverPort(1);
serverPort.back().ip = 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().port = 0;
serverPort.back().protocol.insert("http"); serverPort.back().protocol.insert("http");
s->ports (serverPort); s->ports (serverPort);
@@ -441,7 +441,7 @@ public:
Env env {*this, Env env {*this,
envconfig([](std::unique_ptr<Config> cfg) { envconfig([](std::unique_ptr<Config> cfg) {
(*cfg).deprecatedClearSection("port_rpc"); (*cfg).deprecatedClearSection("port_rpc");
(*cfg)["port_rpc"].set("ip", "127.0.0.1"); (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr());
return cfg; return cfg;
}), }),
std::make_unique<CaptureLogs>(messages)}; std::make_unique<CaptureLogs>(messages)};
@@ -455,7 +455,7 @@ public:
Env env {*this, Env env {*this,
envconfig([](std::unique_ptr<Config> cfg) { envconfig([](std::unique_ptr<Config> cfg) {
(*cfg).deprecatedClearSection("port_rpc"); (*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"); (*cfg)["port_rpc"].set("port", "0");
return cfg; return cfg;
}), }),
@@ -470,7 +470,7 @@ public:
Env env {*this, Env env {*this,
envconfig([](std::unique_ptr<Config> cfg) { envconfig([](std::unique_ptr<Config> cfg) {
(*cfg).deprecatedClearSection("port_rpc"); (*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("port", "8081");
(*cfg)["port_rpc"].set("protocol", ""); (*cfg)["port_rpc"].set("protocol", "");
return cfg; return cfg;
@@ -495,17 +495,17 @@ public:
ConfigSection::importNodeDatabase ()); ConfigSection::importNodeDatabase ());
cfg->legacy("database_path", ""); cfg->legacy("database_path", "");
cfg->setupControl(true, true, true); 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("port", "8080");
(*cfg)["port_peer"].set("protocol", "peer"); (*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("port", "8081");
(*cfg)["port_rpc"].set("protocol", "http,ws2"); (*cfg)["port_rpc"].set("protocol", "http,ws2");
(*cfg)["port_rpc"].set("admin", "127.0.0.1"); (*cfg)["port_rpc"].set("admin", getEnvLocalhostAddr());
(*cfg)["port_ws"].set("ip", "127.0.0.1"); (*cfg)["port_ws"].set("ip", getEnvLocalhostAddr());
(*cfg)["port_ws"].set("port", "8082"); (*cfg)["port_ws"].set("port", "8082");
(*cfg)["port_ws"].set("protocol", "ws"); (*cfg)["port_ws"].set("protocol", "ws");
(*cfg)["port_ws"].set("admin", "127.0.0.1"); (*cfg)["port_ws"].set("admin", getEnvLocalhostAddr());
return cfg; return cfg;
}), }),
std::make_unique<CaptureLogs>(messages)}; std::make_unique<CaptureLogs>(messages)};