mirror of
https://github.com/wownero/wownero.git
synced 2026-10-19 08:28:36 +02:00
net/i2p: support human-readable hostnames
This commit is contained in:
+56
-10
@@ -45,21 +45,16 @@ namespace net
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// !TODO only b32 addresses right now
|
||||
constexpr const char tld[] = u8".b32.i2p";
|
||||
constexpr const char unknown_host[] = "<unknown i2p host>";
|
||||
|
||||
constexpr const unsigned b32_length = 52;
|
||||
|
||||
constexpr const char base32_alphabet[] =
|
||||
u8"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz234567";
|
||||
|
||||
expect<void> host_check(boost::string_ref host) noexcept
|
||||
//! Validate a Base32 address (.b32.i2p)
|
||||
expect<void> host_check_b32(boost::string_ref host) noexcept
|
||||
{
|
||||
if (!host.ends_with(tld))
|
||||
if (!host.ends_with(tld_b32))
|
||||
return {net::error::expected_tld};
|
||||
|
||||
host.remove_suffix(sizeof(tld) - 1);
|
||||
host.remove_suffix(sizeof(tld_b32) - 1);
|
||||
|
||||
if (host.size() != b32_length)
|
||||
return {net::error::invalid_i2p_address};
|
||||
@@ -69,6 +64,55 @@ namespace net
|
||||
return success();
|
||||
}
|
||||
|
||||
//! Validate a human-readable hostname (.i2p)
|
||||
expect<void> host_check_human(boost::string_ref host) noexcept
|
||||
{
|
||||
if (!host.ends_with(tld_i2p))
|
||||
return {net::error::expected_tld};
|
||||
|
||||
host.remove_suffix(sizeof(tld_i2p) - 1);
|
||||
|
||||
if (host.empty() || host.size() > i2p_name_max_length)
|
||||
return {net::error::invalid_i2p_address};
|
||||
|
||||
//! Reject addresses starting or ending with a dot or hyphen
|
||||
if (host.front() == '.' || host.front() == '-' ||
|
||||
host.back() == '.' || host.back() == '-')
|
||||
return {net::error::invalid_i2p_address};
|
||||
|
||||
//! Reject invalid characters, and two dots/hyphens in a row (or a dot next to a hyphen)
|
||||
char previous = '\0';
|
||||
for (const char c : host)
|
||||
{
|
||||
if (!(('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '.' || c == '-'))
|
||||
{
|
||||
return {net::error::invalid_i2p_address};
|
||||
}
|
||||
if ((c == '.' || c == '-') && (previous == '.' || previous == '-'))
|
||||
{
|
||||
return {net::error::invalid_i2p_address};
|
||||
}
|
||||
previous = c;
|
||||
}
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
//! Use appropriate function for address validation
|
||||
expect<void> host_check(boost::string_ref host) noexcept
|
||||
{
|
||||
if (host.ends_with(tld_b32))
|
||||
{
|
||||
return host_check_b32(host);
|
||||
}
|
||||
else if (host.ends_with(tld_i2p))
|
||||
{
|
||||
return host_check_human(host);
|
||||
}
|
||||
|
||||
return {net::error::expected_tld};
|
||||
}
|
||||
|
||||
struct i2p_serialized
|
||||
{
|
||||
std::string host;
|
||||
@@ -110,7 +154,9 @@ namespace net
|
||||
net::canonicalize_host(normalized_host);
|
||||
MONERO_CHECK(host_check(normalized_host));
|
||||
|
||||
static_assert(b32_length + sizeof(tld) == sizeof(i2p_address::host_), "bad internal host size");
|
||||
static_assert(sizeof(i2p_address::host_) >= b32_length + sizeof(tld_b32) &&
|
||||
sizeof(i2p_address::host_) >= i2p_name_max_length + sizeof(tld_i2p));
|
||||
|
||||
return i2p_address{normalized_host};
|
||||
}
|
||||
|
||||
|
||||
+23
-6
@@ -47,10 +47,27 @@ namespace serialization
|
||||
|
||||
namespace net
|
||||
{
|
||||
//! b32 i2p address; internal format not condensed/decoded.
|
||||
/**
|
||||
* Naming rules defined here:
|
||||
* https://i2p.net/en/docs/overview/naming/#naming-rules
|
||||
*/
|
||||
constexpr const char tld_b32[] = u8".b32.i2p";
|
||||
constexpr const char tld_i2p[] = u8".i2p";
|
||||
constexpr const char unknown_host[] = "<unknown i2p host>";
|
||||
|
||||
//! Length of a Base32 I2P address in characters, not including TLD
|
||||
constexpr const unsigned b32_length = 52;
|
||||
|
||||
//! Maximum length of a human-readable I2P hostname in characters, not including TLD
|
||||
constexpr const unsigned i2p_name_max_length = 63;
|
||||
|
||||
/**
|
||||
* An I2P address; internal format not condensed/decoded.
|
||||
* Supports human-readable and Base32 types.
|
||||
*/
|
||||
class i2p_address
|
||||
{
|
||||
char host_[61]; // null-terminated
|
||||
char host_[i2p_name_max_length + sizeof(tld_i2p)] = {}; // null-terminated
|
||||
|
||||
//! Keep in private, `host.size()` has no runtime check
|
||||
i2p_address(boost::string_ref host) noexcept;
|
||||
@@ -59,7 +76,7 @@ namespace net
|
||||
//! \return Size of internal buffer for host.
|
||||
static constexpr std::size_t buffer_size() noexcept { return sizeof(host_); }
|
||||
|
||||
//! \return `<unknown tor host>`.
|
||||
//! \return `<unknown i2p host>`.
|
||||
static const char* unknown_str() noexcept;
|
||||
|
||||
//! An object with `port() == 0` and `host_str() == unknown_str()`.
|
||||
@@ -69,7 +86,7 @@ namespace net
|
||||
static i2p_address unknown() noexcept { return i2p_address{}; }
|
||||
|
||||
/*!
|
||||
Parse `address` in b32 i2p format (i.e. x.b32.i2p:80)
|
||||
Parse `address` in the detected format (i.e. x[.b32].i2p:80)
|
||||
with `default_port` being used if port is not specified in
|
||||
`address`.
|
||||
*/
|
||||
@@ -96,10 +113,10 @@ namespace net
|
||||
//! \return True if i2p addresses are identical.
|
||||
bool is_same_host(const i2p_address& rhs) const noexcept;
|
||||
|
||||
//! \return `x.b32.i2p` or `x.b32.i2p:z` if `port() != 0`.
|
||||
//! \return `x[.b32].i2p` or `x[.b32].i2p:z` if `port() != 0`.
|
||||
std::string str() const;
|
||||
|
||||
//! \return Null-terminated `x.b32.i2p` value or `unknown_str()`.
|
||||
//! \return Null-terminated `x[.b32].i2p` value or `unknown_str()`.
|
||||
const char* host_str() const noexcept { return host_; }
|
||||
|
||||
//! \return `1` to work with I2P socks which considers `0` error.
|
||||
|
||||
+141
-5
@@ -1,5 +1,4 @@
|
||||
// Copyright (c) 2018-2024, The Monero Project
|
||||
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
@@ -491,6 +490,9 @@ namespace
|
||||
"VWW6YBAL4BD7SZMGNCYRUUCPGFKQAHZDDI37KTCEO3AH7NGMCOPN.B32.I2P";
|
||||
static constexpr const char b32_i2p_2[] =
|
||||
"xmrto2bturnore26xmrto2bturnore26xmrto2bturnore26xmr2.b32.i2p";
|
||||
|
||||
static constexpr const char standard_i2p[] = "test.i2p";
|
||||
static constexpr const char standard_i2p_2[] = "reg.i2p";
|
||||
}
|
||||
|
||||
TEST(i2p_address, constants)
|
||||
@@ -511,12 +513,40 @@ TEST(i2p_address, invalid)
|
||||
EXPECT_TRUE(net::i2p_address::make(":").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make(".b32.i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make(".b32.i2p:").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make(".i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make(".i2p:").has_error());
|
||||
|
||||
EXPECT_TRUE(net::i2p_address::make("m=nero.i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make("-monero.i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make(std::string(net::i2p_name_max_length, 'a') + ".i2p").has_value());
|
||||
EXPECT_TRUE(net::i2p_address::make(std::string(net::i2p_name_max_length + 1, 'a') + ".i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make("monero-.i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make("a.-monero.i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make("m..ero.i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make("m..ero.b32.i2p.i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make("monero.i2p-").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make("monero..i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make("a-.b.i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make("a.-b.i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make("a--b.i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make("a.b.i2p").has_value());
|
||||
EXPECT_TRUE(net::i2p_address::make("abc-123.i2p").has_value());
|
||||
EXPECT_TRUE(net::i2p_address::make("example.b32.i2p").has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make(b32_i2p + 1).has_error());
|
||||
|
||||
EXPECT_TRUE(net::i2p_address::make("test.b32.i2p.i2p").has_value());
|
||||
EXPECT_TRUE(net::i2p_address::make(std::string{b32_i2p} + ".i2p").has_value());
|
||||
|
||||
EXPECT_TRUE(net::i2p_address::make(boost::string_ref{b32_i2p, sizeof(b32_i2p) - 2}).has_error());
|
||||
EXPECT_TRUE(net::i2p_address::make(boost::string_ref{standard_i2p, sizeof(standard_i2p) - 2}).has_error());
|
||||
|
||||
std::string i2p{b32_i2p};
|
||||
i2p.at(10) = 1;
|
||||
EXPECT_TRUE(net::i2p_address::make(i2p).has_error());
|
||||
|
||||
std::string i2p_standard{standard_i2p};
|
||||
i2p_standard.at(7) = 1;
|
||||
EXPECT_TRUE(net::i2p_address::make(i2p_standard).has_error());
|
||||
}
|
||||
|
||||
TEST(i2p_address, unblockable_types)
|
||||
@@ -547,7 +577,7 @@ TEST(i2p_address, unblockable_types)
|
||||
EXPECT_EQ(net::i2p_address{}, net::i2p_address::unknown());
|
||||
}
|
||||
|
||||
TEST(i2p_address, valid)
|
||||
TEST(i2p_address, valid_b32)
|
||||
{
|
||||
const auto address1 = net::i2p_address::make(b32_i2p);
|
||||
|
||||
@@ -624,6 +654,85 @@ TEST(i2p_address, valid)
|
||||
EXPECT_FALSE(address2.less(address3));
|
||||
}
|
||||
|
||||
TEST(i2p_address, valid_standard)
|
||||
{
|
||||
const auto address1 = net::i2p_address::make(standard_i2p);
|
||||
|
||||
ASSERT_TRUE(address1.has_value());
|
||||
EXPECT_EQ(1u, address1->port());
|
||||
EXPECT_STREQ(standard_i2p, address1->host_str());
|
||||
EXPECT_STREQ(standard_i2p, address1->str().c_str());
|
||||
EXPECT_TRUE(address1->is_blockable());
|
||||
|
||||
net::i2p_address address2{*address1};
|
||||
|
||||
EXPECT_EQ(1u, address2.port());
|
||||
EXPECT_STREQ(standard_i2p, address2.host_str());
|
||||
EXPECT_STREQ(standard_i2p, address2.str().c_str());
|
||||
EXPECT_TRUE(address2.is_blockable());
|
||||
EXPECT_TRUE(address2.equal(*address1));
|
||||
EXPECT_TRUE(address1->equal(address2));
|
||||
EXPECT_TRUE(address2 == *address1);
|
||||
EXPECT_TRUE(*address1 == address2);
|
||||
EXPECT_FALSE(address2 != *address1);
|
||||
EXPECT_FALSE(*address1 != address2);
|
||||
EXPECT_TRUE(address2.is_same_host(*address1));
|
||||
EXPECT_TRUE(address1->is_same_host(address2));
|
||||
EXPECT_FALSE(address2.less(*address1));
|
||||
EXPECT_FALSE(address1->less(address2));
|
||||
|
||||
address2 = MONERO_UNWRAP(net::i2p_address::make(std::string{standard_i2p_2} + ":6545"));
|
||||
|
||||
EXPECT_EQ(1u, address2.port());
|
||||
EXPECT_STREQ(standard_i2p_2, address2.host_str());
|
||||
EXPECT_EQ(std::string{standard_i2p_2}, address2.str().c_str());
|
||||
EXPECT_TRUE(address2.is_blockable());
|
||||
EXPECT_FALSE(address2.equal(*address1));
|
||||
EXPECT_FALSE(address1->equal(address2));
|
||||
EXPECT_FALSE(address2 == *address1);
|
||||
EXPECT_FALSE(*address1 == address2);
|
||||
EXPECT_TRUE(address2 != *address1);
|
||||
EXPECT_TRUE(*address1 != address2);
|
||||
EXPECT_FALSE(address2.is_same_host(*address1));
|
||||
EXPECT_FALSE(address1->is_same_host(address2));
|
||||
EXPECT_FALSE(address1->less(address2));
|
||||
EXPECT_TRUE(address2.less(*address1));
|
||||
|
||||
net::i2p_address address3 = MONERO_UNWRAP(net::i2p_address::make(std::string{standard_i2p} + ":65535"));
|
||||
|
||||
EXPECT_EQ(1u, address3.port());
|
||||
EXPECT_STREQ(standard_i2p, address3.host_str());
|
||||
EXPECT_EQ(std::string{standard_i2p}, address3.str().c_str());
|
||||
EXPECT_TRUE(address3.is_blockable());
|
||||
EXPECT_TRUE(address3.equal(*address1));
|
||||
EXPECT_TRUE(address1->equal(address3));
|
||||
EXPECT_TRUE(address3 == *address1);
|
||||
EXPECT_TRUE(*address1 == address3);
|
||||
EXPECT_FALSE(address3 != *address1);
|
||||
EXPECT_FALSE(*address1 != address3);
|
||||
EXPECT_TRUE(address3.is_same_host(*address1));
|
||||
EXPECT_TRUE(address1->is_same_host(address3));
|
||||
EXPECT_FALSE(address3.less(*address1));
|
||||
EXPECT_FALSE(address1->less(address3));
|
||||
|
||||
EXPECT_FALSE(address3.equal(address2));
|
||||
EXPECT_FALSE(address2.equal(address3));
|
||||
EXPECT_FALSE(address3 == address2);
|
||||
EXPECT_FALSE(address2 == address3);
|
||||
EXPECT_TRUE(address3 != address2);
|
||||
EXPECT_TRUE(address2 != address3);
|
||||
EXPECT_FALSE(address3.is_same_host(address2));
|
||||
EXPECT_FALSE(address2.is_same_host(address3));
|
||||
EXPECT_TRUE(address2.less(address3));
|
||||
EXPECT_FALSE(address3.less(address2));
|
||||
|
||||
{
|
||||
const auto address = net::i2p_address::make("Example.I2P");
|
||||
ASSERT_TRUE(address.has_value());
|
||||
EXPECT_STREQ("example.i2p", address->host_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(i2p_address, generic_network_address)
|
||||
{
|
||||
const epee::net_utils::network_address i2p1{MONERO_UNWRAP(net::i2p_address::make(b32_i2p))};
|
||||
@@ -645,6 +754,22 @@ TEST(i2p_address, generic_network_address)
|
||||
EXPECT_TRUE(i2p1.is_blockable());
|
||||
EXPECT_TRUE(i2p2.is_blockable());
|
||||
EXPECT_TRUE(ip.is_blockable());
|
||||
|
||||
const epee::net_utils::network_address i2p1_standard{MONERO_UNWRAP(net::i2p_address::make(standard_i2p))};
|
||||
const epee::net_utils::network_address i2p2_standard{MONERO_UNWRAP(net::i2p_address::make(standard_i2p))};
|
||||
|
||||
EXPECT_EQ(i2p1_standard, i2p2_standard);
|
||||
EXPECT_NE(ip, i2p1_standard);
|
||||
EXPECT_LT(ip, i2p1_standard);
|
||||
|
||||
EXPECT_STREQ(standard_i2p, i2p1_standard.host_str().c_str());
|
||||
EXPECT_STREQ(standard_i2p, i2p1_standard.str().c_str());
|
||||
EXPECT_EQ(epee::net_utils::address_type::i2p, i2p1_standard.get_type_id());
|
||||
EXPECT_EQ(epee::net_utils::address_type::i2p, i2p2_standard.get_type_id());
|
||||
EXPECT_EQ(epee::net_utils::zone::i2p, i2p1_standard.get_zone());
|
||||
EXPECT_EQ(epee::net_utils::zone::i2p, i2p2_standard.get_zone());
|
||||
EXPECT_TRUE(i2p1_standard.is_blockable());
|
||||
EXPECT_TRUE(i2p2_standard.is_blockable());
|
||||
}
|
||||
|
||||
namespace
|
||||
@@ -847,9 +972,8 @@ TEST(i2p_address, boost_serialize_unknown)
|
||||
|
||||
TEST(get_network_address, i2p)
|
||||
{
|
||||
expect<epee::net_utils::network_address> address =
|
||||
net::get_network_address("i2p", 0);
|
||||
EXPECT_EQ(net::error::unsupported_address, address);
|
||||
expect<epee::net_utils::network_address> address = net::get_network_address(".i2p", 0);
|
||||
EXPECT_EQ(net::error::invalid_i2p_address, address);
|
||||
|
||||
address = net::get_network_address(".b32.i2p", 0);
|
||||
EXPECT_EQ(net::error::invalid_i2p_address, address);
|
||||
@@ -865,6 +989,18 @@ TEST(get_network_address, i2p)
|
||||
EXPECT_EQ(epee::net_utils::address_type::i2p, address->get_type_id());
|
||||
EXPECT_STREQ(b32_i2p, address->host_str().c_str());
|
||||
EXPECT_EQ(std::string{b32_i2p}, address->str());
|
||||
|
||||
address = net::get_network_address(standard_i2p, 1000);
|
||||
ASSERT_TRUE(bool(address));
|
||||
EXPECT_EQ(epee::net_utils::address_type::i2p, address->get_type_id());
|
||||
EXPECT_STREQ(standard_i2p, address->host_str().c_str());
|
||||
EXPECT_EQ(std::string{standard_i2p}, address->str());
|
||||
|
||||
address = net::get_network_address(std::string{standard_i2p} + ":2000", 1000);
|
||||
ASSERT_TRUE(bool(address));
|
||||
EXPECT_EQ(epee::net_utils::address_type::i2p, address->get_type_id());
|
||||
EXPECT_STREQ(standard_i2p, address->host_str().c_str());
|
||||
EXPECT_EQ(std::string{standard_i2p}, address->str());
|
||||
}
|
||||
|
||||
TEST(get_network_address, ipv4)
|
||||
|
||||
Reference in New Issue
Block a user