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

View File

@@ -20,6 +20,7 @@
#include <ripple/basics/ResolverAsio.h>
#include <ripple/basics/Log.h>
#include <ripple/beast/net/IPAddressConversion.h>
#include <ripple/beast/net/IPEndpoint.h>
#include <ripple/beast/core/WaitableEvent.h>
#include <boost/asio.hpp>
#include <atomic>
@@ -259,6 +260,21 @@ public:
HostAndPort parseName(std::string const& str)
{
// first attempt to parse as an endpoint (IP addr + port).
// If that doesn't succeed, fall back to generic name + port parsing
auto result {beast::IP::Endpoint::from_string_checked (str)};
if (result.second)
{
return make_pair (
result.first.address().to_string(),
std::to_string(result.first.port()));
}
// generic name/port parsing, which doesn't work for
// IPv6 addresses in particular because it considers a colon
// a port separator
// Attempt to find the first and last non-whitespace
auto const find_whitespace = std::bind (
&std::isspace <std::string::value_type>,

View File

@@ -23,7 +23,7 @@
#include <ripple/basics/ToString.h>
#include <ripple/beast/core/LexicalCast.h>
#include <boost/algorithm/string.hpp>
#include <boost/asio/ip/address.hpp>
#include <ripple/beast/net/IPEndpoint.h>
#include <boost/regex.hpp>
#include <algorithm>
#include <cstdarg>
@@ -93,24 +93,38 @@ uint64_t uintFromHex (std::string const& strSrc)
bool parseUrl (parsedURL& pUrl, std::string const& strUrl)
{
// scheme://username:password@hostname:port/rest
static boost::regex reUrl ("(?i)\\`\\s*([[:alpha:]][-+.[:alpha:][:digit:]]*)://([^:/]+)(?::(\\d+))?(/.*)?\\s*?\\'");
static boost::regex reUrl ("(?i)\\`\\s*([[:alpha:]][-+.[:alpha:][:digit:]]*)://([^/]+)(/.*)?\\s*?\\'");
boost::smatch smMatch;
bool bMatch = boost::regex_match (strUrl, smMatch, reUrl); // Match status code.
if (bMatch)
{
std::string strPort;
pUrl.scheme = smMatch[1];
boost::algorithm::to_lower (pUrl.scheme);
pUrl.path = smMatch[3];
pUrl.domain = smMatch[2];
if (smMatch[3].length ())
// now consider the domain/port fragment
auto colonPos = pUrl.domain.find_last_of(':');
if (colonPos != std::string::npos)
{
// use Endpoint class to see if this thing looks
// like an IP addr...
auto result {beast::IP::Endpoint::from_string_checked (pUrl.domain)};
if (result.second)
{
pUrl.domain = result.first.address().to_string();
pUrl.port = result.first.port();
}
else // otherwise we are DNS name + port
{
pUrl.port = beast::lexicalCast <std::uint16_t> (
std::string (smMatch[3]));
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;

View File

@@ -217,19 +217,9 @@ private:
std::thread m_thread;
static boost::asio::ip::udp::endpoint to_endpoint (
IP::Endpoint const &address)
IP::Endpoint const &ep)
{
if (address.is_v4 ())
{
return boost::asio::ip::udp::endpoint (
boost::asio::ip::address_v4 (
address.to_v4().value), address.port ());
}
// VFALCO TODO IPv6 support
assert(false);
return boost::asio::ip::udp::endpoint (
boost::asio::ip::address_v6 (), 0);
return boost::asio::ip::udp::endpoint (ep.address(), ep.port());
}
public:

View File

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

View File

@@ -21,141 +21,17 @@
#define BEAST_NET_IPADDRESSV4_H_INCLUDED
#include <ripple/beast/hash/hash_append.h>
#include <cstdint>
#include <functional>
#include <ios>
#include <string>
#include <utility>
#include <boost/asio/ip/address_v4.hpp>
namespace beast {
namespace IP {
/** Represents a version 4 IP address. */
struct AddressV4
{
/** Default constructor represents the 'any' address. */
AddressV4 ();
/** Construct from a 32-bit unsigned.
@note Octets are formed in order from the MSB to the LSB.
*/
explicit AddressV4 (std::uint32_t value_);
/** Construct from four individual octets..
@note The resulting address is a.b.c.d
*/
AddressV4 (std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d);
/** Create an address from an IPv4 address string in dotted decimal form.
@return A pair with the address, and bool set to `true` on success.
*/
static std::pair <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);
using AddressV4 = boost::asio::ip::address_v4;
/** Returns `true` if the address is a private unroutable address. */
bool is_private (AddressV4 const& addr);
@@ -163,42 +39,12 @@ bool is_private (AddressV4 const& addr);
/** Returns `true` if the address is a public routable address. */
bool is_public (AddressV4 const& addr);
//------------------------------------------------------------------------------
/** Returns the address represented as a string. */
std::string to_string (AddressV4 const& addr);
/** Output stream conversion. */
template <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);
/** Returns the address class for the given address.
@note Class 'D' represents multicast addresses (224.*.*.*).
*/
char get_class (AddressV4 const& address);
}
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

View File

@@ -26,45 +26,12 @@
#include <ios>
#include <string>
#include <utility>
#include <boost/asio/ip/address_v6.hpp>
namespace beast {
namespace IP {
/** Represents a version 4 IP address. */
struct AddressV6
{
explicit AddressV6() = default;
// VFALCO TODO
/** Arithmetic comparison. */
/** @{ */
friend bool operator== (AddressV6 const&, AddressV6 const&)
{ assert(false); return false; }
friend bool operator< (AddressV6 const&, AddressV6 const&)
{ assert(false); return false; }
friend bool operator!= (AddressV6 const& lhs, AddressV6 const& rhs)
{ return ! (lhs == rhs); }
friend bool operator> (AddressV6 const& lhs, AddressV6 const& rhs)
{ return rhs < lhs; }
friend bool operator<= (AddressV6 const& lhs, AddressV6 const& rhs)
{ return ! (lhs > rhs); }
friend bool operator>= (AddressV6 const& lhs, AddressV6 const& rhs)
{ return ! (rhs > lhs); }
/** @} */
};
//------------------------------------------------------------------------------
/** Returns `true` if this is a loopback address. */
bool is_loopback (AddressV6 const& addr);
/** Returns `true` if the address is unspecified. */
bool is_unspecified (AddressV6 const& addr);
/** Returns `true` if the address is a multicast address. */
bool is_multicast (AddressV6 const& addr);
using AddressV6 = boost::asio::ip::address_v6;
/** Returns `true` if the address is a private unroutable address. */
bool is_private (AddressV6 const& addr);
@@ -72,41 +39,7 @@ bool is_private (AddressV6 const& addr);
/** Returns `true` if the address is a public routable address. */
bool is_public (AddressV6 const& addr);
//------------------------------------------------------------------------------
template <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

View File

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

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)
{
if (address.is_v4 ())
{
boost::asio::ip::address_v4::bytes_type const bytes (
address.to_v4().to_bytes());
return Endpoint (AddressV4 (
bytes [0], bytes [1], bytes [2], bytes [3]));
}
// VFALCO TODO IPv6 support
assert(false);
return Endpoint();
return Endpoint {address};
}
Endpoint from_asio (boost::asio::ip::tcp::endpoint const& endpoint)
{
return from_asio (endpoint.address()).at_port (endpoint.port());
return Endpoint {endpoint.address(), endpoint.port()};
}
boost::asio::ip::address to_asio_address (Endpoint const& endpoint)
{
if (endpoint.address().is_v4())
{
return boost::asio::ip::address (
boost::asio::ip::address_v4 (
endpoint.address().to_v4().value));
}
// VFALCO TODO IPv6 support
assert(false);
return boost::asio::ip::address (
boost::asio::ip::address_v6 ());
return endpoint.address();
}
boost::asio::ip::tcp::endpoint to_asio_endpoint (Endpoint const& endpoint)
{
return boost::asio::ip::tcp::endpoint (
to_asio_address (endpoint), endpoint.port());
return boost::asio::ip::tcp::endpoint {endpoint.address(), endpoint.port()};
}
}

View File

@@ -21,7 +21,6 @@
#endif
#include <ripple/beast/net/IPAddressV4.h>
#include <ripple/beast/net/detail/Parse.h>
#include <sstream>
#include <stdexcept>
@@ -29,154 +28,26 @@
namespace beast {
namespace IP {
AddressV4::AddressV4 ()
: value (0)
{
}
AddressV4::AddressV4 (std::uint32_t value_)
: value (value_)
{
}
AddressV4::AddressV4 (std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d)
: value ((a<<24)|(b<<16)|(c<<8)|d)
{
}
std::pair <AddressV4, 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)
{
return
((addr.value & 0xff000000) == 0x0a000000) || // Prefix /8, 10. #.#.#
((addr.value & 0xfff00000) == 0xac100000) || // Prefix /12 172. 16.#.# - 172.31.#.#
((addr.value & 0xffff0000) == 0xc0a80000) || // Prefix /16 192.168.#.#
is_loopback (addr);
((addr.to_ulong() & 0xff000000) == 0x0a000000) || // Prefix /8, 10. #.#.#
((addr.to_ulong() & 0xfff00000) == 0xac100000) || // Prefix /12 172. 16.#.# - 172.31.#.#
((addr.to_ulong() & 0xffff0000) == 0xc0a80000) || // Prefix /16 192.168.#.#
addr.is_loopback();
}
bool is_public (AddressV4 const& addr)
{
return
! is_private (addr) &&
! is_multicast (addr);
! addr.is_multicast();
}
//------------------------------------------------------------------------------
std::string to_string (AddressV4 const& addr)
char get_class (AddressV4 const& addr)
{
std::string s;
s.reserve (15);
s =
std::to_string (addr[0]) + "." +
std::to_string (addr[1]) + "." +
std::to_string (addr[2]) + "." +
std::to_string (addr[3]);
return s;
}
std::istream& operator>> (std::istream& is, AddressV4& addr)
{
std::uint8_t octet [4];
is >> IP::detail::integer (octet [0]);
for (int i = 1; i < 4; ++i)
{
if (!is || !IP::detail::expect(is, '.'))
return is;
is >> IP::detail::integer (octet [i]);
if (!is)
return is;
}
addr = AddressV4 (octet[0], octet[1], octet[2], octet[3]);
return is;
static char const* table = "AAAABBCD";
return table [(addr.to_ulong() & 0xE0000000) >> 29];
}
}

View File

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

View File

@@ -21,7 +21,6 @@
#endif
#include <ripple/beast/net/IPEndpoint.h>
#include <ripple/beast/net/detail/Parse.h>
namespace beast {
namespace IP {
@@ -44,7 +43,7 @@ std::pair <Endpoint, bool> Endpoint::from_string_checked (std::string const& s)
is >> endpoint;
if (! is.fail() && is.rdbuf()->in_avail() == 0)
return std::make_pair (endpoint, true);
return std::make_pair (Endpoint (), false);
return std::make_pair (Endpoint {}, false);
}
Endpoint Endpoint::from_string (std::string const& s)
@@ -53,69 +52,26 @@ Endpoint Endpoint::from_string (std::string const& s)
from_string_checked (s));
if (result.second)
return result.first;
return Endpoint();
}
// VFALCO NOTE This is a hack to support legacy data format
//
Endpoint Endpoint::from_string_altform (std::string const& s)
{
// Accept the regular form if it parses
{
Endpoint ep (Endpoint::from_string (s));
if (! is_unspecified (ep))
return ep;
}
// Now try the alt form
std::stringstream is (s);
AddressV4 v4;
is >> v4;
if (! is.fail())
{
Endpoint ep (v4);
if (is.rdbuf()->in_avail()>0)
{
if (! IP::detail::expect_whitespace (is))
return Endpoint();
while (is.rdbuf()->in_avail()>0)
{
char c;
is.get(c);
if (!isspace (static_cast<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();
return Endpoint {};
}
std::string Endpoint::to_string () const
{
std::string s (address ().to_string ());
if (port() != 0)
s = s + ":" + std::to_string (port());
std::string s;
s.reserve(
(address().is_v6() ? INET6_ADDRSTRLEN-1 : 15) +
(port() == 0 ? 0 : 6 + (address().is_v6() ? 2 : 0)));
if (port() != 0 && address().is_v6())
s += '[';
s += address ().to_string();
if (port())
{
if (address().is_v6())
s += ']';
s += ":" + std::to_string (port());
}
return s;
}
@@ -138,34 +94,88 @@ bool operator< (Endpoint const& lhs, Endpoint const& rhs)
std::istream& operator>> (std::istream& is, Endpoint& endpoint)
{
// VFALCO TODO Support ipv6!
std::string addrStr;
// valid addresses only need INET6_ADDRSTRLEN-1 chars, but allow the extra
// char to check for invalid lengths
addrStr.reserve(INET6_ADDRSTRLEN);
char i {0};
char readTo {0};
is.get(i);
if (i == '[') // we are an IPv6 endpoint
readTo = ']';
else
addrStr+=i;
Address addr;
is >> addr;
if (is.fail())
return is;
while (is && is.rdbuf()->in_avail() > 0 && is.get(i))
{
// NOTE: There is a legacy data format
// that allowed space to be used as address / port separator
// so we continue to honor that here by assuming we are at the end
// of the address portion if we hit a space (or the separator
// we were expecting to see)
if (isspace(static_cast<unsigned char>(i)) || (readTo && i == readTo))
break;
if (is.rdbuf()->in_avail()>0)
if ((i == '.') ||
(i >= '0' && i <= ':') ||
(i >= 'a' && i <= 'f') ||
(i >= 'A' && i <= 'F'))
{
char c;
is.get(c);
if (c != ':')
addrStr+=i;
// don't exceed a reasonable length...
if ( addrStr.size() == INET6_ADDRSTRLEN ||
(readTo && readTo == ':' && addrStr.size() > 15))
{
is.unget();
endpoint = Endpoint (addr);
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.setstate (std::ios_base::failbit);
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;
is >> port;
if (is.fail())
return is;
endpoint = Endpoint (addr, port);
return is;
}
else
endpoint = Endpoint (addr);
return is;
}

View File

@@ -26,7 +26,6 @@
#include <ripple/beast/net/IPEndpoint.h>
#include <boost/beast/core/string.hpp>
#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/lexical_cast.hpp>
#include <boost/optional.hpp>
@@ -174,8 +173,7 @@ public:
std::size_t WORKERS = 0;
// These override the command line client settings
boost::optional<boost::asio::ip::address_v4> rpc_ip;
boost::optional<std::uint16_t> rpc_port;
boost::optional<beast::IP::Endpoint> rpc_ip;
std::unordered_set<uint256, beast::uhash<>> features;

View File

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

View File

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

View File

@@ -1019,8 +1019,43 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMEndpoints> const& m)
std::vector <PeerFinder::Endpoint> endpoints;
endpoints.reserve (m->endpoints().size());
if (m->endpoints_v2().size())
{
endpoints.reserve (m->endpoints_v2().size());
for (auto const& tm : m->endpoints_v2 ())
{
// these endpoint strings support ipv4 and ipv6
auto result = beast::IP::Endpoint::from_string_checked(tm.endpoint());
if (! result.second)
{
JLOG(p_journal_.error()) <<
"failed to parse incoming endpoint: {" <<
tm.endpoint() << "}";
continue;
}
// 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;
@@ -1048,8 +1083,11 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMEndpoints> const& m)
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())

View File

@@ -514,16 +514,24 @@ PeerImp::sendEndpoints (FwdIt first, FwdIt last)
for (;first != last; ++first)
{
auto const& ep = *first;
// eventually remove endpoints and just keep endpoints_v2
// (once we are sure the entire network understands endpoints_v2)
protocol::TMEndpoint& tme (*tm.add_endpoints());
if (ep.address.is_v4())
tme.mutable_ipv4()->set_ipv4(
beast::toNetworkByteOrder (ep.address.to_v4().value));
beast::toNetworkByteOrder<std::uint32_t> (
ep.address.to_v4().to_ulong()));
else
tme.mutable_ipv4()->set_ipv4(0);
tme.mutable_ipv4()->set_ipv4port (ep.address.port());
tme.set_hops (ep.hops);
// add v2 endpoints (strings)
auto& tme2 (*tm.add_endpoints_v2());
tme2.set_endpoint(ep.address.to_string());
tme2.set_hops (ep.hops);
}
tm.set_version (1);
tm.set_version (2);
send (std::make_shared <Message> (tm, protocol::mtENDPOINTS));
}

View File

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

View File

@@ -618,8 +618,15 @@ public:
{
Endpoint ep;
ep.hops = 0;
// we use the unspecified (0) address here because the value is
// irrelevant to recipients. When peers receive an endpoint
// with 0 hops, they use the socket remote_addr instead of the
// value in the message. Furthermore, since the address value
// is ignored, the type/version (ipv4 vs ipv6) doesn't matter
// either. ipv6 has a slightly more compact string
// representation of 0, so use that for self entries.
ep.address = beast::IP::Endpoint (
beast::IP::AddressV4 ()).at_port (
beast::IP::AddressV6 ()).at_port (
config_.listeningPort);
for (auto& t : targets)
t.insert (ep);

View File

@@ -48,7 +48,7 @@ public:
{
beast::IP::Endpoint ep (beast::IP::Endpoint::from_string (m_strings [i]));
if (is_unspecified (ep))
ep = beast::IP::Endpoint::from_string_altform (m_strings [i]);
ep = beast::IP::Endpoint::from_string (m_strings [i]);
if (! is_unspecified (ep))
results.addresses.push_back (ep);
}

View File

@@ -89,15 +89,17 @@ message TMHello
required bytes nodeProof = 4;
optional string fullVersion = 5;
optional uint64 netTime = 6;
optional uint32 ipv4Port = 7;
optional uint32 ipv4Port = 7; // NOT USED
optional uint32 ledgerIndex = 8;
optional bytes ledgerClosed = 9; // our last closed ledger
optional bytes ledgerPrevious = 10; // the ledger before the last closed ledger
optional bool nodePrivate = 11; // Request to not forward IP.
optional TMProofWork proofOfWork = 12; // request/provide proof of work
optional bool testNet = 13; // Running as testnet.
optional uint32 local_ip = 14; // our public IP
optional uint32 remote_ip = 15; // IP we see connection from
optional uint32 local_ip = 14; // NOT USED -- our public IP
optional uint32 remote_ip = 15; // NOT USED -- IP we see connection from
optional string local_ip_str = 16; // our public IP
optional string remote_ip_str = 17; // IP we see connection from
}
// The status of a node in our cluster
@@ -234,6 +236,7 @@ message TMIPv4Endpoint
required uint32 ipv4Port = 2;
}
// this message is obsolete/no longer procesed
message TMPeers
{
repeated TMIPv4Endpoint nodes = 1;
@@ -254,6 +257,15 @@ message TMEndpoints
required uint32 version = 1;
repeated TMEndpoint endpoints = 2;
// An update to the Endpoint type that uses a string
// to represent endpoints, thus allowing ipv6 or ipv4 addresses
message TMEndpointv2
{
required string endpoint = 1;
required uint32 hops = 2;
}
repeated TMEndpointv2 endpoints_v2 = 3;
};
message TMIndexedObject

View File

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

View File

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

View File

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

View File

@@ -172,21 +172,12 @@ private:
while (list2.size () < listSize)
list2.push_back (randomValidator());
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
// Use ports of 0 to allow OS selection
endpoint_type ep1{address_type::from_string("127.0.0.1"), 0};
endpoint_type ep2{address_type::from_string("127.0.0.1"), 0};
auto const sequence = 1;
auto const version = 1;
NetClock::time_point const expiration =
env.timeKeeper().now() + 3600s;
TrustedPublisherServer server1(
ep1,
env.app().getIOService(),
pubSigningKeys1,
manifest1,
@@ -196,7 +187,6 @@ private:
list1);
TrustedPublisherServer server2(
ep2,
env.app().getIOService(),
pubSigningKeys2,
manifest2,
@@ -205,14 +195,13 @@ private:
version,
list2);
std::uint16_t const port1 = server1.local_endpoint().port();
std::uint16_t const port2 = server2.local_endpoint().port();
std::stringstream url1, url2;
url1 << "http://" << server1.local_endpoint() << "/validators";
url2 << "http://" << server2.local_endpoint() << "/validators";
{
// fetch single site
std::vector<std::string> cfgSites(
{"http://127.0.0.1:" + std::to_string(port1) + "/validators"});
std::vector<std::string> cfgSites({ url1.str() });
auto sites = std::make_unique<ValidatorSite> (
env.app().getIOService(), env.app().validators(), journal);
@@ -229,9 +218,7 @@ private:
}
{
// fetch multiple sites
std::vector<std::string> cfgSites({
"http://127.0.0.1:" + std::to_string(port1) + "/validators",
"http://127.0.0.1:" + std::to_string(port2) + "/validators"});
std::vector<std::string> cfgSites({ url1.str(), url2.str() });
auto sites = std::make_unique<ValidatorSite> (
env.app().getIOService(), env.app().validators(), journal);

View File

@@ -77,6 +77,9 @@ public:
BEAST_EXPECT(parseUrl (pUrl, "Mixed://domain/path"));
BEAST_EXPECT(pUrl.scheme == "mixed");
BEAST_EXPECT(pUrl.path == "/path");
BEAST_EXPECT(parseUrl (pUrl, "scheme://[::1]:123/path"));
BEAST_EXPECT(*pUrl.port == 123);
BEAST_EXPECT(pUrl.domain == "::1");
}
void testToString ()

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

View File

@@ -23,6 +23,7 @@
#include <ripple/protocol/SecretKey.h>
#include <ripple/protocol/Sign.h>
#include <ripple/basics/strHex.h>
#include <test/jtx/envconfig.h>
#include <boost/asio.hpp>
#include <boost/beast/core/detail/base64.hpp>
#include <boost/beast/http.hpp>
@@ -55,7 +56,6 @@ public:
};
TrustedPublisherServer(
endpoint_type const& ep,
boost::asio::io_service& ios,
std::pair<PublicKey, SecretKey> keys,
std::string const& manifest,
@@ -65,6 +65,9 @@ public:
std::vector<Validator> const& validators)
: sock_(ios), acceptor_(ios)
{
endpoint_type const& ep {
beast::IP::Address::from_string (ripple::test::getEnvLocalhostAddr()),
0}; // 0 means let OS pick the port based on what's available
std::string data = "{\"sequence\":" + std::to_string(sequence) +
",\"expiration\":" +
std::to_string(expiration.time_since_epoch().count()) +

View File

@@ -25,6 +25,15 @@
namespace ripple {
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
///
/// @param config the configuration object to be initialized

View File

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

View File

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

View File

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

View File

@@ -20,6 +20,7 @@
#include <ripple/basics/make_SSLContext.h>
#include <ripple/beast/core/CurrentThreadName.h>
#include <ripple/beast/unit_test.h>
#include <test/jtx/envconfig.h>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/optional.hpp>
@@ -183,7 +184,8 @@ private:
, server_(server)
, test_(server_.test_)
, acceptor_(test_.io_service_,
endpoint_type(address_type::from_string("127.0.0.1"), 0))
endpoint_type(beast::IP::Address::from_string(
test::getEnvLocalhostAddr()), 0))
, socket_(test_.io_service_)
, strand_(socket_.get_io_service())
{

View File

@@ -21,10 +21,18 @@
#include <ripple/peerfinder/impl/Livecache.h>
#include <ripple/beast/unit_test.h>
#include <ripple/beast/clock/manual_clock.h>
#include <test/beast/IPEndpointCommon.h>
#include <boost/algorithm/string.hpp>
namespace ripple {
namespace PeerFinder {
bool operator== (Endpoint const& a, Endpoint const& b)
{
return (a.hops == b.hops &&
a.address == b.address);
}
class Livecache_test : public beast::unit_test::suite
{
public:
@@ -32,38 +40,178 @@ public:
// Add the address as an endpoint
template <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;
ep.hops = 0;
ep.address = beast::IP::Endpoint (
beast::IP::AddressV4 (index), port);
c.insert (ep);
Endpoint cep {ep, hops};
c.insert (cep);
}
void testFetch ()
void testBasicInsert ()
{
testcase ("Basic Insert");
Livecache <> c (m_clock, beast::Journal());
BEAST_EXPECT(c.empty());
for (auto i = 0; i < 10; ++i)
add(beast::IP::randomEP(true), c);
BEAST_EXPECT(! c.empty());
BEAST_EXPECT(c.size() == 10);
for (auto i = 0; i < 10; ++i)
add(beast::IP::randomEP(false), c);
BEAST_EXPECT(! c.empty());
BEAST_EXPECT(c.size() == 20);
}
void testInsertUpdate ()
{
testcase ("Insert/Update");
Livecache <> c (m_clock, beast::Journal());
add (1, 1, c);
add (2, 1, c);
add (3, 1, c);
add (4, 1, c);
add (4, 2, c);
add (4, 3, c);
add (5, 1, c);
add (6, 1, c);
add (6, 2, c);
add (7, 1, c);
auto ep1 = Endpoint {beast::IP::randomEP(), 2};
c.insert(ep1);
BEAST_EXPECT(c.size() == 1);
// third position list will contain the entry
BEAST_EXPECT((c.hops.begin()+2)->begin()->hops == 2);
// VFALCO TODO!
auto ep2 = Endpoint {ep1.address, 4};
// this will not change the entry has higher hops
c.insert(ep2);
BEAST_EXPECT(c.size() == 1);
// still in third position list
BEAST_EXPECT((c.hops.begin()+2)->begin()->hops == 2);
pass();
auto ep3 = Endpoint {ep1.address, 2};
// this will not change the entry has the same hops as existing
c.insert(ep3);
BEAST_EXPECT(c.size() == 1);
// still in third position list
BEAST_EXPECT((c.hops.begin()+2)->begin()->hops == 2);
auto ep4 = Endpoint {ep1.address, 1};
c.insert(ep4);
BEAST_EXPECT(c.size() == 1);
// now at second position list
BEAST_EXPECT((c.hops.begin()+1)->begin()->hops == 1);
}
void testExpire ()
{
testcase ("Expire");
using namespace std::chrono_literals;
Livecache <> c (m_clock, beast::Journal());
auto ep1 = Endpoint {beast::IP::randomEP(), 1};
c.insert(ep1);
BEAST_EXPECT(c.size() == 1);
c.expire();
BEAST_EXPECT(c.size() == 1);
// verify that advancing to 1 sec before expiration
// leaves our entry intact
m_clock.advance(Tuning::liveCacheSecondsToLive - 1s);
c.expire();
BEAST_EXPECT(c.size() == 1);
// now advance to the point of expiration
m_clock.advance(1s);
c.expire();
BEAST_EXPECT(c.empty());
}
void testHistogram ()
{
testcase ("Histogram");
constexpr auto num_eps = 40;
Livecache <> c (m_clock, beast::Journal());
for (auto i = 0; i < num_eps; ++i)
add(
beast::IP::randomEP(true),
c,
ripple::rand_int(0, static_cast<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
{
testFetch ();
testBasicInsert ();
testInsertUpdate ();
testExpire ();
testHistogram ();
testShuffle ();
}
};

View File

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

View File

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

View File

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

View File

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

View File

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