i2p: initial support

This commit is contained in:
Jethro Grassie 2019-01-21 11:50:03 -05:00
parent 31bdf7bd11
commit 123fc2a25a
No known key found for this signature in database
GPG Key ID: DE8ED755616565BB
14 changed files with 450 additions and 40 deletions

View File

@ -1,7 +1,6 @@
# Anonymity Networks with Monero # Anonymity Networks with Monero
Currently only Tor has been integrated into Monero. Providing support for Currently only Tor and I2P have been integrated into Monero. The usage of
Kovri/I2P should be minimal, but has not yet been attempted. The usage of
these networks is still considered experimental - there are a few pessimistic these networks is still considered experimental - there are a few pessimistic
cases where privacy is leaked. The design is intended to maximize privacy of cases where privacy is leaked. The design is intended to maximize privacy of
the source of a transaction by broadcasting it over an anonymity network, while the source of a transaction by broadcasting it over an anonymity network, while
@ -23,8 +22,8 @@ connections enabled.
## P2P Commands ## P2P Commands
Only handshakes, peer timed syncs, and transaction broadcast messages are Only handshakes, peer timed syncs and transaction broadcast messages are
supported over anonymity networks. If one `--add-exclusive-node` onion address supported over anonymity networks. If one `--add-exclusive-node` p2p address
is specified, then no syncing will take place and only transaction broadcasting is specified, then no syncing will take place and only transaction broadcasting
can occur. It is therefore recommended that `--add-exclusive-node` be combined can occur. It is therefore recommended that `--add-exclusive-node` be combined
with additional exclusive IPv4 address(es). with additional exclusive IPv4 address(es).
@ -47,9 +46,9 @@ separate process. On most systems the configuration will look like:
which tells `monerod` that ".onion" p2p addresses can be forwarded to a socks which tells `monerod` that ".onion" p2p addresses can be forwarded to a socks
proxy at IP 127.0.0.1 port 9050 with a max of 10 outgoing connections and proxy at IP 127.0.0.1 port 9050 with a max of 10 outgoing connections and
".i2p" p2p addresses can be forwarded to a socks proxy at IP 127.0.0.1 port 9000 ".b32.i2p" p2p addresses can be forwarded to a socks proxy at IP 127.0.0.1 port
with the default max outgoing connections. Since there are no seed nodes for 9000 with the default max outgoing connections. Since there are no seed nodes
anonymity connections, peers must be manually specified: for anonymity connections, peers must be manually specified:
> `--add-exclusive-node rveahdfho7wo4b2m.onion:28083` > `--add-exclusive-node rveahdfho7wo4b2m.onion:28083`
> `--add-peer rveahdfho7wo4b2m.onion:28083` > `--add-peer rveahdfho7wo4b2m.onion:28083`
@ -64,27 +63,28 @@ Receiving anonymity connections is done through the option
`--anonymous-inbound`. This option tells `monerod` the inbound address, network `--anonymous-inbound`. This option tells `monerod` the inbound address, network
type, and max connections: type, and max connections:
> `--anonymous-inbound rveahdfho7wo4b2m.onion:28083,127.0.0.28083,25` > `--anonymous-inbound rveahdfho7wo4b2m.onion:28083,127.0.0.1:28083,25`
> `--anonymous-inbound foobar.i2p:5000,127.0.0.1:30000` > `--anonymous-inbound cmeua5767mz2q5jsaelk2rxhf67agrwuetaso5dzbenyzwlbkg2q.b32.i2p:5000,127.0.0.1:30000`
which tells `monerod` that a max of 25 inbound Tor connections are being which tells `monerod` that a max of 25 inbound Tor connections are being
received at address "rveahdfho7wo4b2m.onion:28083" and forwarded to `monerod` received at address "rveahdfho7wo4b2m.onion:28083" and forwarded to `monerod`
localhost port 28083, and a default max I2P connections are being received at localhost port 28083, and a default max I2P connections are being received at
address "foobar.i2p:5000" and forwarded to `monerod` localhost port 30000. address "cmeua5767mz2q5jsaelk2rxhf67agrwuetaso5dzbenyzwlbkg2q.b32.i2p:5000" and
forwarded to `monerod` localhost port 30000.
These addresses will be shared with outgoing peers, over the same network type, These addresses will be shared with outgoing peers, over the same network type,
otherwise the peer will not be notified of the peer address by the proxy. otherwise the peer will not be notified of the peer address by the proxy.
### Network Types ### Network Types
#### Tor #### Tor & I2P
Options `--add-exclusive-node` and `--add-peer` recognize ".onion" addresses, Options `--add-exclusive-node` and `--add-peer` recognize ".onion" and
and will properly forward those addresses to the proxy provided with ".b32.i2p" addresses, and will properly forward those addresses to the proxy
`--proxy tor,...`. provided with `--proxy tor,...` or `--proxy i2p,...`.
Option `--anonymous-inbound` also recognizes ".onion" addresses, and will Option `--anonymous-inbound` also recognizes ".onion" and ".b32.i2p" addresses,
automatically be sent out to outgoing Tor connections so the peer can and will automatically be sent out to outgoing Tor/I2P connections so the peer
distribute the address to its other peers. can distribute the address to its other peers.
##### Configuration ##### Configuration
@ -99,11 +99,8 @@ This will store key information in `/var/lib/tor/data/monero` and will forward
`/usr/lib/tor/data/monero/hostname` will contain the ".onion" address for use `/usr/lib/tor/data/monero/hostname` will contain the ".onion" address for use
with `--anonymous-inbound`. with `--anonymous-inbound`.
#### Kovri/I2P I2P must be configured with a standard server tunnel. Configuration differs by
I2P implementation.
Support for this network has not been implemented. Using ".i2p" addresses or
specifying "i2p" will currently generate an error.
## Privacy Limitations ## Privacy Limitations
@ -132,11 +129,11 @@ more difficult.
### Bandwidth Usage ### Bandwidth Usage
An ISP can passively monitor `monerod` connections from a node and observe when An ISP can passively monitor `monerod` connections from a node and observe when
a transaction is sent over a Tor/Kovri connection via timing analysis + size of a transaction is sent over a Tor/I2P connection via timing analysis + size of
data sent during that timeframe. Kovri should provide better protection against data sent during that timeframe. I2P should provide better protection against
this attack - its connections are not circuit based. However, if a node is this attack - its connections are not circuit based. However, if a node is
only using Kovri for broadcasting Monero transactions, the total aggregate of only using I2P for broadcasting Monero transactions, the total aggregate of
Kovri/I2P data would also leak information. I2P data would also leak information.
#### Mitigation #### Mitigation
@ -165,15 +162,15 @@ simply a best effort attempt.
### Active Bandwidth Shaping ### Active Bandwidth Shaping
An attacker could attempt to bandwidth shape traffic in an attempt to determine An attacker could attempt to bandwidth shape traffic in an attempt to determine
the source of a Tor/Kovri/I2P connection. There isn't great mitigation against the source of a Tor/I2P connection. There isn't great mitigation against
this, but Kovri/I2P should provide better protection against this attack since this, but I2P should provide better protection against this attack since
the connections are not circuit based. the connections are not circuit based.
#### Mitigation #### Mitigation
The best mitigiation is to use Kovri/I2P instead of Tor. However, Kovri/I2P The best mitigiation is to use I2P instead of Tor. However, I2P
has a smaller set of users (less cover traffic) and academic reviews, so there has a smaller set of users (less cover traffic) and academic reviews, so there
is a tradeoff in potential isses. Also, anyone attempting this strategy really is a tradeoff in potential isses. Also, anyone attempting this strategy really
wants to uncover a user, it seems unlikely that this would be performed against wants to uncover a user, it seems unlikely that this would be performed against
every Tor/Kovri/I2P user. every Tor/I2P user.

