diff --git a/docs/COMMAND_LINE.MD b/docs/COMMAND_LINE.MD index fd4b07b..a8bf72d 100644 --- a/docs/COMMAND_LINE.MD +++ b/docs/COMMAND_LINE.MD @@ -29,6 +29,7 @@ --no-autodiff Disable automatic difficulty adjustment for miners connected to stratum (WARNING: incompatible with Nicehash and MRR) --rpc-login Specify username[:password] required for Monero RPC server --socks5 Specify IP:port of a SOCKS5 proxy to use for outgoing connections +--socks5-proxy-type The type of SOCKS5 proxy. Can be one of the following values: auto, plain, tor. Default is auto (auto-detect by the port number) --no-dns Disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too) --p2p-external-port Port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections --no-upnp Disable UPnP port forwarding diff --git a/src/console_commands.cpp b/src/console_commands.cpp index aed65bd..00c12b6 100644 --- a/src/console_commands.cpp +++ b/src/console_commands.cpp @@ -35,7 +35,7 @@ static constexpr int DEFAULT_BACKLOG = 1; namespace p2pool { ConsoleCommands::ConsoleCommands(p2pool* pool) - : TCPServer(DEFAULT_BACKLOG, ConsoleClient::allocate, std::string()) + : TCPServer(DEFAULT_BACKLOG, ConsoleClient::allocate, std::string(), Params::ProxyType::INVALID) , m_pool(pool) , m_tty{} , m_stdin_pipe{} diff --git a/src/main.cpp b/src/main.cpp index 817e2eb..2e1f6e5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -89,6 +89,7 @@ void p2pool_usage() "--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum (WARNING: incompatible with Nicehash and MRR)\n" "--rpc-login Specify username[:password] required for Monero RPC server\n" "--socks5 Specify IP:port of a SOCKS5 proxy to use for outgoing connections\n" + "--socks5-proxy-type The type of SOCKS5 proxy. Can be one of the following values: auto, plain, tor. Default is auto (auto-detect by the port number)\n" "--no-dns Disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too)\n" "--p2p-external-port Port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections\n" #ifdef WITH_UPNP diff --git a/src/merge_mining_client_tari.cpp b/src/merge_mining_client_tari.cpp index 0d948e4..0236f2b 100644 --- a/src/merge_mining_client_tari.cpp +++ b/src/merge_mining_client_tari.cpp @@ -42,7 +42,7 @@ MergeMiningClientTari::MergeMiningClientTari(p2pool* pool, std::string host, con , m_auxWallet(wallet) , m_pool(pool) , m_tariJobParams{} - , m_server(new TariServer(pool->params().m_socks5Proxy)) + , m_server(new TariServer(pool->params().m_socks5Proxy, pool->params().m_socks5ProxyType)) , m_hostStr(host) , m_workerStop(0) , m_pushBlockStop(0) @@ -863,8 +863,8 @@ void MergeMiningClientTari::run() // TariServer and TariClient are simply a proxy from a localhost TCP port to the external Tari node // This is needed for SOCKS5 proxy support (gRPC library doesn't support it natively) -MergeMiningClientTari::TariServer::TariServer(const std::string& socks5Proxy) - : TCPServer(1, MergeMiningClientTari::TariClient::allocate, socks5Proxy) +MergeMiningClientTari::TariServer::TariServer(const std::string& socks5Proxy, Params::ProxyType socks5ProxyType) + : TCPServer(1, MergeMiningClientTari::TariClient::allocate, socks5Proxy, socks5ProxyType) , m_TariNodeIsV6(false) , m_TariNodeHost() , m_TariNodePort(0) diff --git a/src/merge_mining_client_tari.h b/src/merge_mining_client_tari.h index 247101b..8059b74 100644 --- a/src/merge_mining_client_tari.h +++ b/src/merge_mining_client_tari.h @@ -83,7 +83,7 @@ private: struct TariServer : public TCPServer { - explicit TariServer(const std::string& socks5Proxy); + TariServer(const std::string& socks5Proxy, Params::ProxyType socks5ProxyType); ~TariServer() {} [[nodiscard]] bool start(); diff --git a/src/p2p_server.cpp b/src/p2p_server.cpp index d96a431..1f1934c 100644 --- a/src/p2p_server.cpp +++ b/src/p2p_server.cpp @@ -64,7 +64,7 @@ static constexpr hash seed_onion_nodes[] = { }; P2PServer::P2PServer(p2pool* pool) - : TCPServer(DEFAULT_BACKLOG, P2PClient::allocate, pool->params().m_socks5Proxy) + : TCPServer(DEFAULT_BACKLOG, P2PClient::allocate, pool->params().m_socks5Proxy, pool->params().m_socks5ProxyType) , m_pool(pool) , m_cache(pool->params().m_blockCache ? new BlockCache(pool->params()) : nullptr) , m_cacheLoaded(false) @@ -426,14 +426,16 @@ void P2PServer::update_peer_connections() uint32_t num_outgoing = m_numConnections - m_numIncomingConnections; - // Try to have at least N/2 outgoing onion connections - if (!m_socks5Proxy.empty()) { + // Try to have at least N/2 (or N if clearnet P2P is disabled) outgoing onion connections + if (!m_socks5Proxy.empty() && (m_socks5ProxyType == Params::ProxyType::TOR)) { std::vector pubkeys = m_pool->side_chain().seen_onion_pubkeys(); // Add seed nodes pubkeys.insert(pubkeys.end(), seed_onion_nodes, seed_onion_nodes + array_size(seed_onion_nodes)); - for (uint32_t i = m_numOnionConnections, n = N / 2; (i < n) && !pubkeys.empty();) { + const uint32_t n = m_pool->params().m_noClearnetP2P ? N : (N / 2); + + for (uint32_t i = m_numOnionConnections; (i < n) && !pubkeys.empty();) { const uint64_t k = get_random64() % pubkeys.size(); const std::string addr = to_onion_v3(pubkeys[k]); @@ -727,8 +729,21 @@ void P2PServer::load_peer_list() } // Finally load peers from p2pool_peers.txt and p2pool_onion_peers.txt - for (size_t i = 0, n = (m_socks5Proxy.empty() ? 1 : 2); i < n; ++i) { - const std::string path = m_pool->params().m_dataDir + (i ? saved_onion_peer_list_file_name : saved_peer_list_file_name); + std::vector paths; + paths.resize(Params::ProxyType::MAX); + + paths[Params::ProxyType::PLAIN] = saved_peer_list_file_name; + + if (!m_socks5Proxy.empty() && (m_socks5ProxyType == Params::ProxyType::TOR)) { + paths[Params::ProxyType::TOR] = saved_onion_peer_list_file_name; + } + + for (int i = Params::ProxyType::PLAIN; i < Params::ProxyType::MAX; ++i) { + const std::string& path = paths[i]; + + if (path.empty()) { + continue; + } std::ifstream f(path); @@ -740,7 +755,7 @@ void P2PServer::load_peer_list() std::getline(f, address); if (!address.empty()) { - if (i > 0) { + if (i == Params::ProxyType::TOR) { const hash h = from_onion_v3(address); if (!h.empty()) { pubkeys.emplace_back(h); @@ -756,7 +771,7 @@ void P2PServer::load_peer_list() } f.close(); - if (i > 0) { + if (i == Params::ProxyType::TOR) { s.add_onion_pubkeys(pubkeys); } } diff --git a/src/params.cpp b/src/params.cpp index 631a449..233ca21 100644 --- a/src/params.cpp +++ b/src/params.cpp @@ -238,6 +238,18 @@ Params::Params(const std::vector>& args) ok = true; } + if ((arg[0] == "socks5-proxy-type") && has1(arg)) { + const std::string s = tolower(arg[1]); + + m_socks5ProxyType = ProxyType::INVALID; + + if (s == "auto") m_socks5ProxyType = ProxyType::AUTO; + if (s == "plain") m_socks5ProxyType = ProxyType::PLAIN; + if (s == "tor") m_socks5ProxyType = ProxyType::TOR; + + ok = m_socks5ProxyType != ProxyType::INVALID; + } + if (arg[0] == "no-dns") { m_dns = false; disable_resolve_host = true; @@ -426,6 +438,29 @@ Params::Params(const std::vector>& args) else { fixup_path(m_dataDir); } + + // Detect the proxy type + if (!m_socks5Proxy.empty() && (m_socks5ProxyType == ProxyType::AUTO)) { + m_socks5ProxyType = ProxyType::INVALID; + + const size_t k = m_socks5Proxy.find_last_of(':'); + + if ((k != std::string::npos) && (k < m_socks5Proxy.length() - 1)) { + const uint32_t port = std::stoul(m_socks5Proxy.substr(k + 1), nullptr, 10); + + if ((port > 0) && (port < 65536)) { + switch (port) { + case 9050: + m_socks5ProxyType = ProxyType::TOR; + break; + + default: + m_socks5ProxyType = ProxyType::PLAIN; + break; + } + } + } + } } bool Params::valid() const @@ -478,6 +513,11 @@ bool Params::valid() const return false; } + if (m_socks5ProxyType == ProxyType::INVALID) { + LOGERR(1, "Invalid SOCKS5 proxy type. Check the --socks5 parameter."); + return false; + } + return true; } diff --git a/src/params.h b/src/params.h index a330b46..a50d09f 100644 --- a/src/params.h +++ b/src/params.h @@ -108,7 +108,17 @@ struct Params bool m_mini = false; bool m_nano = false; bool m_autoDiff = true; + std::string m_socks5Proxy; + + enum ProxyType { + INVALID = -1, + AUTO = 0, + PLAIN = 1, + TOR = 2, + MAX = 3, + } m_socks5ProxyType = ProxyType::AUTO; + bool m_dns = true; int32_t m_p2pExternalPort = 0; #ifdef WITH_UPNP diff --git a/src/stratum_server.cpp b/src/stratum_server.cpp index 4811f42..2400ab9 100644 --- a/src/stratum_server.cpp +++ b/src/stratum_server.cpp @@ -44,7 +44,7 @@ static constexpr int32_t BAN_THRESHOLD_POINTS = -15; namespace p2pool { StratumServer::StratumServer(p2pool* pool) - : TCPServer(DEFAULT_BACKLOG, StratumClient::allocate, std::string()) + : TCPServer(DEFAULT_BACKLOG, StratumClient::allocate, std::string(), Params::ProxyType::INVALID) , m_pool(pool) , m_autoDiff(pool->params().m_autoDiff) , m_enableFullValidation(pool->params().m_enableFullValidation) diff --git a/src/tcp_server.cpp b/src/tcp_server.cpp index fac43a7..263c5c3 100644 --- a/src/tcp_server.cpp +++ b/src/tcp_server.cpp @@ -23,7 +23,7 @@ static thread_local const char* log_category_prefix = "TCPServer "; namespace p2pool { -TCPServer::TCPServer(int default_backlog, allocate_client_callback allocate_new_client, const std::string& socks5Proxy) +TCPServer::TCPServer(int default_backlog, allocate_client_callback allocate_new_client, const std::string& socks5Proxy, Params::ProxyType socks5ProxyType) : m_allocateNewClient(allocate_new_client) , m_defaultBacklog(default_backlog) , m_loopThread{} @@ -32,6 +32,7 @@ TCPServer::TCPServer(int default_backlog, allocate_client_callback allocate_new_ , m_portMapping(0) #endif , m_socks5Proxy(socks5Proxy) + , m_socks5ProxyType(socks5ProxyType) , m_socks5ProxyV6(false) , m_socks5ProxyIP{} , m_socks5ProxyPort(-1) diff --git a/src/tcp_server.h b/src/tcp_server.h index e4f069e..b5ac073 100644 --- a/src/tcp_server.h +++ b/src/tcp_server.h @@ -18,6 +18,7 @@ #pragma once #include "uv_util.h" +#include "params.h" #ifdef WITH_TLS #include "tls.h" @@ -33,7 +34,7 @@ public: struct Client; typedef Client* (*allocate_client_callback)(); - TCPServer(int default_backlog, allocate_client_callback allocate_new_client, const std::string& socks5Proxy); + TCPServer(int default_backlog, allocate_client_callback allocate_new_client, const std::string& socks5Proxy, Params::ProxyType socks5ProxyType); virtual ~TCPServer(); [[nodiscard]] bool connect_to_peer(bool is_v6, const char* ip, int port); @@ -191,6 +192,7 @@ protected: #endif std::string m_socks5Proxy; + Params::ProxyType m_socks5ProxyType; bool m_socks5ProxyV6; raw_ip m_socks5ProxyIP; int m_socks5ProxyPort; diff --git a/src/util.cpp b/src/util.cpp index 613cf39..bae90b9 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1014,12 +1014,8 @@ hash from_onion_v3(const std::string& address) return {}; } - // Convert address to lowercase - std::string s = address; - std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); - // Checksum validation - if (to_onion_v3(result) != s) { + if (to_onion_v3(result) != tolower(address)) { LOGWARN(3, "Invalid onion address \"" << address << "\": checksum failed"); return {}; } diff --git a/src/util.h b/src/util.h index 2df9b27..f3307fd 100644 --- a/src/util.h +++ b/src/util.h @@ -395,6 +395,18 @@ FORCEINLINE void secure_zero_memory(T& value) secure_zero_memory(&value, sizeof(T)); } +FORCEINLINE std::string tolower(const std::string& s) +{ + std::string result; + result.reserve(s.size() + 1); + + for (char c : s) { + result.push_back(static_cast(std::tolower(c))); + } + + return result; +} + std::string to_onion_v3(const hash& pubkey); hash from_onion_v3(const std::string& address);