Skip to content

Commit cff65c4

Browse files
vasildlontivero
andcommitted
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 <[email protected]>
1 parent f6c267d commit cff65c4

File tree

3 files changed

+108
-12
lines changed

3 files changed

+108
-12
lines changed

src/netaddress.cpp

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -221,25 +221,34 @@ static void Checksum(Span<const uint8_t> addr_pubkey, uint8_t (&checksum)[CHECKS
221221

222222
}; // namespace torv3
223223

224-
/**
225-
* Parse a TOR address and set this object to it.
226-
*
227-
* @returns Whether or not the operation was successful.
228-
*
229-
* @see CNetAddr::IsTor()
230-
*/
231-
bool CNetAddr::SetSpecial(const std::string& str)
224+
bool CNetAddr::SetSpecial(const std::string& addr)
225+
{
226+
if (!ValidAsCString(addr)) {
227+
return false;
228+
}
229+
230+
if (SetTor(addr)) {
231+
return true;
232+
}
233+
234+
if (SetI2P(addr)) {
235+
return true;
236+
}
237+
238+
return false;
239+
}
240+
241+
bool CNetAddr::SetTor(const std::string& addr)
232242
{
233243
static const char* suffix{".onion"};
234244
static constexpr size_t suffix_len{6};
235245

236-
if (!ValidAsCString(str) || str.size() <= suffix_len ||
237-
str.substr(str.size() - suffix_len) != suffix) {
246+
if (addr.size() <= suffix_len || addr.substr(addr.size() - suffix_len) != suffix) {
238247
return false;
239248
}
240249

241250
bool invalid;
242-
const auto& input = DecodeBase32(str.substr(0, str.size() - suffix_len).c_str(), &invalid);
251+
const auto& input = DecodeBase32(addr.substr(0, addr.size() - suffix_len).c_str(), &invalid);
243252

244253
if (invalid) {
245254
return false;
@@ -275,6 +284,34 @@ bool CNetAddr::SetSpecial(const std::string& str)
275284
return false;
276285
}
277286

287+
bool CNetAddr::SetI2P(const std::string& addr)
288+
{
289+
// I2P addresses that we support consist of 52 base32 characters + ".b32.i2p".
290+
static constexpr size_t b32_len{52};
291+
static const char* suffix{".b32.i2p"};
292+
static constexpr size_t suffix_len{8};
293+
294+
if (addr.size() != b32_len + suffix_len || ToLower(addr.substr(b32_len)) != suffix) {
295+
return false;
296+
}
297+
298+
// Remove the ".b32.i2p" suffix and pad to a multiple of 8 chars, so DecodeBase32()
299+
// can decode it.
300+
const std::string b32_padded = addr.substr(0, b32_len) + "====";
301+
302+
bool invalid;
303+
const auto& address_bytes = DecodeBase32(b32_padded.c_str(), &invalid);
304+
305+
if (invalid || address_bytes.size() != ADDR_I2P_SIZE) {
306+
return false;
307+
}
308+
309+
m_net = NET_I2P;
310+
m_addr.assign(address_bytes.begin(), address_bytes.end());
311+
312+
return true;
313+
}
314+
278315
CNetAddr::CNetAddr(const struct in_addr& ipv4Addr)
279316
{
280317
m_net = NET_IPV4;

src/netaddress.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,16 @@ class CNetAddr
151151

152152
bool SetInternal(const std::string& name);
153153

154-
bool SetSpecial(const std::string &strName); // for Tor addresses
154+
/**
155+
* Parse a Tor or I2P address and set this object to it.
156+
* @param[in] addr Address to parse, for example
157+
* pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or
158+
* ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
159+
* @returns Whether the operation was successful.
160+
* @see CNetAddr::IsTor(), CNetAddr::IsI2P()
161+
*/
162+
bool SetSpecial(const std::string& addr);
163+
155164
bool IsBindAny() const; // INADDR_ANY equivalent
156165
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
157166
bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
@@ -248,6 +257,25 @@ class CNetAddr
248257
friend class CSubNet;
249258

250259
private:
260+
/**
261+
* Parse a Tor address and set this object to it.
262+
* @param[in] addr Address to parse, must be a valid C string, for example
263+
* pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or
264+
* 6hzph5hv6337r6p2.onion.
265+
* @returns Whether the operation was successful.
266+
* @see CNetAddr::IsTor()
267+
*/
268+
bool SetTor(const std::string& addr);
269+
270+
/**
271+
* Parse an I2P address and set this object to it.
272+
* @param[in] addr Address to parse, must be a valid C string, for example
273+
* ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
274+
* @returns Whether the operation was successful.
275+
* @see CNetAddr::IsI2P()
276+
*/
277+
bool SetI2P(const std::string& addr);
278+
251279
/**
252280
* BIP155 network ids recognized by this software.
253281
*/

src/test/net_tests.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
322322
BOOST_REQUIRE(addr.IsValid());
323323
BOOST_REQUIRE(addr.IsTor());
324324

325+
BOOST_CHECK(!addr.IsI2P());
325326
BOOST_CHECK(!addr.IsBindAny());
326327
BOOST_CHECK(addr.IsAddrV1Compatible());
327328
BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
@@ -332,6 +333,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
332333
BOOST_REQUIRE(addr.IsValid());
333334
BOOST_REQUIRE(addr.IsTor());
334335

336+
BOOST_CHECK(!addr.IsI2P());
335337
BOOST_CHECK(!addr.IsBindAny());
336338
BOOST_CHECK(!addr.IsAddrV1Compatible());
337339
BOOST_CHECK_EQUAL(addr.ToString(), torv3_addr);
@@ -352,6 +354,35 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
352354
// TOR, invalid base32
353355
BOOST_CHECK(!addr.SetSpecial(std::string{"mf*g zak.onion"}));
354356

357+
// I2P
358+
const char* i2p_addr = "UDHDrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.I2P";
359+
BOOST_REQUIRE(addr.SetSpecial(i2p_addr));
360+
BOOST_REQUIRE(addr.IsValid());
361+
BOOST_REQUIRE(addr.IsI2P());
362+
363+
BOOST_CHECK(!addr.IsTor());
364+
BOOST_CHECK(!addr.IsBindAny());
365+
BOOST_CHECK(!addr.IsAddrV1Compatible());
366+
BOOST_CHECK_EQUAL(addr.ToString(), ToLower(i2p_addr));
367+
368+
// I2P, correct length, but decodes to less than the expected number of bytes.
369+
BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jn=.b32.i2p"));
370+
371+
// I2P, extra unnecessary padding
372+
BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna=.b32.i2p"));
373+
374+
// I2P, malicious
375+
BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v\0wtf.b32.i2p"s));
376+
377+
// I2P, valid but unsupported (56 Base32 characters)
378+
// See "Encrypted LS with Base 32 Addresses" in
379+
// https://geti2p.net/spec/encryptedleaseset.txt
380+
BOOST_CHECK(
381+
!addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscsad.b32.i2p"));
382+
383+
// I2P, invalid base32
384+
BOOST_CHECK(!addr.SetSpecial(std::string{"tp*szydbh4dp.b32.i2p"}));
385+
355386
// Internal
356387
addr.SetInternal("esffpp");
357388
BOOST_REQUIRE(!addr.IsValid()); // "internal" is considered invalid

0 commit comments

Comments
 (0)