mirror of
https://github.com/transmission/transmission.git
synced 2025-12-19 18:08:31 +00:00
refactor: overhaul tr_address special address checks (#7818)
* refactor: rewrite is_martian_addr() with tr_address methods - Fix broken check for IPv4 multicast address in is_martian_address() * refactor: rewrite is_global_unicast_address() - Rewrite using new tr_address methods - Add missing IPv4 loopback check - Follow RFC 4291 IPv6 global unicast definition - Fix and update existing tests * chore: reorganise methods and add comments * fix: check for teredo and 6to4 * test: tests for new methods
This commit is contained in:
@@ -551,7 +551,7 @@ private:
|
||||
|
||||
// N.B. getaddrinfo() will return IPv4-mapped addresses by default on macOS
|
||||
auto socket_address = tr_socket_address::from_sockaddr(info->ai_addr);
|
||||
if (!socket_address || socket_address->address().is_ipv4_mapped_address())
|
||||
if (!socket_address || socket_address->address().is_ipv6_ipv4_mapped())
|
||||
{
|
||||
logdbg(
|
||||
log_name(),
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace global_source_ip_helpers
|
||||
|
||||
// In order for address selection to work right,
|
||||
// this should be a global unicast address, not Teredo or 6to4
|
||||
TR_ASSERT(dst_addr && dst_addr->is_global_unicast_address());
|
||||
TR_ASSERT(dst_addr && dst_addr->is_global_unicast() && !dst_addr->is_ipv6_teredo() && !dst_addr->is_ipv6_6to4());
|
||||
|
||||
if (dst_addr)
|
||||
{
|
||||
@@ -214,7 +214,7 @@ tr_address tr_ip_cache::bind_addr(tr_address_type type) const noexcept
|
||||
|
||||
bool tr_ip_cache::set_global_addr(tr_address const& addr_new) noexcept
|
||||
{
|
||||
if (addr_new.is_global_unicast_address())
|
||||
if (addr_new.is_global_unicast())
|
||||
{
|
||||
auto const lock = std::scoped_lock{ global_addr_mutex_[addr_new.type] };
|
||||
if (auto& addr = global_addr_[addr_new.type]; addr != addr_new)
|
||||
|
||||
@@ -447,32 +447,10 @@ namespace is_valid_for_peers_helpers
|
||||
and is covered under the same license as third-party/dht/dht.c. */
|
||||
[[nodiscard]] auto is_martian_addr(tr_address const& addr, tr_peer_from from)
|
||||
{
|
||||
static auto constexpr Zeroes = std::array<unsigned char, 16>{};
|
||||
auto const loopback_allowed = from == TR_PEER_FROM_INCOMING || from == TR_PEER_FROM_LPD || from == TR_PEER_FROM_RESUME;
|
||||
|
||||
switch (addr.type)
|
||||
{
|
||||
case TR_AF_INET:
|
||||
{
|
||||
auto const* const address = reinterpret_cast<unsigned char const*>(&addr.addr.addr4);
|
||||
return address[0] == 0 || // 0.x.x.x
|
||||
(!loopback_allowed && address[0] == 127) || // 127.x.x.x
|
||||
(address[0] & 0xE0) == 0xE0; // multicast address
|
||||
}
|
||||
|
||||
case TR_AF_INET6:
|
||||
{
|
||||
auto const* const address = reinterpret_cast<unsigned char const*>(&addr.addr.addr6);
|
||||
return address[0] == 0xFF || // multicast address
|
||||
(std::memcmp(address, std::data(Zeroes), 15) == 0 &&
|
||||
(address[15] == 0 || // ::
|
||||
(!loopback_allowed && address[15] == 1)) // ::1
|
||||
);
|
||||
}
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
return addr.is_ipv4_current_network() || addr.is_ipv6_unspecified() ||
|
||||
(!loopback_allowed && (addr.is_ipv4_loopback() || addr.is_ipv6_loopback())) || addr.is_ipv4_multicast() ||
|
||||
addr.is_ipv6_multicast();
|
||||
}
|
||||
|
||||
} // namespace is_valid_for_peers_helpers
|
||||
@@ -670,143 +648,43 @@ int tr_address::compare(tr_address const& that) const noexcept // <=>
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Reserved_IP_addresses
|
||||
[[nodiscard]] bool tr_address::is_global_unicast_address() const noexcept
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc4291.html#section-2.4
|
||||
// address type Binary prefix IPv6 notation Section
|
||||
// ------------ ------------- ------------- -------
|
||||
// Unspecified 00...0 (128 bits) ::/128 2.5.2
|
||||
// Loopback 00...1 (128 bits) ::1/128 2.5.3
|
||||
// Multicast 11111111 FF00::/8 2.7
|
||||
// Link-Local unicast 1111111010 FE80::/10 2.5.6
|
||||
// Global Unicast (everything else)
|
||||
[[nodiscard]] bool tr_address::is_global_unicast() const noexcept
|
||||
{
|
||||
if (is_ipv4())
|
||||
{
|
||||
auto const* const a = reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr);
|
||||
|
||||
// [0.0.0.0–0.255.255.255]
|
||||
// Current network.
|
||||
if (a[0] == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [10.0.0.0 – 10.255.255.255]
|
||||
// Used for local communications within a private network.
|
||||
if (a[0] == 10)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [100.64.0.0–100.127.255.255]
|
||||
// Shared address space for communications between a service provider
|
||||
// and its subscribers when using a carrier-grade NAT.
|
||||
if ((a[0] == 100) && (64 <= a[1] && a[1] <= 127))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [169.254.0.0–169.254.255.255]
|
||||
// Used for link-local addresses[5] between two hosts on a single link
|
||||
// when no IP address is otherwise specified, such as would have
|
||||
// normally been retrieved from a DHCP server.
|
||||
if (a[0] == 169 && a[1] == 254)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [172.16.0.0–172.31.255.255]
|
||||
// Used for local communications within a private network.
|
||||
if ((a[0] == 172) && (16 <= a[1] && a[1] <= 31))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [192.0.0.0–192.0.0.255]
|
||||
// IETF Protocol Assignments.
|
||||
if (a[0] == 192 && a[1] == 0 && a[2] == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [192.0.2.0–192.0.2.255]
|
||||
// Assigned as TEST-NET-1, documentation and examples.
|
||||
if (a[0] == 192 && a[1] == 0 && a[2] == 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [192.88.99.0–192.88.99.255]
|
||||
// Reserved. Formerly used for IPv6 to IPv4 relay.
|
||||
if (a[0] == 192 && a[1] == 88 && a[2] == 99)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [192.168.0.0–192.168.255.255]
|
||||
// Used for local communications within a private network.
|
||||
if (a[0] == 192 && a[1] == 168)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [198.18.0.0–198.19.255.255]
|
||||
// Used for benchmark testing of inter-network communications
|
||||
// between two separate subnets.
|
||||
if (a[0] == 198 && (18 <= a[1] && a[1] <= 19))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [198.51.100.0–198.51.100.255]
|
||||
// Assigned as TEST-NET-2, documentation and examples.
|
||||
if (a[0] == 198 && a[1] == 51 && a[2] == 100)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [203.0.113.0–203.0.113.255]
|
||||
// Assigned as TEST-NET-3, documentation and examples.
|
||||
if (a[0] == 203 && a[1] == 0 && a[2] == 113)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [224.0.0.0–239.255.255.255]
|
||||
// In use for IP multicast. (Former Class D network.)
|
||||
if (224 <= a[0] && a[0] <= 230)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [233.252.0.0-233.252.0.255]
|
||||
// Assigned as MCAST-TEST-NET, documentation and examples.
|
||||
if (a[0] == 233 && a[1] == 252 && a[2] == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// [240.0.0.0–255.255.255.254]
|
||||
// Reserved for future use. (Former Class E network.)
|
||||
// [255.255.255.255]
|
||||
// Reserved for the "limited broadcast" destination address.
|
||||
if (240 <= a[0])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_ipv6())
|
||||
{
|
||||
auto const* const a = addr.addr6.s6_addr;
|
||||
|
||||
// TODO: 2000::/3 is commonly used for global unicast but technically
|
||||
// other spaces would be allowable too, so we should test those here.
|
||||
// See RFC 4291 in the Section 2.4 listing global unicast as everything
|
||||
// that's not link-local, multicast, loopback, or unspecified.
|
||||
return (a[0] & 0xE0) == 0x20;
|
||||
}
|
||||
|
||||
return false;
|
||||
return !is_ipv4_current_network() && //
|
||||
!is_ipv4_10_private() && //
|
||||
!is_ipv4_carrier_grade_nat() && //
|
||||
!is_ipv4_loopback() && //
|
||||
!is_ipv4_link_local() && //
|
||||
!is_ipv4_172_private() && //
|
||||
!is_ipv4_ietf_protocol_assignment() && //
|
||||
!is_ipv4_test_net_1() && //
|
||||
!is_ipv4_6to4_relay() && //
|
||||
!is_ipv4_192_168_private() && //
|
||||
!is_ipv4_benchmark() && //
|
||||
!is_ipv4_test_net_2() && //
|
||||
!is_ipv4_test_net_3() && //
|
||||
!is_ipv4_multicast() && //
|
||||
!is_ipv4_mcast_test_net() && //
|
||||
!is_ipv4_reserved_class_e() && //
|
||||
!is_ipv4_limited_broadcast() && //
|
||||
!is_ipv6_unspecified() && //
|
||||
!is_ipv6_loopback() && //
|
||||
!is_ipv6_multicast() && //
|
||||
!is_ipv6_link_local();
|
||||
}
|
||||
|
||||
std::optional<tr_address> tr_address::from_ipv4_mapped() const noexcept
|
||||
{
|
||||
if (!is_ipv4_mapped_address())
|
||||
if (!is_ipv6_ipv4_mapped())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
@@ -825,7 +703,7 @@ bool tr_socket_address::is_valid_for_peers(tr_peer_from from) const noexcept
|
||||
{
|
||||
using namespace is_valid_for_peers_helpers;
|
||||
|
||||
return is_valid() && !std::empty(port_) && !address_.is_ipv6_link_local_address() && !address_.is_ipv4_mapped_address() &&
|
||||
return is_valid() && !std::empty(port_) && !address_.is_ipv6_link_local() && !address_.is_ipv6_ipv4_mapped() &&
|
||||
!is_martian_addr(address_, from);
|
||||
}
|
||||
|
||||
|
||||
@@ -245,28 +245,170 @@ struct tr_address
|
||||
|
||||
// ---
|
||||
|
||||
[[nodiscard]] bool is_global_unicast_address() const noexcept;
|
||||
[[nodiscard]] bool is_global_unicast() const noexcept;
|
||||
|
||||
[[nodiscard]] constexpr bool is_ipv4_mapped_address() const noexcept
|
||||
// 0.0.0.0/8
|
||||
[[nodiscard]] constexpr bool is_ipv4_current_network() const noexcept
|
||||
{
|
||||
return is_ipv6() && IN6_IS_ADDR_V4MAPPED(&addr.addr6);
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 0U;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool is_ipv6_link_local_address() const noexcept
|
||||
// 10.0.0.0/8
|
||||
[[nodiscard]] constexpr bool is_ipv4_10_private() const noexcept
|
||||
{
|
||||
return is_ipv6() && IN6_IS_ADDR_LINKLOCAL(&addr.addr6);
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 10U;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool is_ipv4_loopback_address() const noexcept
|
||||
// 100.64.0.0/10
|
||||
[[nodiscard]] constexpr bool is_ipv4_carrier_grade_nat() const noexcept
|
||||
{
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 100U &&
|
||||
(reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[1] & 0xC0U) == 64U;
|
||||
}
|
||||
|
||||
// 127.0.0.0/8
|
||||
[[nodiscard]] constexpr bool is_ipv4_loopback() const noexcept
|
||||
{
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 127U;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool is_ipv6_loopback_address() const noexcept
|
||||
// 169.254.0.0/16
|
||||
[[nodiscard]] constexpr bool is_ipv4_link_local() const noexcept
|
||||
{
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 169U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[1] == 254U;
|
||||
}
|
||||
|
||||
// 172.16.0.0/12
|
||||
[[nodiscard]] constexpr bool is_ipv4_172_private() const noexcept
|
||||
{
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 172U &&
|
||||
(reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[1] & 0xF0U) == 16U;
|
||||
}
|
||||
|
||||
// 192.0.0.0/24
|
||||
[[nodiscard]] constexpr bool is_ipv4_ietf_protocol_assignment() const noexcept
|
||||
{
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 192U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[1] == 0U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[2] == 0U;
|
||||
}
|
||||
|
||||
// 192.0.2.0/24
|
||||
[[nodiscard]] constexpr bool is_ipv4_test_net_1() const noexcept
|
||||
{
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 192U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[1] == 0U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[2] == 2U;
|
||||
}
|
||||
|
||||
// 192.88.99.0/24
|
||||
[[nodiscard]] constexpr bool is_ipv4_6to4_relay() const noexcept
|
||||
{
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 192U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[1] == 88U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[2] == 99U;
|
||||
}
|
||||
|
||||
// 192.168.0.0/16
|
||||
[[nodiscard]] constexpr bool is_ipv4_192_168_private() const noexcept
|
||||
{
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 192U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[1] == 168U;
|
||||
}
|
||||
|
||||
// 198.18.0.0/15
|
||||
[[nodiscard]] constexpr bool is_ipv4_benchmark() const noexcept
|
||||
{
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 198U &&
|
||||
(reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[1] & 0xFEU) == 18U;
|
||||
}
|
||||
|
||||
// 198.51.100.0/24
|
||||
[[nodiscard]] constexpr bool is_ipv4_test_net_2() const noexcept
|
||||
{
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 198U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[1] == 51U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[2] == 100U;
|
||||
}
|
||||
|
||||
// 203.0.113.0/24
|
||||
[[nodiscard]] constexpr bool is_ipv4_test_net_3() const noexcept
|
||||
{
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 203U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[1] == 0U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[2] == 113U;
|
||||
}
|
||||
|
||||
// 224.0.0.0/4
|
||||
[[nodiscard]] constexpr bool is_ipv4_multicast() const noexcept
|
||||
{
|
||||
return is_ipv4() && (reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] & 0xF0U) == 224U;
|
||||
}
|
||||
|
||||
// 233.252.0.0/24
|
||||
[[nodiscard]] constexpr bool is_ipv4_mcast_test_net() const noexcept
|
||||
{
|
||||
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 233U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[1] == 252U &&
|
||||
reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[2] == 0U;
|
||||
}
|
||||
|
||||
// 240.0.0.0/4 -255.255.255.255/32
|
||||
[[nodiscard]] constexpr bool is_ipv4_reserved_class_e() const noexcept
|
||||
{
|
||||
return is_ipv4() && !is_ipv4_limited_broadcast() &&
|
||||
(reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] & 0xF0U) == 240U;
|
||||
}
|
||||
|
||||
// 255.255.255.255/32
|
||||
[[nodiscard]] constexpr bool is_ipv4_limited_broadcast() const noexcept
|
||||
{
|
||||
return is_ipv4() && addr.addr4.s_addr == 0xFFFFFFFFU;
|
||||
}
|
||||
|
||||
// ::/128
|
||||
[[nodiscard]] constexpr bool is_ipv6_unspecified() const noexcept
|
||||
{
|
||||
return is_ipv6() && IN6_IS_ADDR_UNSPECIFIED(&addr.addr6);
|
||||
}
|
||||
|
||||
// ::1/128
|
||||
[[nodiscard]] constexpr bool is_ipv6_loopback() const noexcept
|
||||
{
|
||||
return is_ipv6() && IN6_IS_ADDR_LOOPBACK(&addr.addr6);
|
||||
}
|
||||
|
||||
// ::ffff:0:0/96
|
||||
[[nodiscard]] constexpr bool is_ipv6_ipv4_mapped() const noexcept
|
||||
{
|
||||
return is_ipv6() && IN6_IS_ADDR_V4MAPPED(&addr.addr6);
|
||||
}
|
||||
|
||||
// 2001::/32
|
||||
[[nodiscard]] constexpr bool is_ipv6_teredo() const noexcept
|
||||
{
|
||||
return is_ipv6() && reinterpret_cast<uint32_t const*>(&addr.addr6)[0] == htonl(0x20010000U);
|
||||
}
|
||||
|
||||
// 2002::/16
|
||||
[[nodiscard]] constexpr bool is_ipv6_6to4() const noexcept
|
||||
{
|
||||
return is_ipv6() && reinterpret_cast<uint16_t const*>(&addr.addr6)[0] == htons(0x2002U);
|
||||
}
|
||||
|
||||
// fe80::/64 from fe80::/10
|
||||
[[nodiscard]] constexpr bool is_ipv6_link_local() const noexcept
|
||||
{
|
||||
return is_ipv6() && IN6_IS_ADDR_LINKLOCAL(&addr.addr6);
|
||||
}
|
||||
|
||||
// ff00::/8
|
||||
[[nodiscard]] constexpr bool is_ipv6_multicast() const noexcept
|
||||
{
|
||||
return is_ipv6() && IN6_IS_ADDR_MULTICAST(&addr.addr6);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<tr_address> from_ipv4_mapped() const noexcept;
|
||||
|
||||
tr_address_type type = NUM_TR_AF_INET_TYPES;
|
||||
|
||||
@@ -1140,15 +1140,15 @@ void tr_peerMsgsImpl::send_ltep_handshake()
|
||||
|
||||
// If connecting to global peer, then use global address
|
||||
// Otherwise we are connecting to local peer, use bind address directly
|
||||
if (auto const addr = io_->address().is_global_unicast_address() ? session->global_address(TR_AF_INET) :
|
||||
session->bind_address(TR_AF_INET);
|
||||
if (auto const addr = io_->address().is_global_unicast() ? session->global_address(TR_AF_INET) :
|
||||
session->bind_address(TR_AF_INET);
|
||||
addr && !addr->is_any())
|
||||
{
|
||||
TR_ASSERT(addr->is_ipv4());
|
||||
tr_variantDictAddRaw(&val, TR_KEY_ipv4, &addr->addr.addr4, sizeof(addr->addr.addr4));
|
||||
}
|
||||
if (auto const addr = io_->address().is_global_unicast_address() ? session->global_address(TR_AF_INET6) :
|
||||
session->bind_address(TR_AF_INET6);
|
||||
if (auto const addr = io_->address().is_global_unicast() ? session->global_address(TR_AF_INET6) :
|
||||
session->bind_address(TR_AF_INET6);
|
||||
addr && !addr->is_any())
|
||||
{
|
||||
TR_ASSERT(addr->is_ipv6());
|
||||
|
||||
@@ -448,8 +448,7 @@ tr_address tr_session::bind_address(tr_address_type type) const noexcept
|
||||
// otherwise, if we can determine which one to use via global_source_address(ipv6) magic, use it.
|
||||
// otherwise, use any_ipv6 (::).
|
||||
auto const source_addr = source_address(type);
|
||||
auto const default_addr = source_addr && source_addr->is_global_unicast_address() ? *source_addr :
|
||||
tr_address::any(TR_AF_INET6);
|
||||
auto const default_addr = source_addr && source_addr->is_global_unicast() ? *source_addr : tr_address::any(TR_AF_INET6);
|
||||
return tr_address::from_string(settings_.bind_address_ipv6).value_or(default_addr);
|
||||
}
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@ void tr_session::tr_udp_core::sendto(void const* buf, size_t buflen, struct sock
|
||||
return;
|
||||
}
|
||||
else if (
|
||||
addrport && !addrport->address().is_ipv4_loopback_address() && !addrport->address().is_ipv6_loopback_address() &&
|
||||
addrport && !addrport->address().is_ipv4_loopback() && !addrport->address().is_ipv6_loopback() &&
|
||||
!session_.source_address(tr_af_to_ip_protocol(to->sa_family)))
|
||||
{
|
||||
// don't try to send if we don't have a route in this IP protocol
|
||||
|
||||
@@ -141,7 +141,7 @@ TEST_F(IPCacheTest, setGlobalAddr)
|
||||
static auto constexpr AddrStr = std::array{
|
||||
"8.8.8.8"sv, "192.168.133.133"sv, "172.16.241.133"sv, "2001:1890:1112:1::20"sv, "fd12:3456:789a:1::1"sv,
|
||||
};
|
||||
static auto constexpr AddrTests = std::array{ true, false, false, true, false };
|
||||
static auto constexpr AddrTests = std::array{ true, false, false, true, true };
|
||||
static_assert(TR_AF_INET == 0);
|
||||
static_assert(TR_AF_INET6 == 1);
|
||||
static_assert(NUM_TR_AF_INET_TYPES == 2);
|
||||
@@ -154,7 +154,7 @@ TEST_F(IPCacheTest, setGlobalAddr)
|
||||
{
|
||||
auto const addr = tr_address::from_string(AddrStr[i]);
|
||||
ASSERT_TRUE(addr.has_value());
|
||||
EXPECT_EQ(ip_cache_->set_global_addr(*addr), AddrTests[i]);
|
||||
EXPECT_EQ(ip_cache_->set_global_addr(*addr), AddrTests[i]) << AddrStr[i];
|
||||
if (auto const val = ip_cache_->global_addr(addr->type); val && AddrTests[i])
|
||||
{
|
||||
EXPECT_EQ(val->display_name(), AddrStr[i]);
|
||||
@@ -213,11 +213,13 @@ TEST_F(IPCacheTest, globalSourceIPv6)
|
||||
TEST_F(IPCacheTest, onResponseIPQuery)
|
||||
{
|
||||
static auto constexpr AddrStr = std::array{
|
||||
"8.8.8.8"sv, "192.168.133.133"sv, "172.16.241.133"sv, "2001:1890:1112:1::20"sv, "fd12:3456:789a:1::1"sv,
|
||||
"91.121.74.28"sv, "2001:1890:1112:1::20"sv
|
||||
"8.8.8.8"sv, "192.168.133.133"sv, "172.16.241.133"sv, "2001:1890:1112:1::20"sv, "fd12:3456:789a:1::1"sv,
|
||||
"91.121.74.28"sv, "2001:1890:1112:1::20"sv,
|
||||
};
|
||||
static auto constexpr AddrTests = std::array{
|
||||
std::array{ true, false, false, false, false, true, false /* IPv4 */ },
|
||||
std::array{ false, false, false, true, true, false, true /* IPv6 */ },
|
||||
};
|
||||
static auto constexpr AddrTests = std::array{ std::array{ true, false, false, false, false, true, false /* IPv4 */ },
|
||||
std::array{ false, false, false, true, false, false, true /* IPv6 */ } };
|
||||
static_assert(TR_AF_INET == 0);
|
||||
static_assert(TR_AF_INET6 == 1);
|
||||
static_assert(NUM_TR_AF_INET_TYPES == 2);
|
||||
@@ -228,8 +230,9 @@ TEST_F(IPCacheTest, onResponseIPQuery)
|
||||
{
|
||||
void fetch(tr_web::FetchOptions&& options) override // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved)
|
||||
{
|
||||
auto response = tr_web::FetchResponse{ http_code, std::string{ AddrStr[k_] }, std::string{}, true,
|
||||
false, options.done_func_user_data };
|
||||
auto response = tr_web::FetchResponse{
|
||||
http_code, std::string{ AddrStr[k_] }, std::string{}, true, false, options.done_func_user_data,
|
||||
};
|
||||
options.done_func(response);
|
||||
}
|
||||
|
||||
@@ -256,7 +259,8 @@ TEST_F(IPCacheTest, onResponseIPQuery)
|
||||
ip_cache_->update_global_addr(type);
|
||||
|
||||
auto const global_addr = ip_cache_->global_addr(type);
|
||||
EXPECT_EQ(!!global_addr, j == 200 /* HTTP_OK */ && AddrTests[i][k]);
|
||||
EXPECT_EQ(!!global_addr, j == 200 /* HTTP_OK */ && AddrTests[i][k])
|
||||
<< "i = " << i << ", j = "sv << j << ", addr = "sv << AddrStr[k];
|
||||
if (global_addr)
|
||||
{
|
||||
EXPECT_EQ(global_addr->display_name(), AddrStr[k]);
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstddef> // std::byte, size_t
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
@@ -32,8 +31,7 @@ TEST_F(NetTest, conversionsIPv4)
|
||||
static auto constexpr AddrStr = "127.0.0.1"sv;
|
||||
|
||||
auto addr = tr_address::from_string(AddrStr);
|
||||
EXPECT_TRUE(addr.has_value());
|
||||
assert(addr.has_value());
|
||||
ASSERT_TRUE(addr.has_value());
|
||||
EXPECT_EQ(AddrStr, addr->display_name());
|
||||
|
||||
auto [ss, sslen] = tr_socket_address::to_sockaddr(*addr, Port);
|
||||
@@ -191,23 +189,729 @@ TEST_F(NetTest, isGlobalUnicastAddress)
|
||||
{ "100.64.0.0"sv, false },
|
||||
{ "100.128.0.0"sv, true },
|
||||
{ "126.0.0.0"sv, true },
|
||||
{ "127.0.0.0"sv, true },
|
||||
{ "127.0.0.0"sv, false },
|
||||
{ "169.253.255.255"sv, true },
|
||||
{ "169.254.0.0"sv, false },
|
||||
{ "169.254.255.255"sv, false },
|
||||
{ "169.255.0.0"sv, true },
|
||||
{ "223.0.0.0"sv, true },
|
||||
{ "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1", false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e", true },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, true },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
EXPECT_TRUE(address.has_value());
|
||||
assert(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_global_unicast_address()) << presentation;
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_global_unicast()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4CurrentNetwork)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 19>{ {
|
||||
{ "0.0.0.0"sv, true },
|
||||
{ "0.25.37.132"sv, true },
|
||||
{ "0.255.255.255"sv, true },
|
||||
{ "1.0.0.0"sv, false },
|
||||
{ "10.0.0.0"sv, false },
|
||||
{ "10.255.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false },
|
||||
{ "100.64.0.0"sv, false },
|
||||
{ "100.128.0.0"sv, false },
|
||||
{ "126.0.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false },
|
||||
{ "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false },
|
||||
{ "169.254.255.255"sv, false },
|
||||
{ "169.255.0.0"sv, false },
|
||||
{ "223.0.0.0"sv, false },
|
||||
{ "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_current_network()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4And10Private)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 18>{ {
|
||||
{ "0.0.0.0"sv, false },
|
||||
{ "9.255.255.255"sv, false },
|
||||
{ "10.0.0.0"sv, true },
|
||||
{ "10.255.0.0"sv, true },
|
||||
{ "10.255.0.255"sv, true },
|
||||
{ "10.255.255.255"sv, true },
|
||||
{ "11.0.0.0"sv, false },
|
||||
{ "100.128.0.0"sv, false },
|
||||
{ "126.0.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false },
|
||||
{ "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false },
|
||||
{ "169.254.255.255"sv, false },
|
||||
{ "169.255.0.0"sv, false },
|
||||
{ "223.0.0.0"sv, false },
|
||||
{ "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_10_private()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4CarrierGradeNAT)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 19>{ {
|
||||
{ "0.0.0.0"sv, false },
|
||||
{ "1.0.0.0"sv, false },
|
||||
{ "10.0.0.0"sv, false },
|
||||
{ "10.255.0.0"sv, false },
|
||||
{ "100.63.255.255"sv, false },
|
||||
{ "100.64.0.0"sv, true },
|
||||
{ "100.100.32.0"sv, true },
|
||||
{ "100.127.255.255"sv, true },
|
||||
{ "100.128.0.0"sv, false },
|
||||
{ "126.0.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false },
|
||||
{ "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false },
|
||||
{ "169.254.255.255"sv, false },
|
||||
{ "169.255.0.0"sv, false },
|
||||
{ "223.0.0.0"sv, false },
|
||||
{ "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_carrier_grade_nat()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4Loopback)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 19>{ {
|
||||
{ "0.0.0.0"sv, false },
|
||||
{ "1.0.0.0"sv, false },
|
||||
{ "10.0.0.0"sv, false },
|
||||
{ "10.255.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false },
|
||||
{ "100.64.0.0"sv, false },
|
||||
{ "100.128.0.0"sv, false },
|
||||
{ "126.255.255.255"sv, false },
|
||||
{ "127.0.0.0"sv, true },
|
||||
{ "127.12.12.57"sv, true },
|
||||
{ "127.255.255.255"sv, true },
|
||||
{ "128.0.0.0"sv, false },
|
||||
{ "169.254.0.0"sv, false },
|
||||
{ "169.254.255.255"sv, false },
|
||||
{ "169.255.0.0"sv, false },
|
||||
{ "223.0.0.0"sv, false },
|
||||
{ "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_loopback()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4LinkLocal)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 18>{ {
|
||||
{ "0.0.0.0"sv, false },
|
||||
{ "1.0.0.0"sv, false },
|
||||
{ "10.0.0.0"sv, false },
|
||||
{ "10.255.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false },
|
||||
{ "100.64.0.0"sv, false },
|
||||
{ "100.128.0.0"sv, false },
|
||||
{ "126.0.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false },
|
||||
{ "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, true },
|
||||
{ "169.254.235.12"sv, true },
|
||||
{ "169.254.255.255"sv, true },
|
||||
{ "169.255.0.0"sv, false },
|
||||
{ "223.0.0.0"sv, false },
|
||||
{ "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_link_local()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4And172Private)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 20>{ {
|
||||
{ "0.0.0.0"sv, false }, { "10.0.0.0"sv, false },
|
||||
{ "10.255.0.0"sv, false }, { "10.255.0.255"sv, false },
|
||||
{ "100.64.0.0"sv, false }, { "100.128.0.0"sv, false },
|
||||
{ "126.0.0.0"sv, false }, { "127.0.0.0"sv, false },
|
||||
{ "169.253.255.255"sv, false }, { "169.254.0.0"sv, false },
|
||||
{ "169.254.255.255"sv, false }, { "172.15.255.255"sv, false },
|
||||
{ "172.16.0.0"sv, true }, { "172.17.78.245"sv, true },
|
||||
{ "172.31.255.255"sv, true }, { "172.32.0.0"sv, false },
|
||||
{ "223.0.0.0"sv, false }, { "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false }, { "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_172_private()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4IetfProtocolAssignment)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 20>{ {
|
||||
{ "0.0.0.0"sv, false }, { "10.0.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false }, { "100.64.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false }, { "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false }, { "169.254.255.255"sv, false },
|
||||
{ "172.16.0.0"sv, false }, { "172.17.78.245"sv, false },
|
||||
{ "172.31.255.255"sv, false }, { "191.255.255.255"sv, false },
|
||||
{ "192.0.0.0"sv, true }, { "192.0.0.14"sv, true },
|
||||
{ "192.0.0.255"sv, true }, { "192.0.1.0"sv, false },
|
||||
{ "223.0.0.0"sv, false }, { "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false }, { "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_ietf_protocol_assignment()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4TestNet1)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 20>{ {
|
||||
{ "0.0.0.0"sv, false }, { "10.0.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false }, { "100.64.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false }, { "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false }, { "169.254.255.255"sv, false },
|
||||
{ "172.16.0.0"sv, false }, { "172.17.78.245"sv, false },
|
||||
{ "172.31.255.255"sv, false }, { "192.0.1.255"sv, false },
|
||||
{ "192.0.2.0"sv, true }, { "192.0.2.14"sv, true },
|
||||
{ "192.0.2.225"sv, true }, { "192.0.3.0"sv, false },
|
||||
{ "223.0.0.0"sv, false }, { "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false }, { "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_test_net_1()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4And6to4Relay)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 20>{ {
|
||||
{ "0.0.0.0"sv, false }, { "10.0.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false }, { "100.64.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false }, { "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false }, { "169.254.255.255"sv, false },
|
||||
{ "172.16.0.0"sv, false }, { "172.17.78.245"sv, false },
|
||||
{ "172.31.255.255"sv, false }, { "192.88.98.255"sv, false },
|
||||
{ "192.88.99.0"sv, true }, { "192.88.99.14"sv, true },
|
||||
{ "192.88.99.225"sv, true }, { "192.88.100.0"sv, false },
|
||||
{ "223.0.0.0"sv, false }, { "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false }, { "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_6to4_relay()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4And192Private)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 20>{ {
|
||||
{ "0.0.0.0"sv, false }, { "10.0.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false }, { "100.64.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false }, { "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false }, { "169.254.255.255"sv, false },
|
||||
{ "172.16.0.0"sv, false }, { "172.17.78.245"sv, false },
|
||||
{ "172.31.255.255"sv, false }, { "192.167.255.255"sv, false },
|
||||
{ "192.168.0.0"sv, true }, { "192.168.99.14"sv, true },
|
||||
{ "192.168.255.225"sv, true }, { "192.169.0.0"sv, false },
|
||||
{ "223.0.0.0"sv, false }, { "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false }, { "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_192_168_private()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4Benchmark)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 20>{ {
|
||||
{ "0.0.0.0"sv, false }, { "10.0.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false }, { "100.64.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false }, { "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false }, { "169.254.255.255"sv, false },
|
||||
{ "172.16.0.0"sv, false }, { "172.17.78.245"sv, false },
|
||||
{ "172.31.255.255"sv, false }, { "198.17.255.255"sv, false },
|
||||
{ "198.18.0.0"sv, true }, { "198.19.99.14"sv, true },
|
||||
{ "198.19.255.225"sv, true }, { "198.20.0.0"sv, false },
|
||||
{ "223.0.0.0"sv, false }, { "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false }, { "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_benchmark()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4TestNet2)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 20>{ {
|
||||
{ "0.0.0.0"sv, false }, { "10.0.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false }, { "100.64.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false }, { "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false }, { "169.254.255.255"sv, false },
|
||||
{ "172.16.0.0"sv, false }, { "172.17.78.245"sv, false },
|
||||
{ "172.31.255.255"sv, false }, { "198.51.99.255"sv, false },
|
||||
{ "198.51.100.0"sv, true }, { "198.51.100.45"sv, true },
|
||||
{ "198.51.100.255"sv, true }, { "198.51.101.0"sv, false },
|
||||
{ "223.0.0.0"sv, false }, { "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false }, { "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_test_net_2()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4TestNet3)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 20>{ {
|
||||
{ "0.0.0.0"sv, false }, { "10.0.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false }, { "100.64.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false }, { "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false }, { "169.254.255.255"sv, false },
|
||||
{ "172.16.0.0"sv, false }, { "172.17.78.245"sv, false },
|
||||
{ "172.31.255.255"sv, false }, { "203.0.112.255"sv, false },
|
||||
{ "203.0.113.0"sv, true }, { "203.0.113.45"sv, true },
|
||||
{ "203.0.113.255"sv, true }, { "203.0.114.0"sv, false },
|
||||
{ "223.0.0.0"sv, false }, { "224.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false }, { "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_test_net_3()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4Multicast)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 20>{ {
|
||||
{ "0.0.0.0"sv, false }, { "10.0.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false }, { "100.64.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false }, { "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false }, { "169.254.255.255"sv, false },
|
||||
{ "172.16.0.0"sv, false }, { "172.17.78.245"sv, false },
|
||||
{ "172.31.255.255"sv, false }, { "203.0.113.255"sv, false },
|
||||
{ "203.0.114.0"sv, false }, { "223.255.255.255"sv, false },
|
||||
{ "224.0.0.0"sv, true }, { "230.124.45.18"sv, true },
|
||||
{ "239.255.255.255"sv, true }, { "240.0.0.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false }, { "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_multicast()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4McastTestNet)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 20>{ {
|
||||
{ "0.0.0.0"sv, false }, { "10.0.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false }, { "100.64.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false }, { "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false }, { "169.254.255.255"sv, false },
|
||||
{ "172.16.0.0"sv, false }, { "172.17.78.245"sv, false },
|
||||
{ "172.31.255.255"sv, false }, { "203.0.113.255"sv, false },
|
||||
{ "203.0.114.0"sv, false }, { "233.251.255.255"sv, false },
|
||||
{ "233.252.0.0"sv, true }, { "233.252.0.18"sv, true },
|
||||
{ "233.252.0.255"sv, true }, { "233.252.1.0"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false }, { "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_mcast_test_net()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4ReservedClassE)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 20>{ {
|
||||
{ "0.0.0.0"sv, false }, { "10.0.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false }, { "100.64.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false }, { "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false }, { "169.254.255.255"sv, false },
|
||||
{ "172.16.0.0"sv, false }, { "172.17.78.245"sv, false },
|
||||
{ "172.31.255.255"sv, false }, { "203.0.113.255"sv, false },
|
||||
{ "203.0.114.0"sv, false }, { "239.255.255.255"sv, false },
|
||||
{ "240.0.0.0"sv, true }, { "247.252.0.18"sv, true },
|
||||
{ "255.255.255.254"sv, true }, { "255.255.255.255"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false }, { "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_reserved_class_e()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv4LimitedBroadcast)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 20>{ {
|
||||
{ "0.0.0.0"sv, false }, { "10.0.0.0"sv, false },
|
||||
{ "10.255.0.255"sv, false }, { "100.64.0.0"sv, false },
|
||||
{ "127.0.0.0"sv, false }, { "169.253.255.255"sv, false },
|
||||
{ "169.254.0.0"sv, false }, { "169.254.255.255"sv, false },
|
||||
{ "172.16.0.0"sv, false }, { "172.17.78.245"sv, false },
|
||||
{ "172.31.255.255"sv, false }, { "203.0.113.255"sv, false },
|
||||
{ "203.0.114.0"sv, false }, { "239.255.255.255"sv, false },
|
||||
{ "240.0.0.0"sv, false }, { "247.252.0.18"sv, false },
|
||||
{ "255.255.255.254"sv, false }, { "255.255.255.255"sv, true },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false }, { "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv4_limited_broadcast()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv6Unspecified)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 26>{ {
|
||||
{ "0.0.0.0"sv, false },
|
||||
{ "169.254.0.0"sv, false },
|
||||
{ "::"sv, true },
|
||||
{ "0:0:0:0:0:0:0:0"sv, true },
|
||||
{ "::1"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false },
|
||||
{ "0:0:0:0:0:0:0:2"sv, false },
|
||||
{ "::fffe:ffff:ffff"sv, false },
|
||||
{ "::ffff:0:0"sv, false },
|
||||
{ "::ffff:255.255.255.255"sv, false },
|
||||
{ "::1:0:0:0"sv, false },
|
||||
{ "2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001::"sv, false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
{ "2001:0:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001:1::"sv, false },
|
||||
{ "2001:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2002::"sv, false },
|
||||
{ "2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2003::"sv, false },
|
||||
{ "fe80::"sv, false },
|
||||
{ "fe80::1234:5678:9876:5432"sv, false },
|
||||
{ "fe80::ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "ff00::"sv, false },
|
||||
{ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv6_unspecified()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv6Loopback)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 26>{ {
|
||||
{ "0.0.0.0"sv, false },
|
||||
{ "169.254.0.0"sv, false },
|
||||
{ "::"sv, false },
|
||||
{ "0:0:0:0:0:0:0:0"sv, false },
|
||||
{ "::1"sv, true },
|
||||
{ "0:0:0:0:0:0:0:1"sv, true },
|
||||
{ "0:0:0:0:0:0:0:2"sv, false },
|
||||
{ "::fffe:ffff:ffff"sv, false },
|
||||
{ "::ffff:0:0"sv, false },
|
||||
{ "::ffff:255.255.255.255"sv, false },
|
||||
{ "::1:0:0:0"sv, false },
|
||||
{ "2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001::"sv, false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
{ "2001:0:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001:1::"sv, false },
|
||||
{ "2001:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2002::"sv, false },
|
||||
{ "2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2003::"sv, false },
|
||||
{ "fe80::"sv, false },
|
||||
{ "fe80::1234:5678:9876:5432"sv, false },
|
||||
{ "fe80::ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "ff00::"sv, false },
|
||||
{ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv6_loopback()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv6IPv4Mapped)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 26>{ {
|
||||
{ "0.0.0.0"sv, false },
|
||||
{ "169.254.0.0"sv, false },
|
||||
{ "::"sv, false },
|
||||
{ "0:0:0:0:0:0:0:0"sv, false },
|
||||
{ "::1"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false },
|
||||
{ "0:0:0:0:0:0:0:2"sv, false },
|
||||
{ "::fffe:ffff:ffff"sv, false },
|
||||
{ "::ffff:0:0"sv, true },
|
||||
{ "::ffff:255.255.255.255"sv, true },
|
||||
{ "::1:0:0:0"sv, false },
|
||||
{ "2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001::"sv, false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
{ "2001:0:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001:1::"sv, false },
|
||||
{ "2001:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2002::"sv, false },
|
||||
{ "2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2003::"sv, false },
|
||||
{ "fe80::"sv, false },
|
||||
{ "fe80::1234:5678:9876:5432"sv, false },
|
||||
{ "fe80::ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "ff00::"sv, false },
|
||||
{ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv6_ipv4_mapped()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv6Teredo)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 26>{ {
|
||||
{ "0.0.0.0"sv, false },
|
||||
{ "169.254.0.0"sv, false },
|
||||
{ "::"sv, false },
|
||||
{ "0:0:0:0:0:0:0:0"sv, false },
|
||||
{ "::1"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false },
|
||||
{ "0:0:0:0:0:0:0:2"sv, false },
|
||||
{ "::fffe:ffff:ffff"sv, false },
|
||||
{ "::ffff:0:0"sv, false },
|
||||
{ "::ffff:255.255.255.255"sv, false },
|
||||
{ "::1:0:0:0"sv, false },
|
||||
{ "2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001::"sv, true },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, true },
|
||||
{ "2001:0:ffff:ffff:ffff:ffff:ffff:ffff"sv, true },
|
||||
{ "2001:1::"sv, false },
|
||||
{ "2001:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2002::"sv, false },
|
||||
{ "2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2003::"sv, false },
|
||||
{ "fe80::"sv, false },
|
||||
{ "fe80::1234:5678:9876:5432"sv, false },
|
||||
{ "fe80::ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "ff00::"sv, false },
|
||||
{ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv6_teredo()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv6And6to4)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 26>{ {
|
||||
{ "0.0.0.0"sv, false },
|
||||
{ "169.254.0.0"sv, false },
|
||||
{ "::"sv, false },
|
||||
{ "0:0:0:0:0:0:0:0"sv, false },
|
||||
{ "::1"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false },
|
||||
{ "0:0:0:0:0:0:0:2"sv, false },
|
||||
{ "::fffe:ffff:ffff"sv, false },
|
||||
{ "::ffff:0:0"sv, false },
|
||||
{ "::ffff:255.255.255.255"sv, false },
|
||||
{ "::1:0:0:0"sv, false },
|
||||
{ "2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001::"sv, false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
{ "2001:0:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001:1::"sv, false },
|
||||
{ "2001:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2002::"sv, true },
|
||||
{ "2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, true },
|
||||
{ "2003::"sv, false },
|
||||
{ "fe80::"sv, false },
|
||||
{ "fe80::1234:5678:9876:5432"sv, false },
|
||||
{ "fe80::ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "ff00::"sv, false },
|
||||
{ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv6_6to4()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv6LinkLocal)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 26>{ {
|
||||
{ "0.0.0.0"sv, false },
|
||||
{ "169.254.0.0"sv, false },
|
||||
{ "::"sv, false },
|
||||
{ "0:0:0:0:0:0:0:0"sv, false },
|
||||
{ "::1"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false },
|
||||
{ "0:0:0:0:0:0:0:2"sv, false },
|
||||
{ "::fffe:ffff:ffff"sv, false },
|
||||
{ "::ffff:0:0"sv, false },
|
||||
{ "::ffff:255.255.255.255"sv, false },
|
||||
{ "::1:0:0:0"sv, false },
|
||||
{ "2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001::"sv, false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
{ "2001:0:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001:1::"sv, false },
|
||||
{ "2001:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2002::"sv, false },
|
||||
{ "2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2003::"sv, false },
|
||||
{ "fe80::"sv, true },
|
||||
{ "fe80::1234:5678:9876:5432"sv, true },
|
||||
{ "fe80::ffff:ffff:ffff:ffff"sv, true },
|
||||
{ "feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "ff00::"sv, false },
|
||||
{ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv6_link_local()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(NetTest, isIPv6Multicast)
|
||||
{
|
||||
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 26>{ {
|
||||
{ "0.0.0.0"sv, false },
|
||||
{ "169.254.0.0"sv, false },
|
||||
{ "::"sv, false },
|
||||
{ "0:0:0:0:0:0:0:0"sv, false },
|
||||
{ "::1"sv, false },
|
||||
{ "0:0:0:0:0:0:0:1"sv, false },
|
||||
{ "0:0:0:0:0:0:0:2"sv, false },
|
||||
{ "::fffe:ffff:ffff"sv, false },
|
||||
{ "::ffff:0:0"sv, false },
|
||||
{ "::ffff:255.255.255.255"sv, false },
|
||||
{ "::1:0:0:0"sv, false },
|
||||
{ "2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001::"sv, false },
|
||||
{ "2001:0:0eab:dead::a0:abcd:4e"sv, false },
|
||||
{ "2001:0:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2001:1::"sv, false },
|
||||
{ "2001:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2002::"sv, false },
|
||||
{ "2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "2003::"sv, false },
|
||||
{ "fe80::"sv, false },
|
||||
{ "fe80::1234:5678:9876:5432"sv, false },
|
||||
{ "fe80::ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, false },
|
||||
{ "ff00::"sv, true },
|
||||
{ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv, true },
|
||||
} };
|
||||
|
||||
for (auto const& [presentation, expected] : Tests)
|
||||
{
|
||||
auto const address = tr_address::from_string(presentation);
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_EQ(expected, address->is_ipv6_multicast()) << presentation;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user