aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/core_tests/bulletproof_plus.cpp2
-rw-r--r--tests/core_tests/bulletproofs.cpp2
-rw-r--r--tests/core_tests/rct.cpp4
-rw-r--r--tests/core_tests/rct2.cpp2
-rwxr-xr-xtests/functional_tests/functional_tests_rpc.py2
-rw-r--r--tests/fuzz/levin.cpp2
-rw-r--r--tests/net_load_tests/clt.cpp2
-rw-r--r--tests/net_load_tests/net_load_tests.h2
-rw-r--r--tests/net_load_tests/srv.cpp2
-rw-r--r--tests/performance_tests/check_tx_signature.h4
-rw-r--r--tests/performance_tests/construct_tx.h2
-rw-r--r--tests/trezor/daemon.cpp1
-rw-r--r--tests/unit_tests/epee_boosted_tcp_server.cpp117
-rw-r--r--tests/unit_tests/epee_levin_protocol_handler_async.cpp26
-rw-r--r--tests/unit_tests/http.cpp37
-rw-r--r--tests/unit_tests/levin.cpp2
-rw-r--r--tests/unit_tests/net.cpp525
-rw-r--r--tests/unit_tests/node_server.cpp158
-rw-r--r--tests/unit_tests/zmq_rpc.cpp18
20 files changed, 809 insertions, 103 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index ec0d46893..29d75aeab 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -36,8 +36,6 @@ set(TEST_DATA_DIR "${CMAKE_CURRENT_LIST_DIR}/data")
if (WIN32 AND STATIC)
add_definitions(-DSTATICLIB)
- # miniupnp changed their static define
- add_definitions(-DMINIUPNP_STATICLIB)
endif ()
find_package(GTest)
diff --git a/tests/core_tests/bulletproof_plus.cpp b/tests/core_tests/bulletproof_plus.cpp
index d32f9b689..75e927009 100644
--- a/tests/core_tests/bulletproof_plus.cpp
+++ b/tests/core_tests/bulletproof_plus.cpp
@@ -136,7 +136,7 @@ bool gen_bpp_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
rct_txes.resize(rct_txes.size() + 1);
- bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx && !post_tx(rct_txes.back(), n))
diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp
index ee747ab99..71bae61d1 100644
--- a/tests/core_tests/bulletproofs.cpp
+++ b/tests/core_tests/bulletproofs.cpp
@@ -136,7 +136,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
rct_txes.resize(rct_txes.size() + 1);
- bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx && !post_tx(rct_txes.back(), n))
diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp
index a8e545dbd..1f281239d 100644
--- a/tests/core_tests/rct.cpp
+++ b/tests/core_tests/rct.cpp
@@ -122,7 +122,7 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry
std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
- bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes[n], tx_key, additional_tx_keys, true);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), rct_txes[n], tx_key, additional_tx_keys, true);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
events.push_back(rct_txes[n]);
starting_rct_tx_hashes.push_back(get_transaction_hash(rct_txes[n]));
@@ -229,7 +229,7 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry
std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[0].get_keys().m_account_address.m_spend_public_key] = {0,0};
- bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), tx, tx_key, additional_tx_keys, true, rct_config, use_view_tags);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), tx, tx_key, additional_tx_keys, true, rct_config, use_view_tags);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx)
diff --git a/tests/core_tests/rct2.cpp b/tests/core_tests/rct2.cpp
index 971f321a5..696e6a2b7 100644
--- a/tests/core_tests/rct2.cpp
+++ b/tests/core_tests/rct2.cpp
@@ -136,7 +136,7 @@ bool gen_rct2_tx_validation_base::generate_with(std::vector<test_event_entry>& e
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
rct_txes.resize(rct_txes.size() + 1);
- bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx && !post_tx(rct_txes.back(), n))
diff --git a/tests/functional_tests/functional_tests_rpc.py b/tests/functional_tests/functional_tests_rpc.py
index d8ec78810..ec761e0e6 100755
--- a/tests/functional_tests/functional_tests_rpc.py
+++ b/tests/functional_tests/functional_tests_rpc.py
@@ -48,7 +48,7 @@ WALLET_DIRECTORY = builddir + "/functional-tests-directory"
FUNCTIONAL_TESTS_DIRECTORY = builddir + "/tests/functional_tests"
DIFFICULTY = 10
-monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", str(DIFFICULTY), "--no-igd", "--p2p-bind-port", "monerod_p2p_port", "--rpc-bind-port", "monerod_rpc_port", "--zmq-rpc-bind-port", "monerod_zmq_port", "--zmq-pub", "monerod_zmq_pub", "--non-interactive", "--disable-dns-checkpoints", "--check-updates", "disabled", "--rpc-ssl", "disabled", "--data-dir", "monerod_data_dir", "--log-level", "1", "--rpc-max-connections-per-private-ip", "100", "--rpc-max-connections", "100"]
+monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", str(DIFFICULTY), "--p2p-bind-port", "monerod_p2p_port", "--rpc-bind-port", "monerod_rpc_port", "--zmq-rpc-bind-port", "monerod_zmq_port", "--zmq-pub", "monerod_zmq_pub", "--non-interactive", "--disable-dns-checkpoints", "--check-updates", "disabled", "--rpc-ssl", "disabled", "--data-dir", "monerod_data_dir", "--log-level", "1", "--rpc-max-connections-per-private-ip", "100", "--rpc-max-connections", "100"]
monerod_extra = [
["--offline"],
["--rpc-payment-address", "44SKxxLQw929wRF6BA9paQ1EWFshNnKhXM3qz6Mo3JGDE2YG3xyzVutMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUiqhcwR", "--rpc-payment-difficulty", str(DIFFICULTY), "--rpc-payment-credits", "5000", "--offline"],
diff --git a/tests/fuzz/levin.cpp b/tests/fuzz/levin.cpp
index 4c30980e9..922c284e6 100644
--- a/tests/fuzz/levin.cpp
+++ b/tests/fuzz/levin.cpp
@@ -159,7 +159,7 @@ namespace
return m_send_return;
}
- virtual bool close() { return true; }
+ virtual bool close(const bool wait_for_shutdown) { return true; }
virtual bool send_done() { return true; }
virtual bool call_run_once_service_io() { return true; }
virtual bool request_callback() { return true; }
diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp
index 6149de653..d0e79468a 100644
--- a/tests/net_load_tests/clt.cpp
+++ b/tests/net_load_tests/clt.cpp
@@ -110,7 +110,7 @@ namespace
{
if (!m_connections[id].is_nil())
{
- m_tcp_server.get_config_object().close(m_connections[id]);
+ m_tcp_server.get_config_object().close(m_connections[id], true);
return true;
}
else
diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h
index 7af92f159..03ac952f1 100644
--- a/tests/net_load_tests/net_load_tests.h
+++ b/tests/net_load_tests/net_load_tests.h
@@ -188,7 +188,7 @@ namespace net_load_tests
LOG_PRINT_L0("Connection isn't opened");
return false;
}
- if (!m_tcp_server.get_config_object().close(m_connections[idx]))
+ if (!m_tcp_server.get_config_object().close(m_connections[idx], true))
{
LOG_PRINT_L0("Close connection error: " << m_connections[idx]);
if (!ignore_close_fails)
diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp
index de06812a1..59a92718a 100644
--- a/tests/net_load_tests/srv.cpp
+++ b/tests/net_load_tests/srv.cpp
@@ -176,7 +176,7 @@ namespace
if (!ctx.m_closed)
{
ctx.m_closed = true;
- m_tcp_server.get_config_object().close(ctx.m_connection_id);
+ m_tcp_server.get_config_object().close(ctx.m_connection_id, true);
}
else
{
diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h
index 3578a513a..6ba09f739 100644
--- a/tests/performance_tests/check_tx_signature.h
+++ b/tests/performance_tests/check_tx_signature.h
@@ -72,7 +72,7 @@ public:
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0};
rct::RCTConfig rct_config{range_proof_type, bp_version};
- if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, tx_key, additional_tx_keys, rct, rct_config))
+ if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, boost::none, std::vector<uint8_t>(), m_tx, tx_key, additional_tx_keys, rct, rct_config))
return false;
get_transaction_prefix_hash(m_tx, m_tx_prefix_hash);
@@ -136,7 +136,7 @@ public:
m_txes.resize(a_num_txes + (extra_outs > 0 ? 1 : 0));
for (size_t n = 0; n < a_num_txes; ++n)
{
- if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_txes[n], tx_key, additional_tx_keys, true, {rct::RangeProofPaddedBulletproof, 2}))
+ if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, boost::none, std::vector<uint8_t>(), m_txes[n], tx_key, additional_tx_keys, true, {rct::RangeProofPaddedBulletproof, 2}))
return false;
}
diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h
index 96da25306..d7f02a6d5 100644
--- a/tests/performance_tests/construct_tx.h
+++ b/tests/performance_tests/construct_tx.h
@@ -74,7 +74,7 @@ public:
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0};
rct::RCTConfig rct_config{range_proof_type, bp_version};
- return cryptonote::construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, m_destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, tx_key, additional_tx_keys, rct, rct_config);
+ return cryptonote::construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, m_destinations, boost::none, std::vector<uint8_t>(), m_tx, tx_key, additional_tx_keys, rct, rct_config);
}
private:
diff --git a/tests/trezor/daemon.cpp b/tests/trezor/daemon.cpp
index 546a8a180..8eeb30377 100644
--- a/tests/trezor/daemon.cpp
+++ b/tests/trezor/daemon.cpp
@@ -56,7 +56,6 @@ void mock_daemon::default_options(boost::program_options::variables_map & vm)
tools::options::set_option(vm, nodetool::arg_p2p_add_exclusive_node, po::variable_value(exclusive_nodes, false));
tools::options::set_option(vm, nodetool::arg_p2p_bind_ip, po::variable_value(std::string("127.0.0.1"), false));
- tools::options::set_option(vm, nodetool::arg_no_igd, po::variable_value(true, false));
tools::options::set_option(vm, cryptonote::arg_offline, po::variable_value(true, false));
tools::options::set_option(vm, "disable-dns-checkpoints", po::variable_value(true, false));
diff --git a/tests/unit_tests/epee_boosted_tcp_server.cpp b/tests/unit_tests/epee_boosted_tcp_server.cpp
index c56174109..6052f7a50 100644
--- a/tests/unit_tests/epee_boosted_tcp_server.cpp
+++ b/tests/unit_tests/epee_boosted_tcp_server.cpp
@@ -37,10 +37,12 @@
#include "gtest/gtest.h"
+#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "include_base_utils.h"
#include "string_tools.h"
#include "net/abstract_tcp_server2.h"
#include "net/levin_protocol_handler_async.h"
+#include "p2p/net_node.h"
namespace
{
@@ -234,7 +236,7 @@ TEST(test_epee_connection, test_lifetime)
auto tag = create_connection();
ASSERT_TRUE(shared_state->get_connections_count() == 1);
bool success = shared_state->for_connection(tag, [shared_state](context_t& context){
- shared_state->close(context.m_connection_id);
+ shared_state->close(context.m_connection_id, true);
context.m_remote_address.get_zone();
return true;
});
@@ -250,9 +252,9 @@ TEST(test_epee_connection, test_lifetime)
success = shared_state->foreach_connection([&index, shared_state, &tags, &create_connection](context_t& context){
if (!index)
for (const auto &t: tags)
- shared_state->close(t);
+ shared_state->close(t, true);
- shared_state->close(context.m_connection_id);
+ shared_state->close(context.m_connection_id, true);
context.m_remote_address.get_zone();
++index;
@@ -266,7 +268,7 @@ TEST(test_epee_connection, test_lifetime)
index = 0;
success = shared_state->foreach_connection([&index, shared_state](context_t& context){
- shared_state->close(context.m_connection_id);
+ shared_state->close(context.m_connection_id, true);
context.m_remote_address.get_zone();
++index;
return true;
@@ -296,7 +298,7 @@ TEST(test_epee_connection, test_lifetime)
});
ASSERT_TRUE(success);
}
- shared_state->close(tag);
+ shared_state->close(tag, true);
ASSERT_TRUE(shared_state->get_connections_count() == 0);
}
@@ -450,7 +452,7 @@ TEST(test_epee_connection, test_lifetime)
auto tag = context.m_connection_id;
boost::asio::post(io_context, [conn] { conn->cancel(); });
conn.reset();
- s->close(tag);
+ s->close(tag, true);
while (s->sock_count);
}
});
@@ -643,7 +645,7 @@ TEST(boosted_tcp_server, strand_deadlock)
}
else if(context.m_recv_cnt == 2) {
guard.unlock();
- socket->close();
+ socket->close(false);
}
}
return true;
@@ -711,3 +713,104 @@ TEST(boosted_tcp_server, strand_deadlock)
server.timed_wait_server_stop(5 * 1000);
server.deinit_server();
}
+
+TEST(boosted_tcp_server, shutdown)
+{
+ struct context_t: epee::net_utils::connection_context_base {
+ static constexpr size_t get_max_bytes(int) noexcept { return -1; }
+ static constexpr int handshake_command() noexcept { return 1001; }
+ static constexpr bool handshake_complete() noexcept { return true; }
+ };
+
+ struct config_t : epee::levin::async_protocol_handler_config<context_t> {
+ void received_handshake() { handshake_received.raise(); }
+ epee::simple_event handshake_received;
+ };
+
+ struct handler_t : epee::levin::async_protocol_handler<context_t> {
+ using config_type = config_t;
+ using connection_context = context_t;
+ using epee::levin::async_protocol_handler<context_t>::async_protocol_handler;
+
+ bool handle_recv(const void *data, size_t bytes_transferred)
+ {
+ // We don't respond to the handshake (the async_invoke_remote_command2 is waiting for a response)
+ MINFO("handle_recv just came in");
+ config_t* config = dynamic_cast<config_t*>(&m_config);
+ if (config == nullptr)
+ throw std::runtime_error("m_config must be of type config_t");
+ config->received_handshake();
+ return true;
+ }
+ };
+
+ boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::make_address("127.0.0.1"), 5262);
+ epee::net_utils::boosted_tcp_server<handler_t> server(epee::net_utils::e_connection_type_P2P);
+ server.init_server(
+ endpoint.port(),
+ endpoint.address().to_string(),
+ {},
+ {},
+ {},
+ true,
+ epee::net_utils::ssl_support_t::e_ssl_support_disabled
+ );
+
+ // Run the server in a thread and wait for it to start
+ MINFO("Starting the server");
+ std::thread running_server([&]{server.run_server(2, true/*wait*/);} );
+
+ // Have the server connect to itself
+ MINFO("Connecting the server to itself");
+ context_t context;
+ {
+ epee::simple_event connected;
+ server.async_call(
+ [&]{
+ ASSERT_TRUE(
+ server.connect(
+ endpoint.address().to_string(),
+ std::to_string(endpoint.port()),
+ 5,
+ context,
+ "0.0.0.0",
+ epee::net_utils::ssl_support_t::e_ssl_support_disabled
+ )
+ );
+ connected.raise();
+ }
+ );
+ connected.wait();
+ }
+
+ // Invoke handshake to the connection, and wait for cb cancel in a separate thread
+ MINFO("Invoking handshake");
+ epee::simple_event ev;
+ {
+ using COMMAND_HANDSHAKE = nodetool::COMMAND_HANDSHAKE_T<cryptonote::CORE_SYNC_DATA>;
+ COMMAND_HANDSHAKE::request arg;
+ bool r = epee::net_utils::async_invoke_remote_command2<COMMAND_HANDSHAKE::response>(context, COMMAND_HANDSHAKE::ID, arg, server.get_config_object(),
+ [&ev](int code, const COMMAND_HANDSHAKE::response&, context_t&)
+ {
+ ASSERT_EQ(code, LEVIN_ERROR_CONNECTION_DESTROYED);
+ ev.raise();
+ }, P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT);
+ ASSERT_TRUE(r);
+
+ MINFO("Waiting for handshake invocation to be received");
+ server.get_config_object().handshake_received.wait();
+ }
+
+ // Now stop the server, providing the callback necessary to wait for all connections to shutdown
+ const auto close_all_connections = [&]()
+ {
+ server.get_config_object().close(context.m_connection_id, true/*wait_for_shutdown*/);
+ };
+
+ MINFO("Stopping the server");
+ server.send_stop_signal(close_all_connections);
+ running_server.join();
+
+ MINFO("Waiting for handshake to cancel");
+ ev.wait();
+}
diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp
index 9dbb2c19b..103ee9cfa 100644
--- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp
+++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp
@@ -151,7 +151,7 @@ namespace
return m_send_return;
}
- virtual bool close() { /*std::cout << "test_connection::close()" << std::endl; */return true; }
+ virtual bool close(const bool wait_for_shutdown) { /*std::cout << "test_connection::close()" << std::endl; */return true; }
virtual bool send_done() { /*std::cout << "test_connection::send_done()" << std::endl; */return true; }
virtual bool call_run_once_service_io() { std::cout << "test_connection::call_run_once_service_io()" << std::endl; return true; }
virtual bool request_callback() { std::cout << "test_connection::request_callback()" << std::endl; return true; }
@@ -506,7 +506,6 @@ TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_process
}
std::string compare_buffer(1024 * 4, 'c');
- compare_buffer.resize(((1024 - sizeof(epee::levin::bucket_head2)) * 5) - sizeof(epee::levin::bucket_head2)); // add padding zeroes
ASSERT_EQ(4u, m_commands_handler.notify_counter());
ASSERT_EQ(0u, m_commands_handler.invoke_counter());
@@ -573,7 +572,7 @@ TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, does_not_handl
{
prepare_buf();
- ASSERT_TRUE(m_conn->m_protocol_handler.close());
+ ASSERT_TRUE(m_conn->m_protocol_handler.close(true));
ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size()));
}
@@ -652,3 +651,24 @@ TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_short_
ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size()));
}
+
+TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_bad_cb)
+{
+ m_req_head.m_cb = sizeof(epee::levin::bucket_head2);
+ m_req_head.m_flags = LEVIN_PACKET_BEGIN;
+ m_req_head.m_command = 0;
+
+ epee::levin::bucket_head2 inner{};
+ inner.m_cb = 2;
+ m_in_data.resize(sizeof(epee::levin::bucket_head2));
+ prepare_buf();
+
+ ASSERT_TRUE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size()));
+
+ m_req_head.m_cb = 1;
+ m_req_head.m_flags = LEVIN_PACKET_END;
+ m_in_data.resize(1);
+ prepare_buf();
+
+ ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size()));
+}
diff --git a/tests/unit_tests/http.cpp b/tests/unit_tests/http.cpp
index f8eb0127b..1746ee190 100644
--- a/tests/unit_tests/http.cpp
+++ b/tests/unit_tests/http.cpp
@@ -614,10 +614,8 @@ TEST(HTTP_Client_Auth, BadSyntax)
TEST(HTTP_Client_Auth, MD5)
{
- constexpr char method[] = "NOP";
constexpr char nonce[] = "some crazy nonce";
constexpr char realm[] = "the only realm";
- constexpr char uri[] = "/some_file";
const http::login user{"foo", "bar"};
http::http_client_auth auth{user};
@@ -636,42 +634,11 @@ TEST(HTTP_Client_Auth, MD5)
},
});
- EXPECT_EQ(http::http_client_auth::kSuccess, auth.handle_401(response));
- const auto auth_field = auth.get_auth_field(method, uri);
- ASSERT_TRUE(bool(auth_field));
-
- const auto parsed = parse_fields(auth_field->second);
- EXPECT_STREQ(u8"Authorization", auth_field->first.c_str());
- EXPECT_EQ(parsed.end(), parsed.find(u8"opaque"));
- EXPECT_EQ(parsed.end(), parsed.find(u8"qop"));
- EXPECT_EQ(parsed.end(), parsed.find(u8"nc"));
- EXPECT_STREQ(u8"MD5", parsed.at(u8"algorithm").c_str());
- EXPECT_STREQ(nonce, parsed.at(u8"nonce").c_str());
- EXPECT_STREQ(uri, parsed.at(u8"uri").c_str());
- EXPECT_EQ(user.username, parsed.at(u8"username"));
- EXPECT_STREQ(realm, parsed.at(u8"realm").c_str());
-
- const std::string a1 = get_a1(user, parsed);
- const std::string a2 = get_a2(uri);
- const std::string auth_code = md5_hex(
- boost::join(std::vector<std::string>{md5_hex(a1), nonce, md5_hex(a2)}, u8":")
- );
- EXPECT_TRUE(boost::iequals(auth_code, parsed.at(u8"response")));
- {
- const auto auth_field_dup = auth.get_auth_field(method, uri);
- ASSERT_TRUE(bool(auth_field_dup));
- EXPECT_EQ(*auth_field, *auth_field_dup);
- }
-
-
- EXPECT_EQ(http::http_client_auth::kBadPassword, auth.handle_401(response));
- response.m_header_info.m_etc_fields.front().second.append(u8"," + write_fields({{u8"stale", u8"TRUE"}}));
- EXPECT_EQ(http::http_client_auth::kSuccess, auth.handle_401(response));
+ EXPECT_EQ(http::http_client_auth::kParseFailure, auth.handle_401(response));
}
TEST(HTTP_Client_Auth, MD5_auth)
{
- constexpr char cnonce[] = "";
constexpr char method[] = "NOP";
constexpr char nonce[] = "some crazy nonce";
constexpr char opaque[] = "this is the opaque";
@@ -723,7 +690,7 @@ TEST(HTTP_Client_Auth, MD5_auth)
const std::string a1 = get_a1(user, parsed);
const std::string a2 = get_a2(uri);
const std::string auth_code = md5_hex(
- boost::join(std::vector<std::string>{md5_hex(a1), nonce, nc, cnonce, u8"auth", md5_hex(a2)}, u8":")
+ boost::join(std::vector<std::string>{md5_hex(a1), nonce, nc, parsed.at(u8"cnonce"), u8"auth", md5_hex(a2)}, u8":")
);
EXPECT_TRUE(boost::iequals(auth_code, parsed.at(u8"response")));
}
diff --git a/tests/unit_tests/levin.cpp b/tests/unit_tests/levin.cpp
index 25b67907f..1a734181b 100644
--- a/tests/unit_tests/levin.cpp
+++ b/tests/unit_tests/levin.cpp
@@ -63,7 +63,7 @@ namespace
return true;
}
- virtual bool close() override final
+ virtual bool close(const bool wait_for_shutdown) override final
{
return true;
}
diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp
index 16ec7dd21..30e5f5589 100644
--- a/tests/unit_tests/net.cpp
+++ b/tests/unit_tests/net.cpp
@@ -40,6 +40,7 @@
#include <boost/endian/conversion.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <boost/range/combine.hpp>
+#include <boost/optional/optional.hpp>
#include <boost/system/error_code.hpp>
#include <boost/thread/scoped_thread.hpp>
#include <boost/thread/thread.hpp>
@@ -966,6 +967,239 @@ TEST(get_network_address_host_and_port, hostname)
na_host_and_port_test("xmrchain.net:18081", "xmrchain.net", "18081");
}
+TEST(scheme_and_authority, basic)
+{
+ const auto check = [] (const net::scheme_and_authority& actual, const boost::string_ref scheme, const boost::string_ref authority)
+ {
+ EXPECT_EQ(actual.scheme, scheme);
+ EXPECT_EQ(actual.authority, authority);
+ };
+
+ // valid (some ipv6 hostnames are non-standard but allowed)
+ check(net::scheme_and_authority{"socks://host:port/path"}, "socks", "host:port");
+ check(net::scheme_and_authority{"socks://[::ffff]:8080/path"}, "socks", "[::ffff]:8080");
+ check(net::scheme_and_authority{"socks://192.168.0.1/path"}, "socks", "192.168.0.1");
+ check(net::scheme_and_authority{"socks://host"}, "socks", "host");
+ check(net::scheme_and_authority{"socks://@host"}, "socks", "@host");
+ check(net::scheme_and_authority{"socks://user:pass@host"}, "socks", "user:pass@host");
+ check(net::scheme_and_authority{"host:port/path"}, "", "host:port");
+ check(net::scheme_and_authority{"[::ffff]:8080/path"}, "", "[::ffff]:8080");
+ check(net::scheme_and_authority{"192.168.0.1/path"}, "", "192.168.0.1");
+ check(net::scheme_and_authority{"host"}, "", "host");
+ check(net::scheme_and_authority{"192.168.0.1"}, "", "192.168.0.1");
+ check(net::scheme_and_authority{"192.168.0.1:80/path"}, "", "192.168.0.1:80");
+ check(net::scheme_and_authority{"::ffff"}, "", "::ffff");
+ check(net::scheme_and_authority{"[::ffff]:8080"}, "", "[::ffff]:8080");
+ check(net::scheme_and_authority{"example.com/some://valid/path"}, "", "example.com");
+
+ // unsupported URIs (URN cases)
+ check(net::scheme_and_authority{"urn:isbn:number"}, "", "urn:isbn:number");
+ check(net::scheme_and_authority{"urn:isbn/number"}, "", "urn:isbn");
+
+ // invalid cases _not_ strictly rejected until hostname is parsed fully
+ check(net::scheme_and_authority{""}, "", "");
+ check(net::scheme_and_authority{"socks://"}, "socks", "");
+ check(net::scheme_and_authority{"socks:/"}, "", "socks:");
+ check(net::scheme_and_authority{"192.168.0.1:80://"}, "", "192.168.0.1:80:");
+ check(net::scheme_and_authority{"user@::ffff:443"}, "", "user@::ffff:443");
+ check(net::scheme_and_authority{"socks://user:p%61ss@ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:8080"}, "socks", "user:p%61ss@ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:8080");
+}
+
+
+TEST(userinfo_and_hostport, basic)
+{
+ const auto check = [] (const net::userinfo_and_hostport& actual, const boost::string_ref userinfo, const boost::string_ref hostport)
+ {
+ EXPECT_EQ(actual.userinfo, userinfo);
+ EXPECT_EQ(actual.hostport, hostport);
+ };
+
+ check(net::userinfo_and_hostport{"@host:port"}, "", "host:port");
+ check(net::userinfo_and_hostport{"host:port"}, "", "host:port");
+ check(net::userinfo_and_hostport{"[::ffff]"}, "", "[::ffff]");
+ check(net::userinfo_and_hostport{"::ffff"}, "", "::ffff");
+ check(net::userinfo_and_hostport{"user@[::ffff]"}, "user", "[::ffff]");
+ check(net::userinfo_and_hostport{"user:%70ass@192.168.0.1"}, "user:%70ass", "192.168.0.1");
+ check(net::userinfo_and_hostport{"user:pass@host:8080"}, "user:pass", "host:8080");
+
+ // invalid hostname not strictly rejected
+ check(net::userinfo_and_hostport{""}, "", "");
+ check(net::userinfo_and_hostport{"@"}, "", "");
+ check(net::userinfo_and_hostport{":@"}, ":", "");
+ check(net::userinfo_and_hostport{"user@::ffff:443"}, "user", "::ffff:443");
+}
+
+TEST(user_and_pass, basic)
+{
+ const auto check = [] (
+ const boost::optional<net::user_and_pass>& actual,
+ const boost::optional<std::string>& user = boost::none,
+ const boost::string_ref pass = "")
+ {
+ ASSERT_EQ(bool(actual), bool(user));
+ if (actual)
+ {
+ EXPECT_EQ(actual->user, *user);
+ EXPECT_EQ(actual->pass, pass);
+ }
+ };
+
+ check(net::user_and_pass::get(""), std::string{""}, "");
+ check(net::user_and_pass::get("user"), std::string{"user"}, "");
+ check(net::user_and_pass::get("user:"), std::string{"user"}, "");
+ check(net::user_and_pass::get(":pass"), std::string{}, "pass");
+ check(net::user_and_pass::get("user:pass"), std::string("user"), "pass");
+ check(net::user_and_pass::get("user:p%3Ass"), std::string("user"), "p:ss");
+ check(net::user_and_pass::get("%2fser:"), std::string{"/ser"}, "");
+ check(net::user_and_pass::get("user:pas%21"), std::string{"user"}, "pas!");
+ check(net::user_and_pass::get("user::pass"), std::string{"user"}, ":pass");
+ check(net::user_and_pass::get("user%3A:pass"), std::string{"user:"}, "pass");
+ check(net::user_and_pass::get("%25%3A:pass"), std::string{"%:"}, "pass");
+ check(net::user_and_pass::get("user:%00%FF"), std::string{"user"}, boost::string_ref{"\x00\xFF", 2});
+
+ // invalid percent encodings
+ check(net::user_and_pass::get("user%3T"));
+ check(net::user_and_pass::get("user%T"));
+ check(net::user_and_pass::get("user%3"));
+ check(net::user_and_pass::get("user%"));
+}
+
+TEST(uri_components, get)
+{
+ const auto present = [] (const boost::string_ref value)
+ {
+ return boost::optional<std::string>{std::string{value}};
+ };
+ const auto check = [] (
+ const boost::optional<net::uri_components>& actual,
+ const boost::optional<std::string>& scheme = boost::none,
+ const boost::string_ref user = "",
+ const boost::string_ref pass = "",
+ const boost::string_ref hostport = "")
+ {
+ ASSERT_EQ(bool(actual), bool(scheme));
+ if (actual)
+ {
+ EXPECT_EQ(actual->scheme, *scheme);
+ EXPECT_EQ(actual->userinfo.user, user);
+ EXPECT_EQ(actual->userinfo.pass, pass);
+ EXPECT_EQ(actual->hostport, hostport);
+ }
+ };
+
+ // valid (some ipv6 hostnames are non-standard but allowed)
+ check(net::uri_components::get("socks://host:port/path"), present("socks"), "", "", "host:port");
+ check(net::uri_components::get("socks://[::ffff]:8080/path"), present("socks"), "", "", "[::ffff]:8080");
+ check(net::uri_components::get("socks://192.168.0.1/path"), present("socks"), "", "", "192.168.0.1");
+ check(net::uri_components::get("socks://host"), present("socks"), "", "", "host");
+ check(net::uri_components::get("socks://@host"), present("socks"), "", "", "host");
+ check(net::uri_components::get("socks://:@host"), present("socks"), "", "", "host");
+ check(net::uri_components::get("socks://user:@host"), present("socks"), "user", "", "host");
+ check(net::uri_components::get("socks://:pass@host"), present("socks"), "", "pass", "host");
+ check(net::uri_components::get("socks://user:pass@host"), present("socks"), "user", "pass", "host");
+ check(net::uri_components::get("host:port/path"), present(""), "", "", "host:port");
+ check(net::uri_components::get("[::ffff]:8080/path"), present(""), "", "", "[::ffff]:8080");
+ check(net::uri_components::get("192.168.0.1/path"), present(""), "", "", "192.168.0.1");
+ check(net::uri_components::get("host"), present(""), "", "", "host");
+ check(net::uri_components::get("192.168.0.1"), present(""), "", "", "192.168.0.1");
+ check(net::uri_components::get("192.168.0.1:80/path"), present(""), "", "", "192.168.0.1:80");
+ check(net::uri_components::get("::ffff"), present(""), "", "", "::ffff");
+ check(net::uri_components::get("[::ffff]:8080"), present(""), "", "", "[::ffff]:8080");
+ check(net::uri_components::get("example.com/some://valid/path"), present(""), "", "", "example.com");
+
+ // unsupported URIs (URN cases)
+ check(net::uri_components::get("urn:isbn:number"), present(""), "", "", "urn:isbn:number");
+ check(net::uri_components::get("urn:isbn/number"), present(""), "", "", "urn:isbn");
+
+ // invalid cases _not_ strictly rejected until hostname is parsed fully
+ check(net::uri_components::get(""), present(""), "", "", "");
+ check(net::uri_components::get("socks://"), present("socks"), "", "", "");
+ check(net::uri_components::get("socks:/"), present(""), "", "", "socks:");
+ check(net::uri_components::get("192.168.0.1:80://"), present(""), "", "", "192.168.0.1:80:");
+ check(net::uri_components::get("user@::ffff:443"), present(""), "user", "", "::ffff:443");
+ check(net::uri_components::get("socks://user:p%61ss@ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:8080"), present("socks"), "user", "pass", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:8080");
+
+ // invalid percent encodings
+ check(net::uri_components::get("scheme://user%3T@host"));
+ check(net::uri_components::get("user%T@host"));
+ check(net::uri_components::get("scheme://user:%3pass@host"));
+ check(net::uri_components::get("user%@host"));
+}
+
+TEST(socks_endpoint, get)
+{
+ namespace ip = boost::asio::ip;
+ const auto present = [] (const boost::asio::ip::address& value)
+ {
+ return boost::optional<boost::asio::ip::address>{value};
+ };
+ const auto check = [] (
+ const expect<net::socks::endpoint>& actual,
+ const boost::optional<boost::asio::ip::address>& address = boost::none,
+ const std::uint16_t port = 0,
+ const boost::string_ref user = "",
+ const boost::string_ref pass = "",
+ const net::socks::version ver = net::socks::version::v4a)
+ {
+ ASSERT_EQ(bool(actual), bool(address)) << actual.error().message();
+ if (actual)
+ {
+ ASSERT_TRUE(bool(address));
+ EXPECT_EQ(actual->address.address(), *address);
+ EXPECT_EQ(actual->address.port(), port);
+ EXPECT_EQ(actual->userinfo.user, user);
+ EXPECT_EQ(actual->userinfo.pass, pass);
+ EXPECT_EQ(actual->ver, ver);
+ }
+ };
+
+ check(net::socks::endpoint::get("socks://[::ffff]:8080/path"), present(ip::make_address_v6("::ffff")), 8080, "", "", net::socks::version::v4a);
+ check(net::socks::endpoint::get("socks5://user:%70ass@[::ffff]:8080/path"), present(ip::make_address_v6("::ffff")), 8080, "user", "pass", net::socks::version::v5);
+ check(net::socks::endpoint::get("socks4a://192.168.0.1:1/path"), present(ip::make_address_v4("192.168.0.1")), 1, "", "", net::socks::version::v4a);
+ check(net::socks::endpoint::get("socks5://%75@192.168.0.1:1/path"), present(ip::make_address_v4("192.168.0.1")), 1, "u", "", net::socks::version::v5);
+ check(net::socks::endpoint::get("[::ffff]:8080/path"), present(ip::make_address_v6("::ffff")), 8080, "", "", net::socks::version::v4a);
+ check(net::socks::endpoint::get("192.168.0.1:50"), present(ip::make_address_v4("192.168.0.1")), 50, "", "", net::socks::version::v4a);
+ check(net::socks::endpoint::get("192.168.0.1:80/path"), present(ip::make_address_v4("192.168.0.1")), 80, "", "", net::socks::version::v4a);
+
+ // URNs should be rejected
+ check(net::socks::endpoint::get("urn:isbn:number"));
+ check(net::socks::endpoint::get("urn:isbn/number"));
+
+ // port required for socks
+ check(net::socks::endpoint::get("socks5://192.168.0.1/path"));
+ check(net::socks::endpoint::get("192.168.0.1/path"));
+ check(net::socks::endpoint::get("::ffff"));
+
+ // invalid for socks - hostnames not allowed
+ check(net::socks::endpoint::get("socks://host:/path"));
+ check(net::socks::endpoint::get("socks://host:1"));
+ check(net::socks::endpoint::get("socks://@host:1"));
+ check(net::socks::endpoint::get("socks://:@host:1"));
+ check(net::socks::endpoint::get("socks://user:@host:1"));
+ check(net::socks::endpoint::get("socks://:pass@host:1"));
+ check(net::socks::endpoint::get("socks://user:pass@host:1"));
+ check(net::socks::endpoint::get("host:1"));
+ check(net::socks::endpoint::get("host:1/path"));
+ check(net::socks::endpoint::get("example.com:1/some://valid/path"));
+
+ // invalid cases rejected - more bad hostnames
+ check(net::socks::endpoint::get(""));
+ check(net::socks::endpoint::get("socks://"));
+ check(net::socks::endpoint::get("socks:/"));
+ check(net::socks::endpoint::get("192.168.0.1:80://"));
+ check(net::socks::endpoint::get("::ffff:443"));
+ check(net::socks::endpoint::get("socks5://user:p%61ss@ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:8080"));
+
+ // invalid percent encodings
+ check(net::socks::endpoint::get("sock5://user%3T@127.0.0.1:1"));
+ check(net::socks::endpoint::get("socks5::user%T@127.0.0.1:1"));
+ check(net::socks::endpoint::get("socks5://user:%3pass@127.0.0.1:1"));
+ check(net::socks::endpoint::get("socks5://user%@127.0.0.1:1"));
+
+ // user+pass requires socks5
+ check(net::socks::endpoint::get("socks://user:pass@[::ffff]:8080"));
+}
+
namespace
{
using stream_type = boost::asio::ip::tcp;
@@ -1035,6 +1269,58 @@ TEST(socks_client, unsupported_command)
EXPECT_FALSE(test_client->set_resolve_command("example.com"));
EXPECT_TRUE(test_client->buffer().empty());
+
+ EXPECT_FALSE(test_client->set_connect_command(epee::net_utils::ipv6_network_address{}));
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ test_client = net::socks::make_connect_client(
+ stream_type::socket{io_service}, net::socks::version::v5, std::bind( [] {} )
+ );
+ ASSERT_TRUE(bool(test_client));
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ net::user_and_pass userinfo{};
+ userinfo.user = std::string(256, 'a');
+
+ EXPECT_FALSE(test_client->set_connect_command(userinfo.user, 8080));
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ EXPECT_FALSE(test_client->set_connect_command("a", 8080, std::addressof(userinfo)));
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ EXPECT_FALSE(
+ test_client->set_connect_command(
+ epee::net_utils::ipv4_network_address{}, std::addressof(userinfo)
+ )
+ );
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ EXPECT_FALSE(
+ test_client->set_connect_command(
+ epee::net_utils::ipv6_network_address{}, std::addressof(userinfo)
+ )
+ );
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ userinfo.pass = std::move(userinfo.user);
+ userinfo.user.clear();
+
+ EXPECT_FALSE(test_client->set_connect_command("a", 8080, std::addressof(userinfo)));
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ EXPECT_FALSE(
+ test_client->set_connect_command(
+ epee::net_utils::ipv4_network_address{}, std::addressof(userinfo)
+ )
+ );
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ EXPECT_FALSE(
+ test_client->set_connect_command(
+ epee::net_utils::ipv6_network_address{}, std::addressof(userinfo)
+ )
+ );
+ EXPECT_TRUE(test_client->buffer().empty());
}
TEST(socks_client, no_command)
@@ -1082,6 +1368,62 @@ TEST(socks_client, connect_command)
while (!called);
}
+TEST(socks_client, v5_ipv6_connect_command)
+{
+ io_thread io{};
+ stream_type::socket client{io.io_service};
+
+ const boost::asio::ip::address_v6::bytes_type address{0xDE, 0xAD, 0xBE, 0xEF};
+
+ std::atomic<bool> called{false};
+ auto test_client = net::socks::make_connect_client(
+ std::move(client), net::socks::version::v5, checked_client{std::addressof(called), false}
+ );
+ ASSERT_TRUE(bool(test_client));
+
+ ASSERT_TRUE(
+ test_client->set_connect_command(
+ epee::net_utils::ipv6_network_address{boost::asio::ip::address_v6{address}, 80}
+ )
+ );
+ EXPECT_FALSE(test_client->buffer().empty());
+ ASSERT_TRUE(net::socks::client::connect_and_send(std::move(test_client), io.acceptor.local_endpoint()));
+ while (!io.connected)
+ ASSERT_FALSE(called);
+
+ {
+ const std::uint8_t expected_bytes[] = {5, 1, 0};
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {5, 0};
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+ {
+ const std::uint8_t expected_bytes[] = {
+ 5, 1, 0, 4, 0xDE, 0xAD, 0xBE, 0xEF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0x50
+ };
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {
+ 5, 0, 0, 4, 0xBE, 0xEF, 0xDE, 0xAD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0x50
+ };
+
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+
+ // yikes!
+ while (!called);
+}
+
+
TEST(socks_client, connect_command_failed)
{
io_thread io{};
@@ -1118,6 +1460,56 @@ TEST(socks_client, connect_command_failed)
while (!called);
}
+
+TEST(socks_client, v5_ipv4_connect_command_failed)
+{
+ io_thread io{};
+ stream_type::socket client{io.io_service};
+
+ std::atomic<bool> called{false};
+ auto test_client = net::socks::make_connect_client(
+ std::move(client), net::socks::version::v5, checked_client{std::addressof(called), true}
+ );
+ ASSERT_TRUE(bool(test_client));
+
+ ASSERT_TRUE(
+ test_client->set_connect_command(
+ epee::net_utils::ipv4_network_address{boost::endian::native_to_big(std::uint32_t(5000)), 80}
+ )
+ );
+ EXPECT_FALSE(test_client->buffer().empty());
+ ASSERT_TRUE(net::socks::client::connect_and_send(std::move(test_client), io.acceptor.local_endpoint()));
+ while (!io.connected)
+ ASSERT_FALSE(called);
+
+ {
+ const std::uint8_t expected_bytes[] = {5, 1, 0};
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {5, 0};
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+ {
+ const std::uint8_t expected_bytes[] = {
+ 5, 1, 0, 1, 0, 0, 0x13, 0x88, 0, 0x50
+ };
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {5, 2, 0, 1, 0, 0, 0, 0, 0, 0x50};
+
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+
+ // yikes!
+ while (!called);
+}
+
TEST(socks_client, resolve_command)
{
static std::uint8_t reply_bytes[] = {0, 90, 0, 0, 0xff, 0, 0xad, 0};
@@ -1133,7 +1525,7 @@ TEST(socks_client, resolve_command)
, expected_(false)
{};
- virtual void done(boost::system::error_code error, std::shared_ptr<client> self) override
+ virtual void done(boost::system::error_code error, const std::shared_ptr<client>& self) override
{
EXPECT_EQ(this, self.get());
EXPECT_EQ(expected_, bool(error)) << "Resolve failure: " << error.message();
@@ -1189,6 +1581,120 @@ TEST(socks_client, resolve_command)
while (test_client->called_ == 1);
}
+TEST(socks_client, v5_username_host_connect)
+{
+ io_thread io{};
+ stream_type::socket client{io.io_service};
+
+ std::atomic<bool> called{false};
+ auto test_client = net::socks::make_connect_client(
+ std::move(client), net::socks::version::v5, checked_client{std::addressof(called), false}
+ );
+ ASSERT_TRUE(bool(test_client));
+
+ const auto userinfo =
+ net::user_and_pass::get("user:pass").value_or(net::user_and_pass{});
+ ASSERT_TRUE(
+ test_client->set_connect_command("example.com", 80, std::addressof(userinfo))
+ );
+ EXPECT_FALSE(test_client->buffer().empty());
+ ASSERT_TRUE(net::socks::client::connect_and_send(std::move(test_client), io.acceptor.local_endpoint()));
+ while (!io.connected)
+ ASSERT_FALSE(called);
+
+ {
+ const std::uint8_t expected_bytes[] = {5, 2, 0, 2};
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {5, 2};
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+ {
+ const std::uint8_t expected_bytes[] = {
+ 1, 4, 'u', 's', 'e', 'r', 4, 'p', 'a', 's', 's'
+ };
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {1, 0};
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+ {
+ const std::uint8_t expected_bytes[] = {
+ 5, 1, 0, 3, 11, 'e','x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o',
+ 'm', 0, 0x50
+ };
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {
+ 5, 0, 0, 1, 0xDE, 0xAD, 0xBE, 0xEF, 0x50, 00
+ };
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+
+ // yikes!
+ while (!called);
+}
+
+TEST(socks_client, v5_usernameskipped_host_connect)
+{
+ io_thread io{};
+ stream_type::socket client{io.io_service};
+
+ std::atomic<bool> called{false};
+ auto test_client = net::socks::make_connect_client(
+ std::move(client), net::socks::version::v5, checked_client{std::addressof(called), false}
+ );
+ ASSERT_TRUE(bool(test_client));
+
+ const auto userinfo =
+ net::user_and_pass::get("user:pass").value_or(net::user_and_pass{});
+ ASSERT_TRUE(
+ test_client->set_connect_command("example.com", 80, std::addressof(userinfo))
+ );
+ EXPECT_FALSE(test_client->buffer().empty());
+ ASSERT_TRUE(net::socks::client::connect_and_send(std::move(test_client), io.acceptor.local_endpoint()));
+ while (!io.connected)
+ ASSERT_FALSE(called);
+
+ {
+ const std::uint8_t expected_bytes[] = {5, 2, 0, 2};
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {5, 0};
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+ {
+ const std::uint8_t expected_bytes[] = {
+ 5, 1, 0, 3, 11, 'e','x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o',
+ 'm', 0, 0x50
+ };
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {
+ 5, 0, 0, 1, 0xDE, 0xAD, 0xBE, 0xEF, 0x50, 00
+ };
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+
+ // yikes!
+ while (!called);
+}
+
TEST(socks_connector, host)
{
io_thread io{};
@@ -1196,7 +1702,9 @@ TEST(socks_connector, host)
timeout.expires_after(std::chrono::seconds{5});
boost::unique_future<boost::asio::ip::tcp::socket> sock =
- net::socks::connector{io.acceptor.local_endpoint()}("example.com", "8080", timeout);
+ net::socks::connector{
+ std::make_shared<net::socks::endpoint>(io.acceptor.local_endpoint())
+ }("example.com", "8080", timeout);
while (!io.connected)
ASSERT_FALSE(sock.is_ready());
@@ -1223,7 +1731,9 @@ TEST(socks_connector, ipv4)
timeout.expires_after(std::chrono::seconds{5});
boost::unique_future<boost::asio::ip::tcp::socket> sock =
- net::socks::connector{io.acceptor.local_endpoint()}("250.88.125.99", "8080", timeout);
+ net::socks::connector{
+ std::make_shared<net::socks::endpoint>(io.acceptor.local_endpoint())
+ }("250.88.125.99", "8080", timeout);
while (!io.connected)
ASSERT_FALSE(sock.is_ready());
@@ -1249,7 +1759,9 @@ TEST(socks_connector, error)
timeout.expires_after(std::chrono::seconds{5});
boost::unique_future<boost::asio::ip::tcp::socket> sock =
- net::socks::connector{io.acceptor.local_endpoint()}("250.88.125.99", "8080", timeout);
+ net::socks::connector{
+ std::make_shared<net::socks::endpoint>(io.acceptor.local_endpoint())
+ }("250.88.125.99", "8080", timeout);
while (!io.connected)
ASSERT_FALSE(sock.is_ready());
@@ -1275,7 +1787,9 @@ TEST(socks_connector, timeout)
timeout.expires_after(std::chrono::milliseconds{10});
boost::unique_future<boost::asio::ip::tcp::socket> sock =
- net::socks::connector{io.acceptor.local_endpoint()}("250.88.125.99", "8080", timeout);
+ net::socks::connector{
+ std::make_shared<net::socks::endpoint>(io.acceptor.local_endpoint())
+ }("250.88.125.99", "8080", timeout);
ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3}));
EXPECT_THROW(sock.get().is_open(), boost::system::system_error);
@@ -1842,4 +2356,3 @@ TEST(zmq, read_write_termination)
ASSERT_FALSE(bool(received));
EXPECT_EQ(net::zmq::make_error_code(ETERM), received.error());
}
-
diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp
index e55b43911..525bdc4c9 100644
--- a/tests/unit_tests/node_server.cpp
+++ b/tests/unit_tests/node_server.cpp
@@ -42,6 +42,21 @@
#define MAKE_IPV4_ADDRESS_PORT(a,b,c,d,e) epee::net_utils::ipv4_network_address{MAKE_IP(a,b,c,d),e}
#define MAKE_IPV4_SUBNET(a,b,c,d,e) epee::net_utils::ipv4_network_subnet{MAKE_IP(a,b,c,d),e}
+namespace
+{
+ boost::filesystem::path create_temp_dir()
+ {
+ boost::system::error_code ec;
+ auto path = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("daemon-%%%%%%%%%%%%%%%%", ec);
+ if (ec)
+ return boost::filesystem::path{};
+ auto success = boost::filesystem::create_directory(path, ec);
+ if (!ec && success)
+ return path;
+ return boost::filesystem::path{};
+ }
+}
+
namespace cryptonote {
class blockchain_storage;
}
@@ -132,6 +147,68 @@ static bool is_blocked(Server &server, const epee::net_utils::network_address &a
return false;
}
+namespace
+{
+ using path_t = boost::filesystem::path;
+ using ec_t = boost::system::error_code;
+
+ path_t create_temp_dir(const char* pattern)
+ {
+ ec_t ec;
+ path_t path = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(pattern, ec);
+ if (ec)
+ return path_t{};
+
+ const bool success = boost::filesystem::create_directory(path, ec);
+ if (!ec && success)
+ return path;
+
+ return path_t{};
+ }
+
+ void remove_tree(const path_t& path)
+ {
+ ec_t ec;
+ boost::filesystem::remove_all(path, ec);
+ }
+
+ boost::program_options::variables_map make_regtest_options(const path_t& dir)
+ {
+ boost::program_options::options_description desc;
+ cryptonote::core::init_options(desc);
+ Server::init_options(desc);
+
+ std::vector<std::string> args{
+ "--regtest",
+ "--p2p-bind-ip=127.0.0.1",
+ "--out-peers=0",
+ "--in-peers=0",
+ "--data-dir",
+ dir.string(),
+ "--check-updates=disabled",
+ "--disable-dns-checkpoints",
+ "--offline",
+ };
+
+ boost::program_options::variables_map vm;
+ boost::program_options::store(
+ boost::program_options::command_line_parser(args).options(desc).run(),
+ vm
+ );
+ boost::program_options::notify(vm);
+ return vm;
+ }
+
+ nodetool::peerlist_entry make_peer(const epee::net_utils::network_address& address, const nodetool::peerid_type id, const int64_t last_seen)
+ {
+ nodetool::peerlist_entry peer = AUTO_VAL_INIT(peer);
+ peer.adr = address;
+ peer.id = id;
+ peer.last_seen = last_seen;
+ return peer;
+ }
+}
+
TEST(ban, add)
{
test_core pr_core;
@@ -300,17 +377,7 @@ TEST(ban, file_banlist)
Server server(cprotocol);
cprotocol.set_p2p_endpoint(&server);
- auto create_node_dir = [](){
- boost::system::error_code ec;
- auto path = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("daemon-%%%%%%%%%%%%%%%%", ec);
- if (ec)
- return boost::filesystem::path{};
- auto success = boost::filesystem::create_directory(path, ec);
- if (!ec && success)
- return path;
- return boost::filesystem::path{};
- };
- const auto node_dir = create_node_dir();
+ const auto node_dir = create_temp_dir();
ASSERT_TRUE(!node_dir.empty());
auto auto_remove_node_dir = epee::misc_utils::create_scope_leave_handler([&node_dir](){
boost::filesystem::remove_all(node_dir);
@@ -615,7 +682,7 @@ TEST(cryptonote_protocol_handler, race_condition)
}
virtual bool drop_connection(const contexts::basic& context) override {
if (shared_state)
- return shared_state->close(context.m_connection_id);
+ return shared_state->close(context.m_connection_id, true);
else
return {};
}
@@ -697,16 +764,6 @@ TEST(cryptonote_protocol_handler, race_condition)
handshaked.wait();
};
using path_t = boost::filesystem::path;
- auto create_dir = []{
- ec_t ec;
- path_t path = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("daemon-%%%%%%%%%%%%%%%%", ec);
- if (ec)
- return path_t{};
- auto success = boost::filesystem::create_directory(path, ec);
- if (not ec && success)
- return path;
- return path_t{};
- };
auto remove_tree = [](const path_t &path){
ec_t ec;
boost::filesystem::remove_all(path, ec);
@@ -726,7 +783,7 @@ TEST(cryptonote_protocol_handler, race_condition)
};
using options_description_t = boost::program_options::options_description;
- const auto dir = create_dir();
+ const auto dir = create_temp_dir();
ASSERT_TRUE(not dir.empty());
daemons_t daemon{
@@ -1215,21 +1272,11 @@ TEST(node_server, race_condition)
};
using path_t = boost::filesystem::path;
using ec_t = boost::system::error_code;
- auto create_dir = []{
- ec_t ec;
- path_t path = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("daemon-%%%%%%%%%%%%%%%%", ec);
- if (ec)
- return path_t{};
- auto success = boost::filesystem::create_directory(path, ec);
- if (not ec && success)
- return path;
- return path_t{};
- };
auto remove_tree = [](const path_t &path){
ec_t ec;
boost::filesystem::remove_all(path, ec);
};
- const auto dir = create_dir();
+ const auto dir = create_temp_dir();
ASSERT_TRUE(not dir.empty());
protocol_t protocol{};
node_server_t node_server(protocol);
@@ -1244,7 +1291,6 @@ TEST(node_server, race_condition)
"--out-peers=0",
"--data-dir",
dir.string(),
- "--no-igd",
"--add-exclusive-node=127.0.0.1:48080",
"--check-updates=disabled",
"--disable-dns-checkpoints",
@@ -1269,5 +1315,47 @@ TEST(node_server, race_condition)
remove_tree(dir);
}
+TEST(regtest, isolates_p2p_state_from_mainnet_data_dir)
+{
+ const path_t dir = create_temp_dir("regtest-%%%%%%%%%%%%%%%%");
+ ASSERT_TRUE(!dir.empty());
+ auto cleanup = epee::misc_utils::create_scope_leave_handler([&dir]{
+ remove_tree(dir);
+ });
+
+ nodetool::peerlist_types peers{};
+ peers.white.push_back(make_peer(MAKE_IPV4_ADDRESS_PORT(11, 22, 33, 44, 18080), 1, 100));
+ peers.gray.push_back(make_peer(MAKE_IPV4_ADDRESS_PORT(55, 66, 77, 88, 18080), 2, 200));
+
+ const path_t mainnet_state = dir / P2P_NET_DATA_FILENAME;
+ ASSERT_TRUE(nodetool::peerlist_storage{}.store(mainnet_state.string(), peers));
+
+ test_core pr_core;
+ cryptonote::t_cryptonote_protocol_handler<test_core> cprotocol(pr_core, NULL);
+ Server server(cprotocol);
+ cprotocol.set_p2p_endpoint(&server);
+
+ const auto vm = make_regtest_options(dir);
+ ASSERT_TRUE(server.init(vm));
+ ASSERT_EQ(0u, server.get_public_white_peers_count());
+ ASSERT_EQ(0u, server.get_public_gray_peers_count());
+ ASSERT_TRUE(server.deinit());
+
+ const path_t regtest_state = dir / "fake" / P2P_NET_DATA_FILENAME;
+ ASSERT_TRUE(boost::filesystem::exists(regtest_state));
+
+ auto base_storage = nodetool::peerlist_storage::open(mainnet_state.string());
+ ASSERT_TRUE(bool(base_storage));
+ nodetool::peerlist_types base_public = base_storage->take_zone(epee::net_utils::zone::public_);
+ EXPECT_EQ(1u, base_public.white.size());
+ EXPECT_EQ(1u, base_public.gray.size());
+
+ auto regtest_storage = nodetool::peerlist_storage::open(regtest_state.string());
+ ASSERT_TRUE(bool(regtest_storage));
+ nodetool::peerlist_types regtest_public = regtest_storage->take_zone(epee::net_utils::zone::public_);
+ EXPECT_TRUE(regtest_public.white.empty());
+ EXPECT_TRUE(regtest_public.gray.empty());
+}
+
namespace nodetool { template class node_server<cryptonote::t_cryptonote_protocol_handler<test_core>>; }
namespace cryptonote { template class t_cryptonote_protocol_handler<test_core>; }
diff --git a/tests/unit_tests/zmq_rpc.cpp b/tests/unit_tests/zmq_rpc.cpp
index c98f0011b..38b5f083f 100644
--- a/tests/unit_tests/zmq_rpc.cpp
+++ b/tests/unit_tests/zmq_rpc.cpp
@@ -39,6 +39,7 @@
#include "net/zmq.h"
#include "rpc/message.h"
#include "rpc/zmq_pub.h"
+#include "rpc/zmq_restricted_methods.h"
#include "rpc/zmq_server.h"
#include "serialization/json_object.h"
@@ -69,6 +70,23 @@ TEST(ZmqFullMessage, Request)
EXPECT_STREQ("foo", parsed.getRequestType().c_str());
}
+TEST(ZmqRestrictedMethods, BasicCoverage)
+{
+ EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("flush_txpool"));
+ EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("get_peer_list"));
+ EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("mining_status"));
+ EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("relay_tx"));
+ EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("save_bc"));
+ EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("set_log_categories"));
+ EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("set_log_level"));
+ EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("start_mining"));
+ EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("stop_mining"));
+
+ EXPECT_FALSE(cryptonote::rpc::is_blocked_in_restricted_mode("get_height"));
+ EXPECT_FALSE(cryptonote::rpc::is_blocked_in_restricted_mode("get_info"));
+ EXPECT_FALSE(cryptonote::rpc::is_blocked_in_restricted_mode("send_raw_tx"));
+}
+
namespace
{
using published_json = std::pair<std::string, rapidjson::Document>;