View File

@ -47,6 +47,7 @@
namespace net namespace net
{ {
class tor_address; class tor_address;
class i2p_address;
} }
namespace epee namespace epee
@ -196,7 +197,7 @@ namespace net_utils
template<typename Type> const Type &as() const { return as_mutable<const Type>(); } template<typename Type> const Type &as() const { return as_mutable<const Type>(); }
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
// need to `#include "net/tor_address.h"` when serializing `network_address` // need to `#include "net/[i2p|tor]_address.h"` when serializing `network_address`
static constexpr std::integral_constant<bool, is_store> is_store_{}; static constexpr std::integral_constant<bool, is_store> is_store_{};
std::uint8_t type = std::uint8_t(is_store ? this_ref.get_type_id() : address_type::invalid); std::uint8_t type = std::uint8_t(is_store ? this_ref.get_type_id() : address_type::invalid);
@ -209,6 +210,8 @@ namespace net_utils
return this_ref.template serialize_addr<ipv4_network_address>(is_store_, stg, hparent_section); return this_ref.template serialize_addr<ipv4_network_address>(is_store_, stg, hparent_section);
case address_type::tor: case address_type::tor:
return this_ref.template serialize_addr<net::tor_address>(is_store_, stg, hparent_section); return this_ref.template serialize_addr<net::tor_address>(is_store_, stg, hparent_section);
case address_type::i2p:
return this_ref.template serialize_addr<net::i2p_address>(is_store_, stg, hparent_section);
case address_type::invalid: case address_type::invalid:
default: default:
break; break;

