32#include <boost/asio/buffer.hpp>
33#include <boost/asio/io_service.hpp>
34#include <boost/asio/ip/tcp.hpp>
35#include <boost/asio/read.hpp>
36#include <boost/asio/steady_timer.hpp>
37#include <boost/asio/write.hpp>
38#include <boost/endian/conversion.hpp>
39#include <boost/system/error_code.hpp>
40#include <boost/thread/scoped_thread.hpp>
41#include <boost/thread/thread.hpp>
44#include <gtest/gtest.h>
60 static constexpr const char v2_onion[] =
61 "etnto2bturnore26.onion";
62 static constexpr const char v3_onion[] =
63 "vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion";
66TEST(tor_address, constants)
93 std::string onion{v3_onion};
98TEST(tor_address, unblockable_types)
157 EXPECT_EQ(std::string{v2_onion} +
":6545", address2.
str().c_str());
174 EXPECT_EQ(std::string{v3_onion} +
":65535", address2.
str().c_str());
188TEST(tor_address, generic_network_address)
215 net::tor_address tor;
223TEST(tor_address, epee_serializev_v2)
225 std::string buffer{};
238 test_command command{};
261 EXPECT_EQ(std::strlen(v2_onion), host.size());
274TEST(tor_address, epee_serializev_v3)
276 std::string buffer{};
289 test_command command{};
312 EXPECT_EQ(std::strlen(v3_onion), host.size());
325TEST(tor_address, epee_serialize_unknown)
327 std::string buffer{};
340 test_command command{};
376TEST(tor_address, boost_serialize_v2)
378 std::string buffer{};
386 std::ostringstream stream{};
391 buffer = stream.str();
401 std::istringstream stream{buffer};
411TEST(tor_address, boost_serialize_v3)
413 std::string buffer{};
421 std::ostringstream stream{};
426 buffer = stream.str();
436 std::istringstream stream{buffer};
446TEST(tor_address, boost_serialize_unknown)
448 std::string buffer{};
456 std::ostringstream stream{};
461 buffer = stream.str();
471 std::istringstream stream{buffer};
481TEST(get_network_address, onion)
531 using stream_type = boost::asio::ip::tcp;
535 boost::asio::io_service io_service;
536 boost::asio::io_service::work work;
537 stream_type::socket server;
538 stream_type::acceptor acceptor;
540 std::atomic<bool> connected;
546 acceptor(io_service),
547 io([this] () {
try { this->io_service.run(); }
catch (
const std::exception& e) {
MERROR(e.what()); }}),
550 acceptor.open(boost::asio::ip::tcp::v4());
551 acceptor.bind(stream_type::endpoint{boost::asio::ip::address_v4::loopback(), 0});
553 acceptor.async_accept(server, [
this] (boost::system::error_code error) {
554 this->connected =
true;
556 throw boost::system::system_error{
error};
560 ~io_thread() noexcept
568 struct checked_client
570 std::atomic<bool>* called_;
573 void operator()(boost::system::error_code error, net::socks::client::stream_type::socket&&)
const
575 EXPECT_EQ(expected_,
bool(error)) <<
"Socks server: " <<
error.message();
582TEST(socks_client, unsupported_command)
584 boost::asio::io_service io_service{};
585 stream_type::socket client{io_service};
593 EXPECT_FALSE(test_client->set_connect_command(
"example.com", 8080));
596 EXPECT_FALSE(test_client->set_resolve_command(
"example.com"));
602 boost::asio::io_service io_service{};
603 stream_type::socket client{io_service};
612TEST(socks_client, connect_command)
615 stream_type::socket client{io.io_service};
617 std::atomic<bool> called{
false};
623 ASSERT_TRUE(test_client->set_connect_command(
"example.com", 8080));
626 while (!io.connected)
629 const std::uint8_t expected_bytes[] = {
630 4, 1, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00,
631 'e',
'x',
'a',
'm',
'p',
'l',
'e',
'.',
'c',
'o',
'm', 0x00
634 std::uint8_t actual_bytes[
sizeof(expected_bytes)];
635 boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
636 EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes,
sizeof(actual_bytes)) == 0);
638 const std::uint8_t reply_bytes[] = {0, 90, 0, 0, 0, 0, 0, 0};
639 boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
645TEST(socks_client, connect_command_failed)
648 stream_type::socket client{io.io_service};
650 std::atomic<bool> called{
false};
657 test_client->set_connect_command(
663 while (!io.connected)
666 const std::uint8_t expected_bytes[] = {
667 4, 1, 0x0b, 0xb8, 0x00, 0x00, 0x13, 0x88, 0x00
670 std::uint8_t actual_bytes[
sizeof(expected_bytes)];
671 boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
672 EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes,
sizeof(actual_bytes)) == 0);
674 const std::uint8_t reply_bytes[] = {0, 91, 0, 0, 0, 0, 0, 0};
675 boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
681TEST(socks_client, resolve_command)
683 static std::uint8_t reply_bytes[] = {0, 90, 0, 0, 0xff, 0, 0xad, 0};
687 std::atomic<unsigned> called_;
690 resolve_client(stream_type::socket&& proxy)
696 virtual void done(boost::system::error_code error, std::shared_ptr<client> self)
override
699 EXPECT_EQ(expected_,
bool(error)) <<
"Resolve failure: " << error.message();
703 ASSERT_EQ(
sizeof(reply_bytes), buffer().size());
704 EXPECT_EQ(0u, std::memcmp(buffer().data(), reply_bytes,
sizeof(reply_bytes)));
712 stream_type::socket client{io.io_service};
714 auto test_client = std::make_shared<resolve_client>(std::move(client));
717 ASSERT_TRUE(test_client->set_resolve_command(
"example.com"));
720 while (!io.connected)
723 const std::uint8_t expected_bytes[] = {
724 4, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
725 'e',
'x',
'a',
'm',
'p',
'l',
'e',
'.',
'c',
'o',
'm', 0x00
728 std::uint8_t actual_bytes[
sizeof(expected_bytes)];
729 boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
730 EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes,
sizeof(actual_bytes)) == 0);
732 boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
735 while (test_client->called_ == 0);
737 test_client->expected_ =
true;
738 ASSERT_TRUE(test_client->set_resolve_command(
"example.com"));
742 boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
743 EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes,
sizeof(actual_bytes)) == 0);
746 boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
749 while (test_client->called_ == 1);
755 boost::asio::steady_timer timeout{io.io_service};
756 timeout.expires_from_now(std::chrono::seconds{5});
758 boost::unique_future<boost::asio::ip::tcp::socket> sock =
761 while (!io.connected)
763 const std::uint8_t expected_bytes[] = {
764 4, 1, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00,
765 'e',
'x',
'a',
'm',
'p',
'l',
'e',
'.',
'c',
'o',
'm', 0x00
768 std::uint8_t actual_bytes[
sizeof(expected_bytes)];
769 boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
770 EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes,
sizeof(actual_bytes)) == 0);
772 const std::uint8_t reply_bytes[] = {0, 90, 0, 0, 0, 0, 0, 0};
773 boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
775 ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3}));
782 boost::asio::steady_timer timeout{io.io_service};
783 timeout.expires_from_now(std::chrono::seconds{5});
785 boost::unique_future<boost::asio::ip::tcp::socket> sock =
788 while (!io.connected)
790 const std::uint8_t expected_bytes[] = {
791 4, 1, 0x1f, 0x90, 0xfa, 0x58, 0x7d, 0x63, 0x00
794 std::uint8_t actual_bytes[
sizeof(expected_bytes)];
795 boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
796 EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes,
sizeof(actual_bytes)) == 0);
798 const std::uint8_t reply_bytes[] = {0, 90, 0, 0, 0, 0, 0, 0};
799 boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
801 ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3}));
808 boost::asio::steady_timer timeout{io.io_service};
809 timeout.expires_from_now(std::chrono::seconds{5});
811 boost::unique_future<boost::asio::ip::tcp::socket> sock =
814 while (!io.connected)
816 const std::uint8_t expected_bytes[] = {
817 4, 1, 0x1f, 0x90, 0xfa, 0x58, 0x7d, 0x63, 0x00
820 std::uint8_t actual_bytes[
sizeof(expected_bytes)];
821 boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
822 EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes,
sizeof(actual_bytes)) == 0);
824 const std::uint8_t reply_bytes[] = {0, 91, 0, 0, 0, 0, 0, 0};
825 boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
827 ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3}));
828 EXPECT_THROW(sock.get().is_open(), boost::system::system_error);
834 boost::asio::steady_timer timeout{io.io_service};
835 timeout.expires_from_now(std::chrono::milliseconds{10});
837 boost::unique_future<boost::asio::ip::tcp::socket> sock =
840 ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3}));
841 EXPECT_THROW(sock.get().is_open(), boost::system::system_error);
851 std::make_error_condition(std::errc::not_a_socket),
860}().matches(std::errc::not_a_socket)
868catch (
const std::system_error& e)
871EXPECT_EQ(std::make_error_condition(std::errc::not_a_socket), e.code());
886ASSERT_EQ(0u, zmq_bind(recv_socket.get(),
"inproc://testing"));
887ASSERT_EQ(0u, zmq_connect(send_socket.get(),
"inproc://testing"));
910ASSERT_EQ(0u, zmq_bind(recv_socket.get(),
"inproc://testing"));
911ASSERT_EQ(0u, zmq_connect(send_socket.get(),
"inproc://testing"));
917for (
unsigned i = 0; i < 3; ++i)
924 reinterpret_cast<const std::uint8_t*
>(std::addressof(
message[0])) + (i * 333), 333
934TEST(zmq, read_write_termination)
940boost::scoped_thread<> thread{};
947ASSERT_EQ(0u, zmq_bind(recv_socket.get(),
"inproc://testing"));
948ASSERT_EQ(0u, zmq_connect(send_socket.get(),
"inproc://testing"));
960thread = boost::scoped_thread<>{
962 [&context] () { context.reset(); }
address_type get_type_id() const
std::string host_str() const
bool is_blockable() const
bool get_value(const std::string &value_name, t_value &val, hsection hparent_section)
hsection open_section(const std::string §ion_name, hsection hparent_section, bool create_if_notexist=false)
bool store_to_binary(binarybuffer &target)
bool set_value(const std::string &value_name, const t_value &target, hsection hparent_section)
bool load_from_binary(const epee::span< const uint8_t > target)
Non-owning sequence of data. Does not deep copy.
*return False if otherwise error()
Client support for socks connect and resolve commands.
static bool connect_and_send(std::shared_ptr< client > self, const stream_type::endpoint &proxy_address)
static bool send(std::shared_ptr< client > self)
Tor onion address; internal format not condensed/decoded.
static expect< tor_address > make(boost::string_ref address, std::uint16_t default_port=0)
static constexpr bool is_local() noexcept
bool less(const tor_address &rhs) const noexcept
static const char * unknown_str() noexcept
bool equal(const tor_address &rhs) const noexcept
static constexpr epee::net_utils::address_type get_type_id() noexcept
static constexpr bool is_loopback() noexcept
bool is_unknown() const noexcept
const char * host_str() const noexcept
std::uint16_t port() const noexcept
bool is_blockable() const noexcept
static tor_address unknown() noexcept
bool is_same_host(const tor_address &rhs) const noexcept
std::string message("Message requiring signing")
#define ELECTRONEUM_UNWRAP(...)
#define ASSERT_EQ(val1, val2)
#define EXPECT_EQ(val1, val2)
#define EXPECT_NE(val1, val2)
#define EXPECT_THROW(statement, expected_exception)
#define ASSERT_FALSE(condition)
#define ASSERT_NE(val1, val2)
#define EXPECT_TRUE(condition)
#define EXPECT_STREQ(s1, s2)
#define TEST(test_case_name, test_name)
#define ASSERT_TRUE(condition)
#define EXPECT_FALSE(condition)
#define EXPECT_STRNE(s1, s2)
#define EXPECT_LT(val1, val2)
#define KV_SERIALIZE(varialble)
#define END_KV_SERIALIZE_MAP()
#define BEGIN_KV_SERIALIZE_MAP()
std::enable_if< std::is_pod< T >::value, T >::type rand()
span< const T > strspan(const std::string &s) noexcept
make a span from a std::string
error
Tracks LMDB error codes.
std::shared_ptr< client > make_connect_client(client::stream_type::socket &&proxy, socks::version ver, Handler handler)
@ v4a_tor
Extensions defined in Tor codebase.
std::unique_ptr< void, terminate > context
Unique ZMQ context handle, calls zmq_term on destruction.
const std::error_category & error_category() noexcept
expect< void > send(const epee::span< const std::uint8_t > payload, void *const socket, const int flags) noexcept
std::error_code make_error_code(int code) noexcept
expect< std::string > receive(void *const socket, const int flags)
std::unique_ptr< void, close > socket
Unique ZMQ socket handle, calls zmq_close on destruction.
expect< epee::net_utils::network_address > get_network_address(const boost::string_ref address, const std::uint16_t default_port)
@ invalid_port
Outside of 0-65535 range.
@ invalid_tor_address
Invalid base32 or length.
@ unsupported_address
Type not supported by get_network_address.
Primarily for use with epee::net_utils::http_client.
#define ELECTRONEUM_ZMQ_THROW(msg)
Throw an exception with a custom msg, current ZMQ error code, filename, and line number.
#define ELECTRONEUM_ZMQ_CHECK(...)
If the expression is less than 0, return the current ZMQ error code.