aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVasil Dimov <vd@FreeBSD.org>2020-10-21 11:35:19 +0200
committerVasil Dimov <vd@FreeBSD.org>2021-03-01 13:22:11 +0100
commitcff65c4a270887ec171293409ab84f5d0d0be7fc (patch)
tree3b0f9abf229eb94d5fd7e17c004e779d77ba756e
parentf6c267db3be2d7077fd2bdbd34860eba838dea99 (diff)
net: extend CNetAddr::SetSpecial() to support I2P
Recognize also I2P addresses in the form `base32hashofpublickey.b32.i2p` from `CNetAddr::SetSpecial()`. This makes `Lookup()` support them, which in turn makes it possible to manually connect to an I2P node by using `-proxy=i2p_socks5_proxy:port -addnode=i2p_address.b32.i2p:port` Co-authored-by: Lucas Ontivero <lucasontivero@gmail.com>
-rw-r--r--src/netaddress.cpp59
-rw-r--r--src/netaddress.h30
-rw-r--r--src/test/net_tests.cpp31
3 files changed, 108 insertions, 12 deletions
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 85e46fd373..80313b88ce 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -221,25 +221,34 @@ static void Checksum(Span<const uint8_t> addr_pubkey, uint8_t (&checksum)[CHECKS
}; // namespace torv3
-/**
- * Parse a TOR address and set this object to it.
- *
- * @returns Whether or not the operation was successful.
- *
- * @see CNetAddr::IsTor()
- */
-bool CNetAddr::SetSpecial(const std::string& str)
+bool CNetAddr::SetSpecial(const std::string& addr)
+{
+ if (!ValidAsCString(addr)) {
+ return false;
+ }
+
+ if (SetTor(addr)) {
+ return true;
+ }
+
+ if (SetI2P(addr)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool CNetAddr::SetTor(const std::string& addr)
{
static const char* suffix{".onion"};
static constexpr size_t suffix_len{6};
- if (!ValidAsCString(str) || str.size() <= suffix_len ||
- str.substr(str.size() - suffix_len) != suffix) {
+ if (addr.size() <= suffix_len || addr.substr(addr.size() - suffix_len) != suffix) {
return false;
}
bool invalid;
- const auto& input = DecodeBase32(str.substr(0, str.size() - suffix_len).c_str(), &invalid);
+ const auto& input = DecodeBase32(addr.substr(0, addr.size() - suffix_len).c_str(), &invalid);
if (invalid) {
return false;
@@ -275,6 +284,34 @@ bool CNetAddr::SetSpecial(const std::string& str)
return false;
}
+bool CNetAddr::SetI2P(const std::string& addr)
+{
+ // I2P addresses that we support consist of 52 base32 characters + ".b32.i2p".
+ static constexpr size_t b32_len{52};
+ static const char* suffix{".b32.i2p"};
+ static constexpr size_t suffix_len{8};
+
+ if (addr.size() != b32_len + suffix_len || ToLower(addr.substr(b32_len)) != suffix) {
+ return false;
+ }
+
+ // Remove the ".b32.i2p" suffix and pad to a multiple of 8 chars, so DecodeBase32()
+ // can decode it.
+ const std::string b32_padded = addr.substr(0, b32_len) + "====";
+
+ bool invalid;
+ const auto& address_bytes = DecodeBase32(b32_padded.c_str(), &invalid);
+
+ if (invalid || address_bytes.size() != ADDR_I2P_SIZE) {
+ return false;
+ }
+
+ m_net = NET_I2P;
+ m_addr.assign(address_bytes.begin(), address_bytes.end());
+
+ return true;
+}
+
CNetAddr::CNetAddr(const struct in_addr& ipv4Addr)
{
m_net = NET_IPV4;
diff --git a/src/netaddress.h b/src/netaddress.h
index d0986557f7..897ce46cda 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -151,7 +151,16 @@ class CNetAddr
bool SetInternal(const std::string& name);
- bool SetSpecial(const std::string &strName); // for Tor addresses
+ /**
+ * Parse a Tor or I2P address and set this object to it.
+ * @param[in] addr Address to parse, for example
+ * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or
+ * ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
+ * @returns Whether the operation was successful.
+ * @see CNetAddr::IsTor(), CNetAddr::IsI2P()
+ */
+ bool SetSpecial(const std::string& addr);
+
bool IsBindAny() const; // INADDR_ANY equivalent
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
@@ -249,6 +258,25 @@ class CNetAddr
private:
/**
+ * Parse a Tor address and set this object to it.
+ * @param[in] addr Address to parse, must be a valid C string, for example
+ * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or
+ * 6hzph5hv6337r6p2.onion.
+ * @returns Whether the operation was successful.
+ * @see CNetAddr::IsTor()
+ */
+ bool SetTor(const std::string& addr);
+
+ /**
+ * Parse an I2P address and set this object to it.
+ * @param[in] addr Address to parse, must be a valid C string, for example
+ * ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
+ * @returns Whether the operation was successful.
+ * @see CNetAddr::IsI2P()
+ */
+ bool SetI2P(const std::string& addr);
+
+ /**
* BIP155 network ids recognized by this software.
*/
enum BIP155Network : uint8_t {
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 1c7c35528e..f039f76f82 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -322,6 +322,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsValid());
BOOST_REQUIRE(addr.IsTor());
+ BOOST_CHECK(!addr.IsI2P());
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
@@ -332,6 +333,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsValid());
BOOST_REQUIRE(addr.IsTor());
+ BOOST_CHECK(!addr.IsI2P());
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(!addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), torv3_addr);
@@ -352,6 +354,35 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
// TOR, invalid base32
BOOST_CHECK(!addr.SetSpecial(std::string{"mf*g zak.onion"}));
+ // I2P
+ const char* i2p_addr = "UDHDrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.I2P";
+ BOOST_REQUIRE(addr.SetSpecial(i2p_addr));
+ BOOST_REQUIRE(addr.IsValid());
+ BOOST_REQUIRE(addr.IsI2P());
+
+ BOOST_CHECK(!addr.IsTor());
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(!addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), ToLower(i2p_addr));
+
+ // I2P, correct length, but decodes to less than the expected number of bytes.
+ BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jn=.b32.i2p"));
+
+ // I2P, extra unnecessary padding
+ BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna=.b32.i2p"));
+
+ // I2P, malicious
+ BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v\0wtf.b32.i2p"s));
+
+ // I2P, valid but unsupported (56 Base32 characters)
+ // See "Encrypted LS with Base 32 Addresses" in
+ // https://geti2p.net/spec/encryptedleaseset.txt
+ BOOST_CHECK(
+ !addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscsad.b32.i2p"));
+
+ // I2P, invalid base32
+ BOOST_CHECK(!addr.SetSpecial(std::string{"tp*szydbh4dp.b32.i2p"}));
+
// Internal
addr.SetInternal("esffpp");
BOOST_REQUIRE(!addr.IsValid()); // "internal" is considered invalid