View File

@ -108,7 +108,7 @@
#define P2P_DEFAULT_PACKET_MAX_SIZE 50000000 //50000000 bytes maximum packet size #define P2P_DEFAULT_PACKET_MAX_SIZE 50000000 //50000000 bytes maximum packet size
#define P2P_DEFAULT_PEERS_IN_HANDSHAKE 250 #define P2P_DEFAULT_PEERS_IN_HANDSHAKE 250
#define P2P_DEFAULT_CONNECTION_TIMEOUT 5000 //5 seconds #define P2P_DEFAULT_CONNECTION_TIMEOUT 5000 //5 seconds
#define P2P_DEFAULT_TOR_CONNECT_TIMEOUT 20 // seconds #define P2P_DEFAULT_SOCKS_CONNECT_TIMEOUT 45 // seconds
#define P2P_DEFAULT_PING_CONNECTION_TIMEOUT 2000 //2 seconds #define P2P_DEFAULT_PING_CONNECTION_TIMEOUT 2000 //2 seconds
#define P2P_DEFAULT_INVOKE_TIMEOUT 60*2*1000 //2 minutes #define P2P_DEFAULT_INVOKE_TIMEOUT 60*2*1000 //2 minutes
#define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds #define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds

View File

@ -26,8 +26,8 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(net_sources error.cpp parse.cpp socks.cpp tor_address.cpp) set(net_sources error.cpp parse.cpp socks.cpp tor_address.cpp i2p_address.cpp)
set(net_headers error.h parse.h socks.h tor_address.h) set(net_headers error.h parse.h socks.h tor_address.h i2p_address.h)
monero_add_library(net ${net_sources} ${net_headers}) monero_add_library(net ${net_sources} ${net_headers})
target_link_libraries(net epee ${Boost_ASIO_LIBRARY}) target_link_libraries(net epee ${Boost_ASIO_LIBRARY})

View File

