mirror of
https://github.com/monero-project/monero.git
synced 2024-11-17 08:17:37 +00:00
i2p: initial support
This commit is contained in:
parent
31bdf7bd11
commit
123fc2a25a
14 changed files with 450 additions and 40 deletions
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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})
|
||||||
|
|
|
@ -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
200
src/net/i2p_address.cpp
Normal 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
140
src/net/i2p_address.h
Normal 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
|
|
@ -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())
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2014-2018, The Monero Project
|
// Copyright (c) 2014-2018, The Monero Project
|
||||||
//
|
//
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue