aboutsummaryrefslogtreecommitdiff
path: root/src/net
diff options
context:
space:
mode:
authorJethro Grassie <jtg@xtrabass.com>2019-01-21 11:50:03 -0500
committerJethro Grassie <jtg@xtrabass.com>2019-01-30 13:37:45 -0500
commit123fc2a25afaebe89b94c9806eb05033e021d807 (patch)
tree2151ced1046c2b1e40c8c123fa60d2d38ba0df7f /src/net
parentMerge pull request #4988 (diff)
downloadmonero-123fc2a25afaebe89b94c9806eb05033e021d807.tar.xz
i2p: initial support
Diffstat (limited to 'src/net')
-rw-r--r--src/net/CMakeLists.txt4
-rw-r--r--src/net/fwd.h1
-rw-r--r--src/net/i2p_address.cpp200
-rw-r--r--src/net/i2p_address.h140
-rw-r--r--src/net/parse.cpp3
-rw-r--r--src/net/parse.h4
-rw-r--r--src/net/socks.cpp8
-rw-r--r--src/net/socks.h3
8 files changed, 358 insertions, 5 deletions
diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt
index a81372125..fdb988f39 100644
--- a/src/net/CMakeLists.txt
+++ b/src/net/CMakeLists.txt
@@ -26,8 +26,8 @@
# 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.
-set(net_sources error.cpp parse.cpp socks.cpp tor_address.cpp)
-set(net_headers error.h parse.h socks.h tor_address.h)
+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 i2p_address.h)
monero_add_library(net ${net_sources} ${net_headers})
target_link_libraries(net epee ${Boost_ASIO_LIBRARY})
diff --git a/src/net/fwd.h b/src/net/fwd.h
index ee7c539b0..7cae88251 100644
--- a/src/net/fwd.h
+++ b/src/net/fwd.h
@@ -34,6 +34,7 @@ namespace net
{
enum class error : int;
class tor_address;
+ class i2p_address;
namespace socks
{
diff --git a/src/net/i2p_address.cpp b/src/net/i2p_address.cpp
new file mode 100644
index 000000000..cba829d3f
--- /dev/null
+++ b/src/net/i2p_address.cpp
@@ -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;
+ }
+}
diff --git a/src/net/i2p_address.h b/src/net/i2p_address.h
new file mode 100644
index 000000000..28a1118ba
--- /dev/null
+++ b/src/net/i2p_address.h
@@ -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
diff --git a/src/net/parse.cpp b/src/net/parse.cpp
index ebf91eeff..eaaadb67e 100644
--- a/src/net/parse.cpp
+++ b/src/net/parse.cpp
@@ -29,6 +29,7 @@
#include "parse.h"
#include "net/tor_address.h"
+#include "net/i2p_address.h"
#include "string_tools.h"
namespace net
@@ -43,7 +44,7 @@ namespace net
if (host.ends_with(".onion"))
return tor_address::make(address, default_port);
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;
if (host.size() < address.size())
diff --git a/src/net/parse.h b/src/net/parse.h
index 9195ddc2b..5804c4128 100644
--- a/src/net/parse.h
+++ b/src/net/parse.h
@@ -37,11 +37,11 @@
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,
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.
\param default_port If `address` does not specify a port, this value
will be used.
diff --git a/src/net/socks.cpp b/src/net/socks.cpp
index f31efc8c1..53154369b 100644
--- a/src/net/socks.cpp
+++ b/src/net/socks.cpp
@@ -40,6 +40,7 @@
#include "net/net_utils_base.h"
#include "net/tor_address.h"
+#include "net/i2p_address.h"
namespace net
{
@@ -273,6 +274,13 @@ namespace socks
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)
{
if (socks_version() != version::v4a_tor)
diff --git a/src/net/socks.h b/src/net/socks.h
index d29a51ccb..825937792 100644
--- a/src/net/socks.h
+++ b/src/net/socks.h
@@ -155,6 +155,9 @@ namespace socks
//! Try to set `address` as remote Tor hidden service connection request.
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.
bool set_resolve_command(boost::string_ref domain);