@ -34,6 +34,7 @@ namespace net
{ {
enum class error : int; enum class error : int;
class tor_address; class tor_address;
class i2p_address;
namespace socks namespace socks
{ {

200
src/net/i2p_address.cpp Normal file
View File

@ -0,0 +1,200 @@
// Copyright (c) 2019, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "i2p_address.h"
#include <algorithm>
#include <boost/spirit/include/karma_generate.hpp>
#include <boost/spirit/include/karma_uint.hpp>
#include <cassert>
#include <cstring>
#include <limits>
#include "net/error.h"
#include "serialization/keyvalue_serialization.h"
#include "storages/portable_storage.h"
#include "string_tools.h"
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
{
if (!host.ends_with(tld))
return {net::error::expected_tld};
host.remove_suffix(sizeof(tld) - 1);
if (host.size() != b32_length)
return {net::error::invalid_i2p_address};
if (host.find_first_not_of(base32_alphabet) != boost::string_ref::npos)
return {net::error::invalid_i2p_address};
return success();
}
struct i2p_serialized
{
std::string host;
std::uint16_t port;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(host)
KV_SERIALIZE(port)
END_KV_SERIALIZE_MAP()
};
}
i2p_address::i2p_address(const boost::string_ref host, const std::uint16_t port) noexcept
: port_(port)
{
// this is a private constructor, throw if moved to public
assert(host.size() < sizeof(host_));
const std::size_t length = std::min(sizeof(host_) - 1, host.size());
std::memcpy(host_, host.data(), length);
std::memset(host_ + length, 0, sizeof(host_) - length);
}
const char* i2p_address::unknown_str() noexcept
{
return unknown_host;
}
i2p_address::i2p_address() noexcept
: port_(0)
{
static_assert(sizeof(unknown_host) <= sizeof(host_), "bad buffer size");
std::memcpy(host_, unknown_host, sizeof(unknown_host));
std::memset(host_ + sizeof(unknown_host), 0, sizeof(host_) - sizeof(unknown_host));
}
expect<i2p_address> i2p_address::make(const boost::string_ref address, const std::uint16_t default_port)
{
boost::string_ref host = address.substr(0, address.rfind(':'));
const boost::string_ref port =
address.substr(host.size() + (host.size() == address.size() ? 0 : 1));
MONERO_CHECK(host_check(host));
std::uint16_t porti = default_port;
if (!port.empty() && !epee::string_tools::get_xtype_from_string(porti, std::string{port}))
return {net::error::invalid_port};
static_assert(b32_length + sizeof(tld) == sizeof(i2p_address::host_), "bad internal host size");
return i2p_address{host, porti};
}
bool i2p_address::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
{
i2p_serialized in{};
if (in._load(src, hparent) && in.host.size() < sizeof(host_) && (in.host == unknown_host || !host_check(in.host).has_error()))
{
std::memcpy(host_, in.host.data(), in.host.size());
std::memset(host_ + in.host.size(), 0, sizeof(host_) - in.host.size());
port_ = in.port;
return true;
}
static_assert(sizeof(unknown_host) <= sizeof(host_), "bad buffer size");
std::memcpy(host_, unknown_host, sizeof(unknown_host)); // include null terminator
port_ = 0;
return false;
}
bool i2p_address::store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const
{
const i2p_serialized out{std::string{host_}, port_};
return out.store(dest, hparent);
}
i2p_address::i2p_address(const i2p_address& rhs) noexcept
: port_(rhs.port_)
{
std::memcpy(host_, rhs.host_, sizeof(host_));
}
i2p_address& i2p_address::operator=(const i2p_address& rhs) noexcept
{
if (this != std::addressof(rhs))
{
port_ = rhs.port_;
std::memcpy(host_, rhs.host_, sizeof(host_));
}
return *this;
}
bool i2p_address::is_unknown() const noexcept
{
static_assert(1 <= sizeof(host_), "host size too small");
return host_[0] == '<'; // character is not allowed otherwise
}
bool i2p_address::equal(const i2p_address& rhs) const noexcept
{
return port_ == rhs.port_ && is_same_host(rhs);
}
bool i2p_address::less(const i2p_address& rhs) const noexcept
{
return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port();
}
bool i2p_address::is_same_host(const i2p_address& rhs) const noexcept
{
return std::strcmp(host_str(), rhs.host_str()) == 0;
}
std::string i2p_address::str() const
{
const std::size_t host_length = std::strlen(host_str());
const std::size_t port_length =
port_ == 0 ? 0 : std::numeric_limits<std::uint16_t>::digits10 + 2;
std::string out{};
out.reserve(host_length + port_length);
out.assign(host_str(), host_length);
if (port_ != 0)
{
out.push_back(':');
namespace karma = boost::spirit::karma;
karma::generate(std::back_inserter(out), karma::ushort_, port());
}
return out;
}
}

140
src/net/i2p_address.h Normal file
View File

@ -0,0 +1,140 @@
// Copyright (c) 2019, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <boost/utility/string_ref.hpp>
#include <cstdint>
#include <string>
#include "common/expect.h"
#include "net/enums.h"
#include "net/error.h"
namespace epee
{
namespace serialization
{
class portable_storage;
struct section;
}
}
namespace net
{
//! b32 i2p address; internal format not condensed/decoded.
class i2p_address
{
std::uint16_t port_;
char host_[61]; // null-terminated
//! Keep in private, `host.size()` has no runtime check
i2p_address(boost::string_ref host, std::uint16_t port) noexcept;
public:
//! \return Size of internal buffer for host.
static constexpr std::size_t buffer_size() noexcept { return sizeof(host_); }
//! \return `<unknown tor host>`.
static const char* unknown_str() noexcept;
//! An object with `port() == 0` and `host_str() == unknown_str()`.
i2p_address() noexcept;
//! \return A default constructed `i2p_address` object.
static i2p_address unknown() noexcept { return i2p_address{}; }
/*!
Parse `address` in b32 i2p format (i.e. x.b32.i2p:80)
with `default_port` being used if port is not specified in
`address`.
*/
static expect<i2p_address> make(boost::string_ref address, std::uint16_t default_port = 0);
//! Load from epee p2p format, and \return false if not valid tor address
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
//! Store in epee p2p format
bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const;
// Moves and copies are currently identical
i2p_address(const i2p_address& rhs) noexcept;
~i2p_address() = default;
i2p_address& operator=(const i2p_address& rhs) noexcept;
//! \return True if default constructed or via `unknown()`.
bool is_unknown() const noexcept;
bool equal(const i2p_address& rhs) const noexcept;
bool less(const i2p_address& rhs) const noexcept;
//! \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`.
std::string str() const;
//! \return Null-terminated `x.b32.i2p` value or `unknown_str()`.
const char* host_str() const noexcept { return host_; }
//! \return Port value or `0` if unspecified.
std::uint16_t port() const noexcept { return port_; }
static constexpr bool is_loopback() noexcept { return false; }
static constexpr bool is_local() noexcept { return false; }
static constexpr epee::net_utils::address_type get_type_id() noexcept
{
return epee::net_utils::address_type::i2p;
}
static constexpr epee::net_utils::zone get_zone() noexcept
{
return epee::net_utils::zone::i2p;
}
//! \return `!is_unknown()`.
bool is_blockable() const noexcept { return !is_unknown(); }
};
inline bool operator==(const i2p_address& lhs, const i2p_address& rhs) noexcept
{
return lhs.equal(rhs);
}
inline bool operator!=(const i2p_address& lhs, const i2p_address& rhs) noexcept
{
return !lhs.equal(rhs);
}
inline bool operator<(const i2p_address& lhs, const i2p_address& rhs) noexcept
{
return lhs.less(rhs);
}
} // net

View File

@ -29,6 +29,7 @@
#include "parse.h" #include "parse.h"
#include "net/tor_address.h" #include "net/tor_address.h"
#include "net/i2p_address.h"
#include "string_tools.h" #include "string_tools.h"
namespace net namespace net
@ -43,7 +44,7 @@ namespace net
if (host.ends_with(".onion")) if (host.ends_with(".onion"))
return tor_address::make(address, default_port); return tor_address::make(address, default_port);
if (host.ends_with(".i2p")) if (host.ends_with(".i2p"))
return make_error_code(net::error::invalid_i2p_address); // not yet implemented (prevent public DNS lookup) return i2p_address::make(address, default_port);
std::uint16_t port = default_port; std::uint16_t port = default_port;
if (host.size() < address.size()) if (host.size() < address.size())

View File

@ -37,11 +37,11 @@
namespace net namespace net
{ {
/*! /*!
Identifies onion and IPv4 addresses and returns them as a generic Identifies onion, i2p and IPv4 addresses and returns them as a generic
`network_address`. If the type is unsupported, it might be a hostname, `network_address`. If the type is unsupported, it might be a hostname,
and `error() == net::error::kUnsupportedAddress` is returned. and `error() == net::error::kUnsupportedAddress` is returned.
\param address An onion address, ipv4 address or hostname. Hostname \param address An onion address, i2p address, ipv4 address or hostname. Hostname
will return an error. will return an error.
\param default_port If `address` does not specify a port, this value \param default_port If `address` does not specify a port, this value
will be used. will be used.

View File

@ -40,6 +40,7 @@
#include "net/net_utils_base.h" #include "net/net_utils_base.h"
#include "net/tor_address.h" #include "net/tor_address.h"
#include "net/i2p_address.h"
namespace net namespace net
{ {
@ -273,6 +274,13 @@ namespace socks
return false; return false;
} }
bool client::set_connect_command(const net::i2p_address& address)
{
if (!address.is_unknown())
return set_connect_command(address.host_str(), address.port());
return false;
}
bool client::set_resolve_command(boost::string_ref domain) bool client::set_resolve_command(boost::string_ref domain)
{ {
if (socks_version() != version::v4a_tor) if (socks_version() != version::v4a_tor)

View File

@ -155,6 +155,9 @@ namespace socks
//! Try to set `address` as remote Tor hidden service connection request. //! Try to set `address` as remote Tor hidden service connection request.
bool set_connect_command(const net::tor_address& address); bool set_connect_command(const net::tor_address& address);
//! Try to set `address` as remote i2p hidden service connection request.
bool set_connect_command(const net::i2p_address& address);
//! Try to set `domain` as remote DNS A record lookup request. //! Try to set `domain` as remote DNS A record lookup request.
bool set_resolve_command(boost::string_ref domain); bool set_resolve_command(boost::string_ref domain);

View File

@ -46,13 +46,14 @@
#include "net/socks.h" #include "net/socks.h"
#include "net/parse.h" #include "net/parse.h"
#include "net/tor_address.h" #include "net/tor_address.h"
#include "net/i2p_address.h"
#include "p2p/p2p_protocol_defs.h" #include "p2p/p2p_protocol_defs.h"
#include "string_tools.h" #include "string_tools.h"
namespace namespace
{ {
constexpr const boost::chrono::milliseconds future_poll_interval{500}; constexpr const boost::chrono::milliseconds future_poll_interval{500};
constexpr const std::chrono::seconds tor_connect_timeout{P2P_DEFAULT_TOR_CONNECT_TIMEOUT}; constexpr const std::chrono::seconds socks_connect_timeout{P2P_DEFAULT_SOCKS_CONNECT_TIMEOUT};
std::int64_t get_max_connections(const boost::iterator_range<boost::string_ref::const_iterator> value) noexcept std::int64_t get_max_connections(const boost::iterator_range<boost::string_ref::const_iterator> value) noexcept
{ {
@ -90,6 +91,9 @@ namespace
case net::tor_address::get_type_id(): case net::tor_address::get_type_id():
set = client->set_connect_command(remote.as<net::tor_address>()); set = client->set_connect_command(remote.as<net::tor_address>());
break; break;
case net::i2p_address::get_type_id():
set = client->set_connect_command(remote.as<net::i2p_address>());
break;
default: default:
MERROR("Unsupported network address in socks_connect"); MERROR("Unsupported network address in socks_connect");
return false; return false;
@ -177,6 +181,9 @@ namespace nodetool
case epee::net_utils::zone::tor: case epee::net_utils::zone::tor:
proxies.back().zone = epee::net_utils::zone::tor; proxies.back().zone = epee::net_utils::zone::tor;
break; break;
case epee::net_utils::zone::i2p:
proxies.back().zone = epee::net_utils::zone::i2p;
break;
default: default:
MERROR("Invalid network for --" << arg_proxy.name); MERROR("Invalid network for --" << arg_proxy.name);
return boost::none; return boost::none;
@ -235,6 +242,10 @@ namespace nodetool
inbounds.back().our_address = std::move(*our_address); inbounds.back().our_address = std::move(*our_address);
inbounds.back().default_remote = net::tor_address::unknown(); inbounds.back().default_remote = net::tor_address::unknown();
break; break;
case net::i2p_address::get_type_id():
inbounds.back().our_address = std::move(*our_address);
inbounds.back().default_remote = net::i2p_address::unknown();
break;
default: default:
MERROR("Invalid inbound address (" << address << ") for --" << arg_anonymous_inbound.name << ": " << (our_address ? "invalid type" : our_address.error().message())); MERROR("Invalid inbound address (" << address << ") for --" << arg_anonymous_inbound.name << ": " << (our_address ? "invalid type" : our_address.error().message()));
return boost::none; return boost::none;
@ -308,7 +319,7 @@ namespace nodetool
const auto start = std::chrono::steady_clock::now(); const auto start = std::chrono::steady_clock::now();
while (socks_result.wait_for(future_poll_interval) == boost::future_status::timeout) while (socks_result.wait_for(future_poll_interval) == boost::future_status::timeout)
{ {
if (tor_connect_timeout < std::chrono::steady_clock::now() - start) if (socks_connect_timeout < std::chrono::steady_clock::now() - start)
{ {
MERROR("Timeout on socks connect (" << proxy << " to " << remote.str() << ")"); MERROR("Timeout on socks connect (" << proxy << " to " << remote.str() << ")");
return boost::none; return boost::none;

View File

@ -35,6 +35,7 @@
#include "common/expect.h" #include "common/expect.h"
#include "net/net_utils_base.h" #include "net/net_utils_base.h"
#include "net/tor_address.h" #include "net/tor_address.h"
#include "net/i2p_address.h"
#include "p2p/p2p_protocol_defs.h" #include "p2p/p2p_protocol_defs.h"
#ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED #ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED
@ -76,6 +77,9 @@ namespace boost
case net::tor_address::get_type_id(): case net::tor_address::get_type_id():
do_serialize<net::tor_address>(is_saving, a, na); do_serialize<net::tor_address>(is_saving, a, na);
break; break;
case net::i2p_address::get_type_id():
do_serialize<net::i2p_address>(is_saving, a, na);
break;
case epee::net_utils::address_type::invalid: case epee::net_utils::address_type::invalid:
default: default:
throw std::runtime_error("Unsupported network address type"); throw std::runtime_error("Unsupported network address type");
@ -106,6 +110,20 @@ namespace boost
a.save_binary(na.host_str(), length); a.save_binary(na.host_str(), length);
} }
template <class Archive, class ver_type>
inline void save(Archive& a, const net::i2p_address& na, const ver_type)
{
const size_t length = std::strlen(na.host_str());
if (length > 255)
MONERO_THROW(net::error::invalid_i2p_address, "i2p address too long");
const uint16_t port{na.port()};
const uint8_t len = length;
a & port;
a & len;
a.save_binary(na.host_str(), length);
}
template <class Archive, class ver_type> template <class Archive, class ver_type>
inline void load(Archive& a, net::tor_address& na, const ver_type) inline void load(Archive& a, net::tor_address& na, const ver_type)
{ {
@ -127,12 +145,39 @@ namespace boost
na = MONERO_UNWRAP(net::tor_address::make(host, port)); na = MONERO_UNWRAP(net::tor_address::make(host, port));
} }
template <class Archive, class ver_type>
inline void load(Archive& a, net::i2p_address& na, const ver_type)
{
uint16_t port = 0;
uint8_t length = 0;
a & port;
a & length;
if (length > net::i2p_address::buffer_size())
MONERO_THROW(net::error::invalid_i2p_address, "i2p address too long");
char host[net::i2p_address::buffer_size()] = {0};
a.load_binary(host, length);
host[sizeof(host) - 1] = 0;
if (std::strcmp(host, net::i2p_address::unknown_str()) == 0)
na = net::i2p_address::unknown();
else
na = MONERO_UNWRAP(net::i2p_address::make(host, port));
}
template <class Archive, class ver_type> template <class Archive, class ver_type>
inline void serialize(Archive &a, net::tor_address& na, const ver_type ver) inline void serialize(Archive &a, net::tor_address& na, const ver_type ver)
{ {
boost::serialization::split_free(a, na, ver); boost::serialization::split_free(a, na, ver);
} }
template <class Archive, class ver_type>
inline void serialize(Archive &a, net::i2p_address& na, const ver_type ver)
{
boost::serialization::split_free(a, na, ver);
}
template <class Archive, class ver_type> template <class Archive, class ver_type>
inline void serialize(Archive &a, nodetool::peerlist_entry& pl, const ver_type ver) inline void serialize(Archive &a, nodetool::peerlist_entry& pl, const ver_type ver)
{ {

View File

@ -35,6 +35,7 @@
#include "serialization/keyvalue_serialization.h" #include "serialization/keyvalue_serialization.h"
#include "net/net_utils_base.h" #include "net/net_utils_base.h"
#include "net/tor_address.h" // needed for serialization #include "net/tor_address.h" // needed for serialization
#include "net/i2p_address.h" // needed for serialization
#include "misc_language.h" #include "misc_language.h"
#include "string_tools.h" #include "string_tools.h"
#include "time_helper.h" #include "time_helper.h"