diff --git a/include/bitcoin/network/config/address.hpp b/include/bitcoin/network/config/address.hpp index 807634976..2110f5ed4 100644 --- a/include/bitcoin/network/config/address.hpp +++ b/include/bitcoin/network/config/address.hpp @@ -46,6 +46,9 @@ class BCT_API address address(const messages::address_item& item) NOEXCEPT; address(const messages::address_item::cptr& message) NOEXCEPT; + /// If endpoint is a DNS name (not numeric) default address is returned. + address(const asio::endpoint& uri) NOEXCEPT; + // Methods. // ------------------------------------------------------------------------ // All values are denormalized (IPv6 or IPv4). diff --git a/include/bitcoin/network/config/authority.hpp b/include/bitcoin/network/config/authority.hpp index b4ec6f85c..cfac64f8f 100644 --- a/include/bitcoin/network/config/authority.hpp +++ b/include/bitcoin/network/config/authority.hpp @@ -20,7 +20,6 @@ #define LIBBITCOIN_NETWORK_CONFIG_AUTHORITY_HPP #include -#include #include #include #include @@ -29,86 +28,34 @@ namespace libbitcoin { namespace network { namespace config { -/// Container for an [ip-address, port, CIDR] tuple. -/// Subnet matching is employed when nonzero CIDR suffix is present. -/// Internal storage always denormalized to native IPv4/IPv6 (no mapped IPv6). -/// Provided for connection management (not p2p network messaging). +/// Add message address types to base authority configuration class. +/// Message addresses are 16 byte ipv6 encoding with ipv4 addresses mapped. class BCT_API authority + : public system::config::authority { public: typedef std::shared_ptr ptr; DEFAULT_COPY_MOVE_DESTRUCT(authority); - authority() NOEXCEPT; + /// Use base class constructors. + using system::config::authority::authority; - /// Deserialize [IPv6]|IPv4[:port][/cidr] (IPv6 [literal]). - authority(const std::string& authority) THROWS; - authority(const asio::address& ip, uint16_t port, uint8_t cidr=0) NOEXCEPT; authority(const messages::address_item& item) NOEXCEPT; - authority(const asio::endpoint& endpoint) NOEXCEPT; - authority(const config::address& address) NOEXCEPT; - /// Properties. - /// ----------------------------------------------------------------------- - - /// The IPv4 or IPv6 address, denormalized (no mapped IPv6). - const asio::address& ip() const NOEXCEPT; - - /// The ip port of the authority. - uint16_t port() const NOEXCEPT; - - /// The ip subnet mask in cidr format (zero implies none). - uint8_t cidr() const NOEXCEPT; - - /// Methods. - /// ----------------------------------------------------------------------- - /// All serializations are denormalized (IPv6 or IPv4). - - /// The IPv4 or IPv6 address and port as an asio endpoint. - asio::endpoint to_endpoint() const NOEXCEPT; - - /// IPv6|IPv4 - std::string to_host() const NOEXCEPT; - - /// [IPv6]|IPv4 - std::string to_literal() const NOEXCEPT; - - /// Serialize [IPv6]|IPv4[:port][/cidr] (IPv6 [literal]). - std::string to_string() const NOEXCEPT; - - /// Authority converted to messages::ip_address or messages::address_item. - /// Message addresses are 16 byte ipv6 encoding with ipv4 addresses mapped. + /// Authority converted to messages::ip_address. messages::ip_address to_ip_address() const NOEXCEPT; + + /// Authority converted to messages::address_item. messages::address_item to_address_item() const NOEXCEPT; messages::address_item to_address_item(uint32_t timestamp, uint64_t services) const NOEXCEPT; - /// Operators. - /// ----------------------------------------------------------------------- - - /// False if ip address is unspecified or port is zero. - operator bool() const NOEXCEPT; - /// Equality treats zero port as * and non-zero CIDR as subnet identifier. /// Equality is subnet containment when one subnet identifier is present. - /// Distinct subnets are usequal even if intersecting, same subnets equal. - bool operator==(const authority& other) const NOEXCEPT; - bool operator!=(const authority& other) const NOEXCEPT; + /// Distinct subnets are unequal even if intersecting, same subnets equal. bool operator==(const messages::address_item& other) const NOEXCEPT; bool operator!=(const messages::address_item& other) const NOEXCEPT; - - /// Same format as construct(string) and to_string(). - friend std::istream& operator>>(std::istream& input, - authority& argument) THROWS; - friend std::ostream& operator<<(std::ostream& output, - const authority& argument) NOEXCEPT; - -private: - // These are not thread safe. - asio::address ip_; - uint16_t port_; - uint8_t cidr_; }; typedef std::vector authorities; diff --git a/include/bitcoin/network/config/endpoint.hpp b/include/bitcoin/network/config/endpoint.hpp index cabad9552..cb22866ea 100644 --- a/include/bitcoin/network/config/endpoint.hpp +++ b/include/bitcoin/network/config/endpoint.hpp @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -29,87 +28,30 @@ namespace libbitcoin { namespace network { namespace config { - -/// Container for a [scheme, host, port] tuple. -/// IPv6 URIs encoded with literal host (en.wikipedia.org/wiki/IPv6_address). -/// Provided for serialization of network endpoints in URI format. + +/// Add message address types to base endpoint configuration class. class BCT_API endpoint + : public system::config::endpoint { public: typedef std::shared_ptr ptr; DEFAULT_COPY_MOVE_DESTRUCT(endpoint); - endpoint() NOEXCEPT; - - /// The scheme and port may be undefined, in which case the port is - /// reported as zero and the scheme is reported as an empty string. - /// The value is of the form: [scheme://]host[:port] (dns name or ip). - endpoint(const std::string& uri) THROWS; - endpoint(const std::string& host, uint16_t port) NOEXCEPT; - endpoint(const std::string& scheme, const std::string& host, - uint16_t port) NOEXCEPT; - endpoint(const asio::endpoint& uri) NOEXCEPT; - endpoint(const asio::address& ip, uint16_t port) NOEXCEPT; - endpoint(const config::authority& authority) NOEXCEPT; - - /// Properties. - /// ----------------------------------------------------------------------- - - /// The scheme of the endpoint or empty string. - const std::string& scheme() const NOEXCEPT; - - /// The host name or ip address of the endpoint. - const std::string& host() const NOEXCEPT; - - /// The tcp port of the endpoint. - uint16_t port() const NOEXCEPT; - - /// Methods. - /// ----------------------------------------------------------------------- - - /// An empty scheme and/or empty (zero) port is omitted. - /// The endpoint is of the form: [scheme://]host[:port] - std::string to_uri() const NOEXCEPT; + using system::config::endpoint::endpoint; - /// Return a new endpoint that replaces host instances of "*" with - /// "localhost". This is intended for clients that wish to connect - /// to a service that has been configured to bind to all interfaces. - endpoint to_local() const NOEXCEPT; - - /// IP address object if host is numeric, otherwise unspecified. - address to_address() const NOEXCEPT; - - /// Operators. - /// ----------------------------------------------------------------------- - - /// Cast to IP address object if host is numeric, otherwise unspecified. + /// If endpoint is a DNS name (not numeric) default address is returned. operator const address() const NOEXCEPT; - - /// False if the endpoint is not initialized. - operator bool() const NOEXCEPT; + operator const authority() const NOEXCEPT; /// Equality considers all properties (scheme, host, port). /// Non-numeric and invalid endpoints will match the default address_item. - bool operator==(const endpoint& other) const NOEXCEPT; - bool operator!=(const endpoint& other) const NOEXCEPT; bool operator==(const messages::address_item& other) const NOEXCEPT; bool operator!=(const messages::address_item& other) const NOEXCEPT; - friend std::istream& operator>>(std::istream& input, - endpoint& argument) THROWS; - friend std::ostream& operator<<(std::ostream& output, - const endpoint& argument) NOEXCEPT; - protected: - std::string to_authority() const NOEXCEPT; + address to_address() const NOEXCEPT; messages::address_item to_address_item() const NOEXCEPT; - -private: - // These are not thread safe. - std::string scheme_; - std::string host_; - uint16_t port_; }; typedef std::vector endpoints; diff --git a/include/bitcoin/network/config/utilities.hpp b/include/bitcoin/network/config/utilities.hpp index 9ea0d1918..7d945f048 100644 --- a/include/bitcoin/network/config/utilities.hpp +++ b/include/bitcoin/network/config/utilities.hpp @@ -19,43 +19,19 @@ #ifndef LIBBITCOIN_NETWORK_CONFIG_UTILITIES_HPP #define LIBBITCOIN_NETWORK_CONFIG_UTILITIES_HPP -#include -#include #include #include #include +#include namespace libbitcoin { namespace network { namespace config { -/// IPv6 supports embedding of an IPv4 address (4 bytes) into IPv6 encodings -/// (16 bytes). The two formats, "compatible" and "mapped" embed the same -/// address differently, with the distinction being the level of support of the -/// device. This is problematic for addresses, as they are device independent. -/// P2P protocol is 16 bytes and allows for either encoding, however the -/// "compatible" concoding is deprecated, so we produce only mapped encoding -/// for P2P serialization. However, as both formats are send via P2P we decode -/// from all three IPv6 encodings (native, compatible, mapped). For human -/// readability we serialize addresses as text, for both logging and shutdown -/// persistence. We refer to this format as denormalized, as it supports only -/// native IPv4 and native IPv6 serialization. IPv6 host names are "bracketed". -/// This provides distinction from the port number (otherwise conflating ":"). -/// This form is referred to as "literal" IPv6 encoding (from IPv6 URIs). All -/// text addresses are literal encodings, and all host names are serialized as -/// non-literal, and deserialized as either literal or non-literal. - -/// datatracker.ietf.org/doc/html/rfc4291 -constexpr size_t ipv4_size = 4; -constexpr size_t ipv6_size = 16; -static constexpr system::data_array ip_map_prefix -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff -}; - /// True if ip_address starts with the ip map prefix (maps to a v4 address). constexpr bool is_v4(const messages::ip_address& ip) NOEXCEPT { + using namespace system::config; return std::equal(ip_map_prefix.begin(), ip_map_prefix.end(), ip.begin()); } @@ -64,28 +40,10 @@ constexpr bool is_v6(const messages::ip_address& ip) NOEXCEPT return !is_v4(ip); } -/// Member if subnet addresses contain host. -BCT_API bool is_member(const asio::address& ip, const asio::address& subnet, - uint8_t cidr) NOEXCEPT; - -/// Unmap IPv6-mapped addresses. -BCT_API asio::address denormalize(const asio::address& ip) NOEXCEPT; - -/// Denormalizes to IPv4 (unmapped), literal emits unbracketed. -BCT_API std::string to_host(const asio::address& ip) NOEXCEPT; -BCT_API std::string to_literal(const asio::address& ip) NOEXCEPT; -BCT_API asio::address from_host(const std::string& host) THROWS; - /// Not denormalizing. BCT_API messages::ip_address to_address(const asio::address& ip) NOEXCEPT; BCT_API asio::address from_address(const messages::ip_address& address) NOEXCEPT; -/// Parsers. -BCT_API bool parse_authority(asio::address& ip, uint16_t& port, - uint8_t& cidr, const std::string& value) NOEXCEPT; -BCT_API bool parse_endpoint(std::string& scheme, std::string& host, - uint16_t& port, const std::string& value) NOEXCEPT; - } // namespace config } // namespace network } // namespace libbitcoin diff --git a/src/config/address.cpp b/src/config/address.cpp index e8d558e41..92a3e446f 100644 --- a/src/config/address.cpp +++ b/src/config/address.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -57,17 +58,22 @@ address::address(const messages::address_item::cptr& message) NOEXCEPT { } +address::address(const asio::endpoint& uri) NOEXCEPT + : address(endpoint{ uri }) +{ +} + // Methods. // ---------------------------------------------------------------------------- asio::address address::to_ip() const NOEXCEPT { - return config::denormalize(from_address(address_->ip)); + return system::config::denormalize(from_address(address_->ip)); } std::string address::to_host() const NOEXCEPT { - return config::to_host(to_ip()); + return system::config::to_host(to_ip()); } std::string address::to_string() const NOEXCEPT diff --git a/src/config/authority.cpp b/src/config/authority.cpp index 62a302e8f..b0bdb1663 100644 --- a/src/config/authority.cpp +++ b/src/config/authority.cpp @@ -18,8 +18,6 @@ */ #include -#include -#include #include #include #include @@ -29,92 +27,11 @@ namespace libbitcoin { namespace network { namespace config { -using namespace system; - -// Contructors. -// ---------------------------------------------------------------------------- - -// Default authority is IPv6 unspecified (not IPv6-mapped unspecified). -authority::authority() NOEXCEPT - : authority(asio::ipv6{}, {}) -{ -} - -// Deserialzation does not map IPv4 and does not support mapped encoding. -authority::authority(const std::string& authority) THROWS - : ip_{}, port_{}, cidr_{} -{ - std::stringstream(authority) >> *this; -} - -// This allows unusable CIDR values (ok). -// IPv6-mapped IPv4 are normalized to IPv4. -authority::authority(const asio::address& ip, uint16_t port, - uint8_t cidr) NOEXCEPT - : ip_(config::denormalize(ip)), port_(port), cidr_(cidr) -{ -} - authority::authority(const messages::address_item& item) NOEXCEPT : authority(from_address(item.ip), item.port) { } -authority::authority(const asio::endpoint& endpoint) NOEXCEPT - : authority(endpoint.address(), endpoint.port()) -{ -} - -authority::authority(const config::address& address) NOEXCEPT - : authority(address.to_ip(), address.port()) -{ -} - -// Properties. -// ---------------------------------------------------------------------------- - -const asio::address& authority::ip() const NOEXCEPT -{ - return ip_; -} - -uint16_t authority::port() const NOEXCEPT -{ - return port_; -} - -uint8_t authority::cidr() const NOEXCEPT -{ - return cidr_; -} - -// Methods. -// ---------------------------------------------------------------------------- - -asio::endpoint authority::to_endpoint() const NOEXCEPT -{ - return { ip(), port() }; -} - -std::string authority::to_host() const NOEXCEPT -{ - return config::to_host(ip()); -} - -std::string authority::to_literal() const NOEXCEPT -{ - return config::to_literal(ip()); -} - -std::string authority::to_string() const NOEXCEPT -{ - std::stringstream value{}; - value << *this; - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - return value.str(); - BC_POP_WARNING() -} - messages::address_item authority::to_address_item() const NOEXCEPT { return to_address_item({}, messages::service::node_none); @@ -131,50 +48,17 @@ messages::ip_address authority::to_ip_address() const NOEXCEPT return config::to_address(ip()); } -// Operators. -// ---------------------------------------------------------------------------- - -authority::operator bool() const NOEXCEPT -{ - return !is_zero(port_) && !ip().is_unspecified(); -} - -bool authority::operator==(const authority& other) const NOEXCEPT -{ - // both non-zero ports must match (zero/non-zero or both zero are matched). - if ((!is_zero(port()) && !is_zero(other.port())) && port() != other.port()) - return false; - - // both non-zero cidrs must match. - if ((!is_zero(cidr()) && !is_zero(other.cidr())) && cidr() != other.cidr()) - return false; - - // same cidrs, match ips only. - if (cidr() == other.cidr()) - return ip() == other.ip(); - - // one zero (host) and one non-zero (subnet) cidr, match host to subnet. - return is_zero(cidr()) ? - config::is_member(ip(), other.ip(), other.cidr()) : - config::is_member(other.ip(), ip(), cidr()); -} - -bool authority::operator!=(const authority& other) const NOEXCEPT -{ - return !(*this == other); -} - bool authority::operator==(const messages::address_item& other) const NOEXCEPT { // both non-zero ports must match (zero/non-zero or both zero are matched). if ((!is_zero(port()) && !is_zero(other.port)) && port() != other.port) return false; + using namespace system::config; const auto host = denormalize(from_address(other.ip)); // if both zero cidr, match hosts, otherwise host membership in subnet. - return is_zero(cidr()) ? host == ip() : - config::is_member(host, ip(), cidr()); + return is_zero(cidr()) ? host == ip() : is_member(host, ip(), cidr()); } bool authority::operator!=(const messages::address_item& other) const NOEXCEPT @@ -182,32 +66,6 @@ bool authority::operator!=(const messages::address_item& other) const NOEXCEPT return !(*this == other); } -// This allows unusable CIDR values (ok). -std::istream& operator>>(std::istream& input, - authority& argument) THROWS -{ - std::string value{}; - input >> value; - - if (!parse_authority(argument.ip_, argument.port_, argument.cidr_, value)) - throw istream_exception(value); - - return input; -} - -// This allows unusable CIDR values (ok). -std::ostream& operator<<(std::ostream& output, - const authority& argument) NOEXCEPT -{ - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - output - << argument.to_literal() - << (!is_zero(argument.port()) ? ":" + serialize(argument.port()) : "") - << (!is_zero(argument.cidr()) ? "/" + serialize(argument.cidr()) : ""); - BC_POP_WARNING() - return output; -} - } // namespace config } // namespace network } // namespace libbitcoin diff --git a/src/config/endpoint.cpp b/src/config/endpoint.cpp index ff0b5e4fb..39db5acf9 100644 --- a/src/config/endpoint.cpp +++ b/src/config/endpoint.cpp @@ -18,10 +18,8 @@ */ #include -#include #include -#include -#include +#include #include #include @@ -29,88 +27,12 @@ namespace libbitcoin { namespace network { namespace config { -using namespace system; - -// Contructors. -// ---------------------------------------------------------------------------- - -endpoint::endpoint() NOEXCEPT - : endpoint({}, "localhost", {}) -{ -} - -endpoint::endpoint(const std::string& uri) THROWS - : endpoint() -{ - std::stringstream(uri) >> *this; -} - -endpoint::endpoint(const std::string& host, uint16_t port) NOEXCEPT - : endpoint({}, host, port) -{ -} - -endpoint::endpoint(const std::string& scheme, const std::string& host, - uint16_t port) NOEXCEPT - : scheme_(scheme), host_(host), port_(port) -{ -} - -endpoint::endpoint(const asio::endpoint& uri) NOEXCEPT - : endpoint(uri.address(), uri.port()) -{ -} - -endpoint::endpoint(const asio::address& ip, uint16_t port) NOEXCEPT - : endpoint(config::to_host(ip), port) -{ -} - -endpoint::endpoint(const config::authority& authority) NOEXCEPT - : endpoint(authority.ip(), authority.port()) -{ -} - -// Properties. -// ---------------------------------------------------------------------------- - -const std::string& endpoint::scheme() const NOEXCEPT -{ - return scheme_; -} - -const std::string& endpoint::host() const NOEXCEPT -{ - return host_; -} - -uint16_t endpoint::port() const NOEXCEPT -{ - return port_; -} - -// Methods. -// ---------------------------------------------------------------------------- - -std::string endpoint::to_uri() const NOEXCEPT -{ - std::stringstream value{}; - value << *this; - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - return value.str(); - BC_POP_WARNING() -} - -endpoint endpoint::to_local() const NOEXCEPT -{ - const auto host = (host_ == "*" ? "localhost" : host_); - return endpoint(scheme_, host, port_); -} - +// protected address endpoint::to_address() const NOEXCEPT { try { + // Throws if textual authority does not parse to IP address. return { to_authority() }; } catch (const std::exception&) @@ -119,41 +41,20 @@ address endpoint::to_address() const NOEXCEPT } } -// protected -std::string endpoint::to_authority() const NOEXCEPT -{ - return is_zero(port()) ? host() : host() + ":" + serialize(port()); -} - // protected messages::address_item endpoint::to_address_item() const NOEXCEPT { return to_address(); } -// Operators. -// ---------------------------------------------------------------------------- - endpoint::operator const address() const NOEXCEPT { return to_address(); } -endpoint::operator bool() const NOEXCEPT +endpoint::operator const authority() const NOEXCEPT { - return !scheme_.empty(); -} - -bool endpoint::operator==(const endpoint& other) const NOEXCEPT -{ - return host_ == other.host_ - && port_ == other.port_ - && scheme_ == other.scheme_; -} - -bool endpoint::operator!=(const endpoint& other) const NOEXCEPT -{ - return !(*this == other); + return authority{ to_address() }; } bool endpoint::operator==(const messages::address_item& other) const NOEXCEPT @@ -167,30 +68,6 @@ bool endpoint::operator!=(const messages::address_item& other) const NOEXCEPT return !(*this == other); } -std::istream& operator>>(std::istream& input, - endpoint& argument) THROWS -{ - std::string value{}; - input >> value; - - if (!parse_endpoint(argument.scheme_, argument.host_, argument.port_, value)) - throw istream_exception(value); - - return input; -} - -std::ostream& operator<<(std::ostream& output, - const endpoint& argument) NOEXCEPT -{ - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - output - << (argument.scheme().empty() ? "" : argument.scheme() + "://") - << (argument.host()) - << (is_zero(argument.port()) ? "" : ":" + serialize(argument.port())); - BC_POP_WARNING() - return output; -} - } // namespace config } // namespace network } // namespace libbitcoin diff --git a/src/config/utilities.cpp b/src/config/utilities.cpp index a81da80df..efb2d9ce5 100644 --- a/src/config/utilities.cpp +++ b/src/config/utilities.cpp @@ -18,7 +18,6 @@ */ #include -#include #include #include #include @@ -27,132 +26,13 @@ namespace libbitcoin { namespace network { namespace config { - + using namespace boost::asio; +using namespace system::config; -constexpr uint8_t maximum_cidr_ip4 = 32; -constexpr uint8_t maximum_cidr_ip6 = 128; static_assert(array_count == ipv6_size); -static_assert(array_count == ipv4_size); -static_assert(array_count == ipv6_size); static_assert(is_same_type); -// Because system::deserialize doesn't convert empty to zero. -template -inline bool to_integer(Integer& out, const std::string& in) NOEXCEPT -{ - if (in.empty()) - { - out = Integer{}; - return true; - } - - return system::deserialize(out, in); -} - -// For calling consistency. -inline bool to_string(std::string& to, std::string&& from) NOEXCEPT -{ - to = std::move(from); - return true; -} - -// ASIO make_address allows a port on win32 (which is then lost), so guard in -// regex. ASIO addresses do not have ports, that's what endpoints are for. -inline bool make_address(asio::address& ip, const std::string& host) NOEXCEPT -{ - try - { - // Regex extracts literal host, non-win32 boost make_address rejects. - ip = ip::make_address(system::trim_copy(host, { "[", "]" })); - return true; - } - catch (const std::exception&) - { - return false; - } -} - -// regex parsers. -// ---------------------------------------------------------------------------- - -// en.wikipedia.org/wiki/List_of_URI_schemes -// Schemes of p2p network and our zeromq endpoints. -#define SCHEME "(tcp|udp|http|https|inproc):\\/\\/" -#define IPV4 "([0-9.]+)" -#define IPV6 "\\[([0-9a-f:]+)\\]" -#define HOST "([^:?/\\\\]+)" -#define PORT ":([1-9][0-9]{0,4})" -#define CIDR "\\/([1-9][0-9]{0,2})" - -// sregex_iterator doesn't provide `at()`. -BC_PUSH_WARNING(NO_ARRAY_INDEXING) -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - -// Excludes ipv4 mapped/compat, unbracketed, and ports allowed by make_address. -bool parse_host(asio::address& ip, const std::string& value) NOEXCEPT -{ - static const std::regex regular - { - "^(" IPV4 "|" IPV6 ")$" - }; - - std::sregex_iterator token{ value.begin(), value.end(), regular }; - std::sregex_iterator end{}; - return token != end - && make_address(ip, (*token)[1].str()); -} - -// Excludes ipv4 mapped/compat to ipv6. -bool parse_authority(asio::address& ip, uint16_t& port, uint8_t& cidr, - const std::string& value) NOEXCEPT -{ - static const std::regex regular - { - "^(" IPV4 "|" IPV6 ")(" PORT ")?(" CIDR ")?$" - }; - - std::sregex_iterator token{ value.begin(), value.end(), regular }; - std::sregex_iterator end{}; - return token != end - && make_address(ip, (*token)[1].str()) - && to_integer(port, (*token)[5].str()) - && to_integer(cidr, (*token)[7].str()) - && ((ip.is_v4() && cidr <= maximum_cidr_ip4) || - (ip.is_v6() && cidr <= maximum_cidr_ip6)); -} - -// Excludes ipv4 mapped/compat to ipv6. -bool parse_endpoint(std::string& scheme, std::string& host, uint16_t& port, - const std::string& value) NOEXCEPT -{ - static const std::regex regular - { - "^(" SCHEME ")?(" IPV4 "|" IPV6 "|" HOST ")(" PORT ")?$" - }; - - std::sregex_iterator token{ value.begin(), value.end(), regular }; - std::sregex_iterator end{}; - return token != end - && to_string(scheme, (*token)[2].str()) - && to_string(host, (*token)[3].str()) - && to_integer(port, (*token)[8].str()); -} - -BC_POP_WARNING() -BC_POP_WARNING() - -// asio/asio conversions. -// ---------------------------------------------------------------------------- - -inline bool is_embedded_v4(const asio::ipv6& ip6) NOEXCEPT -{ - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - // is_v4_compatible is deprecated, removed in 1.87, no replacement. - return ip6.is_v4_mapped() || ip6.is_v4_compatible(); - BC_POP_WARNING() -} - static asio::ipv6 to_v6(const asio::ipv4& ip4) NOEXCEPT { try @@ -165,93 +45,6 @@ static asio::ipv6 to_v6(const asio::ipv4& ip4) NOEXCEPT } } -// Convert IPv6-mapped to IPV4 (ensures consistent internal matching). -// Reduce 4 encodings (IPv6, IPv6-mapped, IPv6-compat, IPv4) to 2 (IPv6, IPv4). -asio::address denormalize(const asio::address& ip) NOEXCEPT -{ - if (ip.is_v6()) - { - try - { - const auto ip6 = ip.to_v6(); - - // to_v4 is deprecated, removed in 1.87, no replacement. - if (is_embedded_v4(ip6)) return { ip6.to_v4() }; - } - catch (const std::exception&) - { - } - } - - return ip; -} - -// asio/string host conversions. -// ---------------------------------------------------------------------------- - -inline std::string to_host(const asio::ipv6& ip6) NOEXCEPT -{ - try - { - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - return is_embedded_v4(ip6) ? to_host(ip6.to_v4()) : ip6.to_string(); - BC_POP_WARNING() - } - catch (const std::exception&) - { - return { "::" }; - } -} - -inline std::string to_host(const asio::ipv4& ip4) NOEXCEPT -{ - try - { - return ip4.to_string(); - } - catch (const std::exception&) - { - return { "0.0.0.0" }; - } -} - -// Serialize to host denormal form (unmapped) without ipv6 backets. -std::string to_host(const asio::address& ip) NOEXCEPT -{ - try - { - const auto host = denormalize(ip); - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - return host.is_v4() ? to_host(host.to_v4()) : to_host(host.to_v6()); - BC_POP_WARNING() - } - catch (const std::exception&) - { - return { "0.0.0.0" }; - } -} - -// Serialize to host denormal form (unmapped) and with ipv6 backets. -std::string to_literal(const asio::address& ip) NOEXCEPT -{ - const auto host = to_host(ip); - return (host.find(":") == std::string::npos) ? host : ("[" + host + "]"); -} - -// Rejects ipv6 mapped/compat to ipv4 and unbracketed ipv6. -asio::address from_host(const std::string& host) THROWS -{ - asio::address out{}; - if (!parse_host(out, host)) - throw istream_exception{ host }; - - return out; -} - -// asio/messages conversions. -// ---------------------------------------------------------------------------- -// messages::ip_address <=> asio::ipv6 (mapped ipv4 as applicable). - static asio::ipv6 get_ipv6(const asio::address& ip) NOEXCEPT { try @@ -281,39 +74,6 @@ asio::address from_address(const messages::ip_address& address) NOEXCEPT } } -inline bool is_member_v4(const asio::ipv4& ip4, const asio::ipv4& net4, - uint8_t cidr) THROWS -{ - const auto hosts = ip::make_network_v4(net4, cidr).hosts(); - return hosts.find(ip4) != hosts.end(); -} - -inline bool is_member_v6(const asio::ipv6& ip6, const asio::ipv6& net6, - uint8_t cidr) THROWS -{ - const auto hosts = ip::make_network_v6(net6, cidr).hosts(); - return hosts.find(ip6) != hosts.end(); -} - -// This assumes host and/or subnet v4 address(s) unmapped. -bool is_member(const asio::address& ip, const asio::address& subnet, - uint8_t cidr) NOEXCEPT -{ - try - { - if (ip.is_v4() && subnet.is_v4()) - return is_member_v4(ip.to_v4(), subnet.to_v4(), cidr); - - if (ip.is_v6() && subnet.is_v6()) - return is_member_v6(ip.to_v6(), subnet.to_v6(), cidr); - } - catch (const std::exception&) - { - } - - return false; -} - } // namespace config } // namespace network } // namespace libbitcoin diff --git a/src/net/connector.cpp b/src/net/connector.cpp index 57050f2dd..6cb070825 100644 --- a/src/net/connector.cpp +++ b/src/net/connector.cpp @@ -93,7 +93,7 @@ void connector::connect(const authority& host, void connector::connect(const endpoint& host, socket_handler&& handler) NOEXCEPT { - start(host.host(), host.port(), host.to_address(), std::move(handler)); + start(host.host(), host.port(), host, std::move(handler)); } // protected diff --git a/src/net/socket.cpp b/src/net/socket.cpp index c1c1b8429..aafac3618 100644 --- a/src/net/socket.cpp +++ b/src/net/socket.cpp @@ -260,7 +260,7 @@ void socket::handle_connect(const error::boost_code& ec, // Outgoing connection requires address_ for .inbound() resolution. if (is_zero(address_.port())) - address_ = config::endpoint{ peer }.to_address(); + address_ = { peer }; if (error::asio_is_canceled(ec)) { diff --git a/src/settings.cpp b/src/settings.cpp index 790f6817d..339e6f45b 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -126,7 +126,9 @@ void settings::initialize() NOEXCEPT BC_ASSERT_MSG(friends.empty(), "friends not empty"); // Dynamic conversion of peers is O(N^2), so set on initialize. - friends = system::projection(peers); + // This converts endpoints to addresses, so will produce the default + // address for any hosts that are DNS names (i.e. not IP addresses). + friends = system::projection(peers); } bool settings::witness_node() const NOEXCEPT diff --git a/test/config/authority.cpp b/test/config/authority.cpp index d90efb0aa..b7ce28730 100644 --- a/test/config/authority.cpp +++ b/test/config/authority.cpp @@ -21,7 +21,7 @@ BOOST_AUTO_TEST_SUITE(authority_tests) -using namespace network::config; +using namespace config; using namespace boost::program_options; // tools.ietf.org/html/rfc4291#section-2.2 @@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE(authority__ip_port_cidr__ipv4_authority__expected) stream << BC_AUTHORITY_IPV4_ADDRESS ":" << expected_port << "/" << expected_cidr; const authority host(stream.str()); BOOST_REQUIRE_EQUAL(host.port(), expected_port); - BOOST_REQUIRE_EQUAL(host.ip(), from_host(BC_AUTHORITY_IPV4_ADDRESS)); + BOOST_REQUIRE_EQUAL(host.ip(), system::config::from_host(BC_AUTHORITY_IPV4_ADDRESS)); BOOST_REQUIRE_EQUAL(host.cidr(), expected_cidr); } @@ -193,7 +193,7 @@ BOOST_AUTO_TEST_CASE(authority__ip_port_cidr__ipv6_authority__expected) stream << "[" BC_AUTHORITY_IPV6_COMPRESSED_ADDRESS "]:" << expected_port << "/" << expected_cidr; const authority host(stream.str()); BOOST_REQUIRE_EQUAL(host.port(), expected_port); - BOOST_REQUIRE_EQUAL(host.ip(), from_host("[" BC_AUTHORITY_IPV6_COMPRESSED_ADDRESS "]")); + BOOST_REQUIRE_EQUAL(host.ip(), system::config::from_host("[" BC_AUTHORITY_IPV6_COMPRESSED_ADDRESS "]")); BOOST_REQUIRE_EQUAL(host.cidr(), expected_cidr); } @@ -475,32 +475,6 @@ BOOST_AUTO_TEST_CASE(authority__to_address_item2__parameters__expected) BOOST_REQUIRE(net_equal(host.to_address_item(expected.timestamp, expected.services), expected)); } -// bool - -BOOST_AUTO_TEST_CASE(authority__bool__default__false) -{ - const authority host{}; - BOOST_REQUIRE(!host); -} - -BOOST_AUTO_TEST_CASE(authority__bool__unspecified__false) -{ - const authority host{ from_address(messages::unspecified_ip_address), 42 }; - BOOST_REQUIRE(!host); -} - -BOOST_AUTO_TEST_CASE(authority__bool__unspecified_ip_port__false) -{ - const authority host{ from_address(test_ipv6_address), messages::unspecified_ip_port }; - BOOST_REQUIRE(!host); -} - -BOOST_AUTO_TEST_CASE(authority__bool__loopback_nonzero_port__true) -{ - const authority host{ from_address(messages::loopback_ip_address), 42 }; - BOOST_REQUIRE(host); -} - // equality BOOST_AUTO_TEST_CASE(authority__equality__default_default__true) diff --git a/test/config/utilities.cpp b/test/config/utilities.cpp index 62991ebbc..d9c32ca2d 100644 --- a/test/config/utilities.cpp +++ b/test/config/utilities.cpp @@ -19,6 +19,7 @@ #include "../test.hpp" using namespace config; +using namespace system::config; using namespace boost::program_options; BOOST_AUTO_TEST_SUITE(utilities_tests) diff --git a/test/net/connector.cpp b/test/net/connector.cpp index f5a42af8b..54b9caec0 100644 --- a/test/net/connector.cpp +++ b/test/net/connector.cpp @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(connector__connect_address__bogus_address_suspended__servic boost::asio::post(strand, [&]() NOEXCEPT { // DNS resolve failure (race), timeout includes a socket. - instance->connect(config::address{ config::endpoint{ "42.42.42.42:42" }.to_address() }, + instance->connect(config::endpoint{ "42.42.42.42:42" }, [&](const code& ec, const socket::ptr& socket) NOEXCEPT { result &= (ec == error::service_suspended); @@ -124,7 +124,7 @@ BOOST_AUTO_TEST_CASE(connector__connect_address__bogus_address__operation_timeou boost::asio::post(strand, [&]() NOEXCEPT { // DNS resolve failure (race), timeout includes a socket. - instance->connect(config::address{ config::endpoint{ "42.42.42.42:42" }.to_address() }, + instance->connect(config::endpoint{ "42.42.42.42:42" }, [&](const code& ec, const socket::ptr& socket) NOEXCEPT { result &= (ec == error::operation_timeout); diff --git a/test/settings.cpp b/test/settings.cpp index d9bdff188..b489c98d2 100644 --- a/test/settings.cpp +++ b/test/settings.cpp @@ -435,7 +435,7 @@ BOOST_AUTO_TEST_CASE(settings__first_self__empty_selfs__default) { settings instance{}; instance.selfs.clear(); - BOOST_REQUIRE(!instance.first_self()); + BOOST_REQUIRE(instance.first_self() == config::authority{}); } BOOST_AUTO_TEST_CASE(settings__first_self__multiple_selfs__front)