mirror of
https://github.com/monero-project/monero.git
synced 2025-08-02 07:36:11 -04:00
Adding initial support for broadcasting transactions over Tor
- Support for ".onion" in --add-exclusive-node and --add-peer - Add --anonymizing-proxy for outbound Tor connections - Add --anonymous-inbounds for inbound Tor connections - Support for sharing ".onion" addresses over Tor connections - Support for broadcasting transactions received over RPC exclusively over Tor (else broadcast over public IP when Tor not enabled).
This commit is contained in:
parent
1e5cd3b35a
commit
973403bc9f
39 changed files with 4298 additions and 831 deletions
34
src/net/CMakeLists.txt
Normal file
34
src/net/CMakeLists.txt
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Copyright (c) 2018, 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.
|
||||
|
||||
set(net_sources error.cpp parse.cpp socks.cpp tor_address.cpp)
|
||||
set(net_headers error.h parse.h socks.h tor_address.h)
|
||||
|
||||
monero_add_library(net ${net_sources} ${net_headers})
|
||||
target_link_libraries(net epee ${Boost_ASIO_LIBRARY})
|
||||
|
92
src/net/error.cpp
Normal file
92
src/net/error.cpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
// Copyright (c) 2018, 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 "error.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct net_category : std::error_category
|
||||
{
|
||||
net_category() noexcept
|
||||
: std::error_category()
|
||||
{}
|
||||
|
||||
const char* name() const noexcept override
|
||||
{
|
||||
return "net::error_category";
|
||||
}
|
||||
|
||||
std::string message(int value) const override
|
||||
{
|
||||
switch (net::error(value))
|
||||
{
|
||||
case net::error::expected_tld:
|
||||
return "Expected top-level domain";
|
||||
case net::error::invalid_host:
|
||||
return "Host value is not valid";
|
||||
case net::error::invalid_i2p_address:
|
||||
return "Invalid I2P address";
|
||||
case net::error::invalid_port:
|
||||
return "Invalid port value (expected 0-65535)";
|
||||
case net::error::invalid_tor_address:
|
||||
return "Invalid Tor address";
|
||||
case net::error::unsupported_address:
|
||||
return "Network address not supported";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "Unknown net::error";
|
||||
}
|
||||
|
||||
std::error_condition default_error_condition(int value) const noexcept override
|
||||
{
|
||||
switch (net::error(value))
|
||||
{
|
||||
case net::error::invalid_port:
|
||||
return std::errc::result_out_of_range;
|
||||
case net::error::expected_tld:
|
||||
case net::error::invalid_tor_address:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return std::error_condition{value, *this};
|
||||
}
|
||||
};
|
||||
} // anonymous
|
||||
|
||||
namespace net
|
||||
{
|
||||
std::error_category const& error_category() noexcept
|
||||
{
|
||||
static const net_category instance{};
|
||||
return instance;
|
||||
}
|
||||
}
|
64
src/net/error.h
Normal file
64
src/net/error.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) 2018, 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 <system_error>
|
||||
#include <type_traits>
|
||||
|
||||
namespace net
|
||||
{
|
||||
//! General net errors
|
||||
enum class error : int
|
||||
{
|
||||
// 0 reserved for success (as per expect<T>)
|
||||
expected_tld = 1, //!< Expected a tld
|
||||
invalid_host, //!< Hostname is not valid
|
||||
invalid_i2p_address,
|
||||
invalid_port, //!< Outside of 0-65535 range
|
||||
invalid_tor_address,//!< Invalid base32 or length
|
||||
unsupported_address //!< Type not supported by `get_network_address`
|
||||
};
|
||||
|
||||
//! \return `std::error_category` for `net` namespace.
|
||||
std::error_category const& error_category() noexcept;
|
||||
|
||||
//! \return `net::error` as a `std::error_code` value.
|
||||
inline std::error_code make_error_code(error value) noexcept
|
||||
{
|
||||
return std::error_code{int(value), error_category()};
|
||||
}
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct is_error_code_enum<::net::error>
|
||||
: true_type
|
||||
{};
|
||||
}
|
45
src/net/fwd.h
Normal file
45
src/net/fwd.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) 2018, 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 <cstdint>
|
||||
|
||||
namespace net
|
||||
{
|
||||
enum class error : int;
|
||||
class tor_address;
|
||||
|
||||
namespace socks
|
||||
{
|
||||
class client;
|
||||
template<typename> class connect_handler;
|
||||
enum class error : int;
|
||||
enum class version : std::uint8_t;
|
||||
}
|
||||
}
|
60
src/net/parse.cpp
Normal file
60
src/net/parse.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) 2018, 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 "parse.h"
|
||||
|
||||
#include "net/tor_address.h"
|
||||
#include "string_tools.h"
|
||||
|
||||
namespace net
|
||||
{
|
||||
expect<epee::net_utils::network_address>
|
||||
get_network_address(const boost::string_ref address, const std::uint16_t default_port)
|
||||
{
|
||||
const boost::string_ref host = address.substr(0, address.rfind(':'));
|
||||
|
||||
if (host.empty())
|
||||
return make_error_code(net::error::invalid_host);
|
||||
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)
|
||||
|
||||
std::uint16_t port = default_port;
|
||||
if (host.size() < address.size())
|
||||
{
|
||||
if (!epee::string_tools::get_xtype_from_string(port, std::string{address.substr(host.size() + 1)}))
|
||||
return make_error_code(net::error::invalid_port);
|
||||
}
|
||||
|
||||
std::uint32_t ip = 0;
|
||||
if (epee::string_tools::get_ip_int32_from_string(ip, std::string{host}))
|
||||
return {epee::net_utils::ipv4_network_address{ip, port}};
|
||||
return make_error_code(net::error::unsupported_address);
|
||||
}
|
||||
}
|
54
src/net/parse.h
Normal file
54
src/net/parse.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2018, 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 "common/expect.h"
|
||||
#include "net/net_utils_base.h"
|
||||
|
||||
namespace net
|
||||
{
|
||||
/*!
|
||||
Identifies onion 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
|
||||
will return an error.
|
||||
\param default_port If `address` does not specify a port, this value
|
||||
will be used.
|
||||
|
||||
\return A tor or IPv4 address, else error.
|
||||
*/
|
||||
expect<epee::net_utils::network_address>
|
||||
get_network_address(boost::string_ref address, std::uint16_t default_port);
|
||||
}
|
||||
|
308
src/net/socks.cpp
Normal file
308
src/net/socks.cpp
Normal file
|
@ -0,0 +1,308 @@
|
|||
// Copyright (c) 2018, 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 "socks.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/read.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/endian/arithmetic.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "net/net_utils_base.h"
|
||||
#include "net/tor_address.h"
|
||||
|
||||
namespace net
|
||||
{
|
||||
namespace socks
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr const unsigned v4_reply_size = 8;
|
||||
constexpr const std::uint8_t v4_connect_command = 1;
|
||||
constexpr const std::uint8_t v4tor_resolve_command = 0xf0;
|
||||
constexpr const std::uint8_t v4_request_granted = 90;
|
||||
|
||||
struct v4_header
|
||||
{
|
||||
std::uint8_t version;
|
||||
std::uint8_t command_code;
|
||||
boost::endian::big_uint16_t port;
|
||||
boost::endian::big_uint32_t ip;
|
||||
};
|
||||
|
||||
std::size_t write_domain_header(epee::span<std::uint8_t> out, const std::uint8_t command, const std::uint16_t port, const boost::string_ref domain)
|
||||
{
|
||||
if (std::numeric_limits<std::size_t>::max() - sizeof(v4_header) - 2 < domain.size())
|
||||
return 0;
|
||||
|
||||
const std::size_t buf_size = sizeof(v4_header) + domain.size() + 2;
|
||||
if (out.size() < buf_size)
|
||||
return 0;
|
||||
|
||||
// version 4, 1 indicates invalid ip for domain extension
|
||||
const v4_header temp{4, command, port, std::uint32_t(1)};
|
||||
std::memcpy(out.data(), std::addressof(temp), sizeof(temp));
|
||||
out.remove_prefix(sizeof(temp));
|
||||
|
||||
*(out.data()) = 0;
|
||||
out.remove_prefix(1);
|
||||
|
||||
std::memcpy(out.data(), domain.data(), domain.size());
|
||||
out.remove_prefix(domain.size());
|
||||
|
||||
*(out.data()) = 0;
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
struct socks_category : boost::system::error_category
|
||||
{
|
||||
explicit socks_category() noexcept
|
||||
: boost::system::error_category()
|
||||
{}
|
||||
|
||||
const char* name() const noexcept override
|
||||
{
|
||||
return "net::socks::error_category";
|
||||
}
|
||||
|
||||
virtual std::string message(int value) const override
|
||||
{
|
||||
switch (socks::error(value))
|
||||
{
|
||||
case socks::error::rejected:
|
||||
return "Socks request rejected or failed";
|
||||
case socks::error::identd_connection:
|
||||
return "Socks request rejected because server cannot connect to identd on the client";
|
||||
case socks::error::identd_user:
|
||||
return "Socks request rejected because the client program and identd report different user-ids";
|
||||
|
||||
case socks::error::bad_read:
|
||||
return "Socks boost::async_read read fewer bytes than expected";
|
||||
case socks::error::bad_write:
|
||||
return "Socks boost::async_write wrote fewer bytes than expected";
|
||||
case socks::error::unexpected_version:
|
||||
return "Socks server returned unexpected version in reply";
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "Unknown net::socks::error";
|
||||
}
|
||||
|
||||
boost::system::error_condition default_error_condition(int value) const noexcept override
|
||||
{
|
||||
switch (socks::error(value))
|
||||
{
|
||||
case socks::error::bad_read:
|
||||
case socks::error::bad_write:
|
||||
return boost::system::errc::io_error;
|
||||
case socks::error::unexpected_version:
|
||||
return boost::system::errc::protocol_error;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
if (1 <= value && value <= 256)
|
||||
return boost::system::errc::protocol_error;
|
||||
|
||||
return boost::system::error_condition{value, *this};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const boost::system::error_category& error_category() noexcept
|
||||
{
|
||||
static const socks_category instance{};
|
||||
return instance;
|
||||
}
|
||||
|
||||
struct client::completed
|
||||
{
|
||||
std::shared_ptr<client> self_;
|
||||
|
||||
void operator()(const boost::system::error_code error, const std::size_t bytes) const
|
||||
{
|
||||
static_assert(1 < sizeof(self_->buffer_), "buffer too small for v4 response");
|
||||
|
||||
if (self_)
|
||||
{
|
||||
client& self = *self_;
|
||||
self.buffer_size_ = std::min(bytes, sizeof(self.buffer_));
|
||||
|
||||
if (error)
|
||||
self.done(error, std::move(self_));
|
||||
else if (self.buffer().size() < sizeof(v4_header))
|
||||
self.done(socks::error::bad_read, std::move(self_));
|
||||
else if (self.buffer_[0] != 0) // response version
|
||||
self.done(socks::error::unexpected_version, std::move(self_));
|
||||
else if (self.buffer_[1] != v4_request_granted)
|
||||
self.done(socks::error(int(self.buffer_[1]) + 1), std::move(self_));
|
||||
else
|
||||
self.done(boost::system::error_code{}, std::move(self_));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct client::read
|
||||
{
|
||||
std::shared_ptr<client> self_;
|
||||
|
||||
static boost::asio::mutable_buffers_1 get_buffer(client& self) noexcept
|
||||
{
|
||||
static_assert(sizeof(v4_header) <= sizeof(self.buffer_), "buffer too small for v4 response");
|
||||
return boost::asio::buffer(self.buffer_, sizeof(v4_header));
|
||||
}
|
||||
|
||||
void operator()(const boost::system::error_code error, const std::size_t bytes)
|
||||
{
|
||||
if (self_)
|
||||
{
|
||||
client& self = *self_;
|
||||
if (error)
|
||||
self.done(error, std::move(self_));
|
||||
else if (bytes < self.buffer().size())
|
||||
self.done(socks::error::bad_write, std::move(self_));
|
||||
else
|
||||
boost::asio::async_read(self.proxy_, get_buffer(self), completed{std::move(self_)});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct client::write
|
||||
{
|
||||
std::shared_ptr<client> self_;
|
||||
|
||||
static boost::asio::const_buffers_1 get_buffer(client const& self) noexcept
|
||||
{
|
||||
return boost::asio::buffer(self.buffer_, self.buffer_size_);
|
||||
}
|
||||
|
||||
void operator()(const boost::system::error_code error)
|
||||
{
|
||||
if (self_)
|
||||
{
|
||||
client& self = *self_;
|
||||
if (error)
|
||||
self.done(error, std::move(self_));
|
||||
else
|
||||
boost::asio::async_write(self.proxy_, get_buffer(self), read{std::move(self_)});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
client::client(stream_type::socket&& proxy, socks::version ver)
|
||||
: proxy_(std::move(proxy)), buffer_size_(0), buffer_(), ver_(ver)
|
||||
{}
|
||||
|
||||
client::~client() {}
|
||||
|
||||
bool client::set_connect_command(const epee::net_utils::ipv4_network_address& address)
|
||||
{
|
||||
switch (socks_version())
|
||||
{
|
||||
case version::v4:
|
||||
case version::v4a:
|
||||
case version::v4a_tor:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
static_assert(sizeof(v4_header) < sizeof(buffer_), "buffer size too small for request");
|
||||
static_assert(0 < sizeof(buffer_), "buffer size too small for null termination");
|
||||
|
||||
// version 4
|
||||
const v4_header temp{4, v4_connect_command, address.port(), boost::endian::big_to_native(address.ip())};
|
||||
std::memcpy(std::addressof(buffer_), std::addressof(temp), sizeof(temp));
|
||||
buffer_[sizeof(temp)] = 0;
|
||||
buffer_size_ = sizeof(temp) + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool client::set_connect_command(const boost::string_ref domain, std::uint16_t port)
|
||||
{
|
||||
switch (socks_version())
|
||||
{
|
||||
case version::v4a:
|
||||
case version::v4a_tor:
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::size_t buf_used = write_domain_header(buffer_, v4_connect_command, port, domain);
|
||||
buffer_size_ = buf_used;
|
||||
return buf_used != 0;
|
||||
}
|
||||
|
||||
bool client::set_connect_command(const net::tor_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)
|
||||
return false;
|
||||
|
||||
const std::size_t buf_used = write_domain_header(buffer_, v4tor_resolve_command, 0, domain);
|
||||
buffer_size_ = buf_used;
|
||||
return buf_used != 0;
|
||||
}
|
||||
|
||||
bool client::connect_and_send(std::shared_ptr<client> self, const stream_type::endpoint& proxy_address)
|
||||
{
|
||||
if (self && !self->buffer().empty())
|
||||
{
|
||||
client& alias = *self;
|
||||
alias.proxy_.async_connect(proxy_address, write{std::move(self)});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool client::send(std::shared_ptr<client> self)
|
||||
{
|
||||
if (self && !self->buffer().empty())
|
||||
{
|
||||
client& alias = *self;
|
||||
boost::asio::async_write(alias.proxy_, write::get_buffer(alias), read{std::move(self)});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // socks
|
||||
} // net
|
225
src/net/socks.h
Normal file
225
src/net/socks.h
Normal file
|
@ -0,0 +1,225 @@
|
|||
// Copyright (c) 2018, 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 <cstdint>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "net/fwd.h"
|
||||
#include "span.h"
|
||||
|
||||
namespace epee
|
||||
{
|
||||
namespace net_utils
|
||||
{
|
||||
class ipv4_network_address;
|
||||
}
|
||||
}
|
||||
|
||||
namespace net
|
||||
{
|
||||
namespace socks
|
||||
{
|
||||
//! Supported socks variants.
|
||||
enum class version : std::uint8_t
|
||||
{
|
||||
v4 = 0,
|
||||
v4a,
|
||||
v4a_tor //!< Extensions defined in Tor codebase
|
||||
};
|
||||
|
||||
//! Possible errors with socks communication. Defined in https://www.openssh.com/txt/socks4.protocol
|
||||
enum class error : int
|
||||
{
|
||||
// 0 is reserved for success value
|
||||
// 1-256 -> reserved for error values from socks server (+1 from wire value).
|
||||
rejected = 92,
|
||||
identd_connection,
|
||||
identd_user,
|
||||
// Specific to application
|
||||
bad_read = 257,
|
||||
bad_write,
|
||||
unexpected_version
|
||||
};
|
||||
|
||||
/* boost::system::error_code is extended for easier compatibility with
|
||||
boost::asio errors. If std::error_code is needed (with expect<T> for
|
||||
instance), then upgrade to boost 1.65+ or use conversion code in
|
||||
develop branch at boost/system/detail/std_interoperability.hpp */
|
||||
|
||||
//! \return boost::system::error_category for net::socks namespace
|
||||
const boost::system::error_category& error_category() noexcept;
|
||||
|
||||
//! \return net::socks::error as a boost::system::error_code.
|
||||
inline boost::system::error_code make_error_code(error value) noexcept
|
||||
{
|
||||
return boost::system::error_code{int(value), socks::error_category()};
|
||||
}
|
||||
|
||||
//! Client support for socks connect and resolve commands.
|
||||
class client
|
||||
{
|
||||
boost::asio::ip::tcp::socket proxy_;
|
||||
std::uint16_t buffer_size_;
|
||||
std::uint8_t buffer_[1024];
|
||||
socks::version ver_;
|
||||
|
||||
/*!
|
||||
Only invoked after `*send(...)` function completes or fails.
|
||||
`bool(error) == false` indicates success; `self.get()` is always
|
||||
`this` and allows implementations to skip
|
||||
`std::enable_shared_from_this<T>` (ASIO callbacks need shared_ptr).
|
||||
The design saves space and reduces cycles (everything uses moves,
|
||||
so no atomic operations are ever necessary).
|
||||
|
||||
\param error when processing last command (if any).
|
||||
\param self `shared_ptr<client>` handle to `this`.
|
||||
*/
|
||||
virtual void done(boost::system::error_code error, std::shared_ptr<client> self) = 0;
|
||||
|
||||
public:
|
||||
using stream_type = boost::asio::ip::tcp;
|
||||
|
||||
// defined in cpp
|
||||
struct write;
|
||||
struct read;
|
||||
struct completed;
|
||||
|
||||
/*!
|
||||
\param proxy ownership is passed into `this`. Does not have to be
|
||||
in connected state.
|
||||
\param ver socks version for the connection.
|
||||
*/
|
||||
explicit client(stream_type::socket&& proxy, socks::version ver);
|
||||
|
||||
client(const client&) = delete;
|
||||
virtual ~client();
|
||||
client& operator=(const client&) = delete;
|
||||
|
||||
//! \return Ownership of socks client socket object.
|
||||
stream_type::socket take_socket()
|
||||
{
|
||||
return stream_type::socket{std::move(proxy_)};
|
||||
}
|
||||
|
||||
//! \return Socks version.
|
||||
socks::version socks_version() const noexcept { return ver_; }
|
||||
|
||||
//! \return Contents of internal buffer.
|
||||
epee::span<const std::uint8_t> buffer() const noexcept
|
||||
{
|
||||
return {buffer_, buffer_size_};
|
||||
}
|
||||
|
||||
//! \post `buffer.empty()`.
|
||||
void clear_command() noexcept { buffer_size_ = 0; }
|
||||
|
||||
//! Try to set `address` as remote connection request.
|
||||
bool set_connect_command(const epee::net_utils::ipv4_network_address& address);
|
||||
|
||||
//! Try to set `domain` + `port` as remote connection request.
|
||||
bool set_connect_command(boost::string_ref domain, std::uint16_t port);
|
||||
|
||||
//! Try to set `address` as remote Tor hidden service connection request.
|
||||
bool set_connect_command(const net::tor_address& address);
|
||||
|
||||
//! Try to set `domain` as remote DNS A record lookup request.
|
||||
bool set_resolve_command(boost::string_ref domain);
|
||||
|
||||
/*!
|
||||
Asynchronously connect to `proxy_address` then issue command in
|
||||
`buffer()`. The `done(...)` method will be invoked upon completion
|
||||
with `self` and potential `error`s.
|
||||
|
||||
\note Must use one of the `self->set_*_command` calls before using
|
||||
this function.
|
||||
|
||||
\param self ownership of object is given to function.
|
||||
\param proxy_address of the socks server.
|
||||
\return False if `self->buffer().empty()` (no command set).
|
||||
*/
|
||||
static bool connect_and_send(std::shared_ptr<client> self, const stream_type::endpoint& proxy_address);
|
||||
|
||||
/*!
|
||||
Assume existing connection to proxy server; asynchronously issue
|
||||
command in `buffer()`. The `done(...)` method will be invoked
|
||||
upon completion with `self` and potential `error`s.
|
||||
|
||||
\note Must use one of the `self->set_*_command` calls before using
|
||||
the function.
|
||||
|
||||
\param self ownership of object is given to function.
|
||||
\return False if `self->buffer().empty()` (no command set).
|
||||
*/
|
||||
static bool send(std::shared_ptr<client> self);
|
||||
};
|
||||
|
||||
template<typename Handler>
|
||||
class connect_client : public client
|
||||
{
|
||||
Handler handler_;
|
||||
|
||||
virtual void done(boost::system::error_code error, std::shared_ptr<client>) override
|
||||
{
|
||||
handler_(error, take_socket());
|
||||
}
|
||||
|
||||
public:
|
||||
explicit connect_client(stream_type::socket&& proxy, socks::version ver, Handler&& handler)
|
||||
: client(std::move(proxy), ver), handler_(std::move(handler))
|
||||
{}
|
||||
|
||||
virtual ~connect_client() override {}
|
||||
};
|
||||
|
||||
template<typename Handler>
|
||||
inline std::shared_ptr<client>
|
||||
make_connect_client(client::stream_type::socket&& proxy, socks::version ver, Handler handler)
|
||||
{
|
||||
return std::make_shared<connect_client<Handler>>(std::move(proxy), ver, std::move(handler));
|
||||
}
|
||||
} // socks
|
||||
} // net
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace system
|
||||
{
|
||||
template<>
|
||||
struct is_error_code_enum<net::socks::error>
|
||||
: true_type
|
||||
{};
|
||||
} // system
|
||||
} // boost
|
203
src/net/tor_address.cpp
Normal file
203
src/net/tor_address.cpp
Normal file
|
@ -0,0 +1,203 @@
|
|||
// Copyright (c) 2018, 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 "tor_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
|
||||
{
|
||||
constexpr const char tld[] = u8".onion";
|
||||
constexpr const char unknown_host[] = "<unknown tor host>";
|
||||
|
||||
constexpr const unsigned v2_length = 16;
|
||||
constexpr const unsigned v3_length = 56;
|
||||
|
||||
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);
|
||||
|
||||
//! \TODO v3 has checksum, base32 decoding is required to verify it
|
||||
if (host.size() != v2_length && host.size() != v3_length)
|
||||
return {net::error::invalid_tor_address};
|
||||
if (host.find_first_not_of(base32_alphabet) != boost::string_ref::npos)
|
||||
return {net::error::invalid_tor_address};
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
struct tor_serialized
|
||||
{
|
||||
std::string host;
|
||||
std::uint16_t port;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(host)
|
||||
KV_SERIALIZE(port)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
}
|
||||
|
||||
tor_address::tor_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* tor_address::unknown_str() noexcept
|
||||
{
|
||||
return unknown_host;
|
||||
}
|
||||
|
||||
tor_address::tor_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<tor_address> tor_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(v2_length <= v3_length, "bad internal host size");
|
||||
static_assert(v3_length + sizeof(tld) == sizeof(tor_address::host_), "bad internal host size");
|
||||
return tor_address{host, porti};
|
||||
}
|
||||
|
||||
bool tor_address::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
|
||||
{
|
||||
tor_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 tor_address::store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const
|
||||
{
|
||||
const tor_serialized out{std::string{host_}, port_};
|
||||
return out.store(dest, hparent);
|
||||
}
|
||||
|
||||
tor_address::tor_address(const tor_address& rhs) noexcept
|
||||
: port_(rhs.port_)
|
||||
{
|
||||
std::memcpy(host_, rhs.host_, sizeof(host_));
|
||||
}
|
||||
|
||||
tor_address& tor_address::operator=(const tor_address& rhs) noexcept
|
||||
{
|
||||
if (this != std::addressof(rhs))
|
||||
{
|
||||
port_ = rhs.port_;
|
||||
std::memcpy(host_, rhs.host_, sizeof(host_));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool tor_address::is_unknown() const noexcept
|
||||
{
|
||||
static_assert(1 <= sizeof(host_), "host size too small");
|
||||
return host_[0] == '<'; // character is not allowed otherwise
|
||||
}
|
||||
|
||||
bool tor_address::equal(const tor_address& rhs) const noexcept
|
||||
{
|
||||
return port_ == rhs.port_ && is_same_host(rhs);
|
||||
}
|
||||
|
||||
bool tor_address::less(const tor_address& rhs) const noexcept
|
||||
{
|
||||
return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port();
|
||||
}
|
||||
|
||||
bool tor_address::is_same_host(const tor_address& rhs) const noexcept
|
||||
{
|
||||
//! \TODO v2 and v3 should be comparable - requires base32
|
||||
return std::strcmp(host_str(), rhs.host_str()) == 0;
|
||||
}
|
||||
|
||||
std::string tor_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/tor_address.h
Normal file
140
src/net/tor_address.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
// Copyright (c) 2018, 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
|
||||
{
|
||||
//! Tor onion address; internal format not condensed/decoded.
|
||||
class tor_address
|
||||
{
|
||||
std::uint16_t port_;
|
||||
char host_[63]; // null-terminated
|
||||
|
||||
//! Keep in private, `host.size()` has no runtime check
|
||||
tor_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()`.
|
||||
tor_address() noexcept;
|
||||
|
||||
//! \return A default constructed `tor_address` object.
|
||||
static tor_address unknown() noexcept { return tor_address{}; }
|
||||
|
||||
/*!
|
||||
Parse `address` in onion v2 or v3 format with (i.e. x.onion:80)
|
||||
with `default_port` being used iff port is not specified in
|
||||
`address`.
|
||||
*/
|
||||
static expect<tor_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
|
||||
|
||||
tor_address(const tor_address& rhs) noexcept;
|
||||
~tor_address() = default;
|
||||
tor_address& operator=(const tor_address& rhs) noexcept;
|
||||
|
||||
//! \return True if default constructed or via `unknown()`.
|
||||
bool is_unknown() const noexcept;
|
||||
|
||||
bool equal(const tor_address& rhs) const noexcept;
|
||||
bool less(const tor_address& rhs) const noexcept;
|
||||
|
||||
//! \return True if onion addresses are identical.
|
||||
bool is_same_host(const tor_address& rhs) const noexcept;
|
||||
|
||||
//! \return `x.onion` or `x.onion:z` if `port() != 0`.
|
||||
std::string str() const;
|
||||
|
||||
//! \return Null-terminated `x.onion` 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::tor;
|
||||
}
|
||||
|
||||
static constexpr epee::net_utils::zone get_zone() noexcept
|
||||
{
|
||||
return epee::net_utils::zone::tor;
|
||||
}
|
||||
|
||||
//! \return `!is_unknown()`.
|
||||
bool is_blockable() const noexcept { return !is_unknown(); }
|
||||
};
|
||||
|
||||
inline bool operator==(const tor_address& lhs, const tor_address& rhs) noexcept
|
||||
{
|
||||
return lhs.equal(rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const tor_address& lhs, const tor_address& rhs) noexcept
|
||||
{
|
||||
return !lhs.equal(rhs);
|
||||
}
|
||||
|
||||
inline bool operator<(const tor_address& lhs, const tor_address& rhs) noexcept
|
||||
{
|
||||
return lhs.less(rhs);
|
||||
}
|
||||
} // net
|
Loading…
Add table
Add a link
Reference in a new issue