Support .onion domains for addpeers command and --addpeers option (requires SOCKS5 proxy)

This commit is contained in:
SChernykh
2025-10-18 15:33:20 +02:00
parent e2f0ec7c69
commit 4e690feefb
5 changed files with 120 additions and 43 deletions
+1 -1
View File
@@ -911,7 +911,7 @@ bool MergeMiningClientTari::TariServer::connect_upstream(TariClient* downstream)
upstream->m_owner = this;
upstream->m_port = port;
upstream->m_isV6 = is_v6;
upstream->m_addressType = is_v6 ? Client::AddressType::IPv6 : Client::AddressType::IPv4;
if (!str_to_ip(is_v6, ip.c_str(), upstream->m_addr)) {
return_client(upstream);
+21 -12
View File
@@ -274,7 +274,13 @@ void P2PServer::connect_to_peers(const std::string& peer_list)
parse_address_list(peer_list,
[this](bool is_v6, const std::string& /*address*/, std::string ip, int port)
{
if (!m_pool->params().m_dns || resolve_host(ip, is_v6)) {
if (!m_socks5Proxy.empty() && (ip.find_first_not_of("0123456789.:") != std::string::npos)) {
// Assume it's a domain name and use the proxy to resolve it
if (!connect_to_peer(ip, port)) {
LOGERR(5, "connect_to_peers: failed to connect to " << ip);
}
}
else if (!m_pool->params().m_dns || resolve_host(ip, is_v6)) {
if (!connect_to_peer(is_v6, ip.c_str(), port)) {
LOGERR(5, "connect_to_peers: failed to connect to " << ip);
}
@@ -315,11 +321,14 @@ void P2PServer::update_peer_connections()
connected_clients.reserve(m_numConnections);
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
const int timeout = client->m_handshakeComplete ? 300 : 10;
if ((cur_time >= client->m_lastAlive + timeout) && (client->m_socks5ProxyState == Client::Socks5ProxyState::Default)) {
const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive);
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting");
client->close();
continue;
if (cur_time >= client->m_lastAlive + timeout) {
const Client::Socks5ProxyState s = client->m_socks5ProxyState;
if ((s == Client::Socks5ProxyState::Default) || (s == Client::Socks5ProxyState::ConnectRequestSent)) {
const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive);
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting");
client->close();
continue;
}
}
if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) {
@@ -791,7 +800,7 @@ void P2PServer::remove_peer_from_list(const P2PClient* client)
for (auto it = m_peerList.begin(); it != m_peerList.end(); ++it) {
const Peer& p = *it;
if ((p.m_isV6 == client->m_isV6) && (p.m_port == client->m_listenPort) && (p.m_addr == client->m_addr)) {
if ((p.m_isV6 == client->isV6()) && (p.m_port == client->m_listenPort) && (p.m_addr == client->m_addr)) {
m_peerList.erase(it);
return;
}
@@ -2462,7 +2471,7 @@ bool P2PServer::P2PClient::on_listen_port(const uint8_t* buf)
m_listenPort = port;
static_cast<P2PServer*>(m_owner)->update_peer_in_list(m_isV6, m_addr, port);
static_cast<P2PServer*>(m_owner)->update_peer_in_list(isV6(), m_addr, port);
return true;
}
@@ -2675,7 +2684,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
continue;
}
const Peer p{ client->m_isV6, client->m_addr, client->m_listenPort, 0, 0 };
const Peer p{ client->isV6(), client->m_addr, client->m_listenPort, 0, 0 };
++n;
// Use https://en.wikipedia.org/wiki/Reservoir_sampling algorithm
@@ -3146,7 +3155,7 @@ bool P2PServer::P2PClient::on_monero_block_broadcast(const uint8_t* buf, uint32_
bool pow_check_passed;
};
Work* work = new Work{ {}, server, this, m_resetCounter, m_isV6, m_addr, pool->hasher(), std::move(blob), height, seed, diff, { buf0, buf0 + size0 }, false };
Work* work = new Work{ {}, server, this, m_resetCounter, isV6(), m_addr, pool->hasher(), std::move(blob), height, seed, diff, { buf0, buf0 + size0 }, false };
work->req.data = work;
const int err = uv_queue_work(&server->m_loop, &work->req,
@@ -3301,7 +3310,7 @@ bool P2PServer::P2PClient::handle_incoming_block_async(const PoolBlock* block, u
bool result;
};
Work* work = new Work{ {}, *block, this, server, m_resetCounter.load(), m_isV6, m_addr, {}, true };
Work* work = new Work{ {}, *block, this, server, m_resetCounter.load(), isV6(), m_addr, {}, true };
work->req.data = work;
const int err = uv_queue_work(&server->m_loop, &work->req,
@@ -3369,7 +3378,7 @@ void P2PServer::P2PClient::post_handle_incoming_block(p2pool* pool, const PoolBl
P2PClient* c = server->m_fastestPeer;
if (c && (c != this) && (c->m_broadcastMaxHeight >= block.m_sidechainHeight)) {
LOGINFO(5, "peer " << static_cast<char*>(c->m_addrString) << " is faster, sending BLOCK_REQUEST to it instead");
c->post_handle_incoming_block(pool, block, c->m_resetCounter.load(), c->m_isV6, c->m_addr, missing_blocks, true);
c->post_handle_incoming_block(pool, block, c->m_resetCounter.load(), c->isV6(), c->m_addr, missing_blocks, true);
return;
}
}
+1 -1
View File
@@ -448,7 +448,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
share.m_server = this;
share.m_client = client;
share.m_clientIPv6 = client->m_isV6;
share.m_clientIPv6 = client->isV6();
share.m_clientAddr = client->m_addr;
memcpy(share.m_clientAddrString, client->m_addrString, sizeof(share.m_clientAddrString));
memcpy(share.m_clientCustomUser, client->m_customUser, sizeof(share.m_clientCustomUser));
+87 -28
View File
@@ -282,7 +282,7 @@ bool TCPServer::connect_to_peer(bool is_v6, const char* ip, int port)
Client* client = get_client();
client->m_owner = this;
client->m_port = port;
client->m_isV6 = is_v6;
client->m_addressType = is_v6 ? Client::AddressType::IPv6 : Client::AddressType::IPv4;
if (!str_to_ip(is_v6, ip, client->m_addr)) {
return_client(client);
@@ -310,12 +310,40 @@ bool TCPServer::connect_to_peer(bool is_v6, const raw_ip& ip, int port)
client->m_owner = this;
client->m_addr = ip;
client->m_port = port;
client->m_isV6 = is_v6;
client->m_addressType = is_v6 ? Client::AddressType::IPv6 : Client::AddressType::IPv4;
client->init_addr_string();
return connect_to_peer(client);
}
bool TCPServer::connect_to_peer(const std::string& domain, int port)
{
if (m_socks5Proxy.empty()) {
LOGERR(1, "Can't connect to " << domain << ": SOCKS5 proxy is required");
return false;
}
if (m_finished.load()) {
return false;
}
Client* client = get_client();
client->m_owner = this;
client->m_addr = raw_ip::localhost_ipv4;
client->m_port = port;
client->m_addressType = Client::AddressType::DomainName;
log::Stream s(client->m_addrString);
s << domain << ':' << port << '\0';
if (s.m_spilled) {
LOGERR(1, "Can't connect to " << domain << ": too long domain name");
return false;
}
return connect_to_peer(client);
}
bool TCPServer::is_banned(bool is_v6, raw_ip ip)
{
if (ip.is_localhost()) {
@@ -345,13 +373,13 @@ bool TCPServer::is_banned(bool is_v6, raw_ip ip)
bool TCPServer::connect_to_peer(Client* client)
{
if (is_banned(client->m_isV6, client->m_addr)) {
if (is_banned(client->isV6(), client->m_addr)) {
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(client->m_addrString) << log::NoColor() << " is banned, not connecting to it");
return_client(client);
return false;
}
if (!m_pendingConnections.insert(client->m_addr).second) {
if ((client->m_addressType != Client::AddressType::DomainName) && (m_pendingConnections.find(client->m_addr) != m_pendingConnections.end())) {
LOGINFO(6, "there is already a pending connection to this IP, not connecting to " << log::Gray() << static_cast<char*>(client->m_addrString));
return_client(client);
return false;
@@ -385,18 +413,22 @@ bool TCPServer::connect_to_peer(Client* client)
sockaddr_storage addr{};
if (m_socks5Proxy.empty()) {
if (client->m_isV6) {
if (client->m_addressType == Client::AddressType::IPv6) {
sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(&addr);
addr6->sin6_family = AF_INET6;
memcpy(&addr6->sin6_addr, client->m_addr.data, sizeof(in6_addr));
addr6->sin6_port = htons(static_cast<uint16_t>(client->m_port));
}
else {
else if (client->m_addressType == Client::AddressType::IPv4) {
sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(&addr);
addr4->sin_family = AF_INET;
memcpy(&addr4->sin_addr, client->m_addr.data + sizeof(raw_ip::ipv4_prefix), sizeof(in_addr));
addr4->sin_port = htons(static_cast<uint16_t>(client->m_port));
}
else {
LOGWARN(5, "failed to initiate tcp connection to " << static_cast<const char*>(client->m_addrString) << ": domain name is unresolved and SOCKS5 proxy is not enabled");
return false;
}
}
else {
if (m_socks5ProxyV6) {
@@ -416,11 +448,14 @@ bool TCPServer::connect_to_peer(Client* client)
err = uv_tcp_connect(connect_request, &client->m_socket, reinterpret_cast<sockaddr*>(&addr), on_connect);
if (err) {
LOGWARN(5, "failed to initiate tcp connection to " << static_cast<const char*>(client->m_addrString) << ", error " << uv_err_name(err));
m_pendingConnections.erase(client->m_addr);
uv_close(reinterpret_cast<uv_handle_t*>(&client->m_socket), on_connection_error);
return false;
}
if (client->m_addressType != Client::AddressType::DomainName) {
m_pendingConnections.insert(client->m_addr);
}
LOGINFO(5, "connecting to " << log::Gray() << static_cast<const char*>(client->m_addrString));
return true;
}
@@ -755,7 +790,9 @@ void TCPServer::on_connect(uv_connect_t* req, int status)
return;
}
server->m_pendingConnections.erase(client->m_addr);
if (client->m_addressType != Client::AddressType::DomainName) {
server->m_pendingConnections.erase(client->m_addr);
}
if (status) {
if (status == UV_ETIMEDOUT) {
@@ -764,7 +801,7 @@ void TCPServer::on_connect(uv_connect_t* req, int status)
else {
LOGWARN(5, "failed to connect to " << static_cast<char*>(client->m_addrString) << ", error " << uv_err_name(status));
}
server->on_connect_failed(client->m_isV6, client->m_addr, client->m_port);
server->on_connect_failed(client->isV6(), client->m_addr, client->m_port);
client->on_connect_failed(status);
uv_close(reinterpret_cast<uv_handle_t*>(&client->m_socket), on_connection_error);
return;
@@ -823,7 +860,12 @@ void TCPServer::on_new_client(uv_stream_t* server, Client* client)
if (client->m_isIncoming) {
++m_numIncomingConnections;
client->m_isV6 = (std::find(m_listenSockets6.begin(), m_listenSockets6.end(), reinterpret_cast<uv_tcp_t*>(server)) != m_listenSockets6.end());
if (std::find(m_listenSockets6.begin(), m_listenSockets6.end(), reinterpret_cast<uv_tcp_t*>(server)) != m_listenSockets6.end()) {
client->m_addressType = Client::AddressType::IPv6;
}
else {
client->m_addressType = Client::AddressType::IPv4;
}
sockaddr_storage peer_addr;
int peer_addr_len = static_cast<int>(sizeof(peer_addr));
@@ -834,7 +876,7 @@ void TCPServer::on_new_client(uv_stream_t* server, Client* client)
return;
}
if (client->m_isV6) {
if (client->isV6()) {
memcpy(client->m_addr.data, &reinterpret_cast<sockaddr_in6*>(&peer_addr)->sin6_addr, sizeof(in6_addr));
client->m_port = ntohs(reinterpret_cast<sockaddr_in6*>(&peer_addr)->sin6_port);
}
@@ -849,7 +891,7 @@ void TCPServer::on_new_client(uv_stream_t* server, Client* client)
LOGINFO(5, "new connection " << (client->m_isIncoming ? "from " : "to ") << log::Gray() << static_cast<char*>(client->m_addrString));
if (is_banned(client->m_isV6, client->m_addr)) {
if (is_banned(client->isV6(), client->m_addr)) {
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(client->m_addrString) << log::NoColor() << " is banned, disconnecting");
client->close();
return;
@@ -1033,12 +1075,12 @@ TCPServer::Client::Client(char* read_buf, size_t size)
, m_prev(nullptr)
, m_next(nullptr)
, m_socket{}
, m_isV6(false)
, m_addressType(AddressType::IPv4)
, m_isIncoming(false)
, m_readBufInUse(false)
, m_isClosing(false)
, m_numRead(0)
, m_addr{}
, m_addr(raw_ip::localhost_ipv4)
, m_port(0)
, m_addrString{}
, m_socks5ProxyState(Socks5ProxyState::Default)
@@ -1059,12 +1101,12 @@ void TCPServer::Client::reset()
m_prev = nullptr;
m_next = nullptr;
memset(&m_socket, 0, sizeof(m_socket));
m_isV6 = false;
m_addressType = AddressType::IPv4;
m_isIncoming = false;
m_readBufInUse = false;
m_isClosing = false;
m_numRead = 0;
m_addr = {};
m_addr = raw_ip::localhost_ipv4;
m_port = -1;
m_addrString[0] = '\0';
m_socks5ProxyState = Socks5ProxyState::Default;
@@ -1157,27 +1199,44 @@ bool TCPServer::Client::on_proxy_handshake(const char* data, uint32_t size)
const bool result = m_owner->send(this,
[this](uint8_t* buf, size_t buf_size) -> size_t
{
if (buf_size < 22) {
if (buf_size < ADDR_STRING_SIZE + 7) {
return 0;
}
buf[0] = 5; // Protocol version (SOCKS5)
buf[1] = 1; // CONNECT
buf[2] = 0; // RESERVED
if (m_isV6) {
if (m_addressType == AddressType::DomainName) {
buf[3] = 3; // ATYP
const char* s = strchr(m_addrString, ':');
const size_t domain_len = s ? (s - m_addrString) : strlen(m_addrString);
buf[4] = static_cast<uint8_t>(domain_len);
memcpy(buf + 5, m_addrString, domain_len);
buf[domain_len + 5] = static_cast<uint8_t>(m_port >> 8);
buf[domain_len + 6] = static_cast<uint8_t>(m_port & 0xFF);
return domain_len + 7;
}
if (m_addressType == AddressType::IPv6) {
buf[3] = 4; // ATYP
memcpy(buf + 4, m_addr.data, 16);
buf[20] = static_cast<uint8_t>(m_port >> 8);
buf[21] = static_cast<uint8_t>(m_port & 0xFF);
}
else {
buf[3] = 1; // ATYP
memcpy(buf + 4, m_addr.data + sizeof(raw_ip::ipv4_prefix), 4);
buf[8] = static_cast<uint8_t>(m_port >> 8);
buf[9] = static_cast<uint8_t>(m_port & 0xFF);
return 22;
}
return m_isV6 ? 22 : 10;
buf[3] = 1; // ATYP
memcpy(buf + 4, m_addr.data + sizeof(raw_ip::ipv4_prefix), 4);
buf[8] = static_cast<uint8_t>(m_port >> 8);
buf[9] = static_cast<uint8_t>(m_port & 0xFF);
return 10;
});
if (result) {
@@ -1298,7 +1357,7 @@ void TCPServer::Client::ban(uint64_t seconds)
if (m_owner) {
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " banned for " << seconds << " seconds");
m_owner->ban(m_isV6, m_addr, seconds);
m_owner->ban(isV6(), m_addr, seconds);
}
}
@@ -1307,7 +1366,7 @@ void TCPServer::Client::init_addr_string()
const char* addr_str;
char addr_str_buf[64];
if (m_isV6) {
if (isV6()) {
addr_str = inet_ntop(AF_INET6, m_addr.data, addr_str_buf, sizeof(addr_str_buf));
}
else {
@@ -1321,7 +1380,7 @@ void TCPServer::Client::init_addr_string()
}
log::Stream s(m_addrString);
if (m_isV6) {
if (isV6()) {
s << '[' << log::const_buf(addr_str, n) << "]:" << m_port << '\0';
}
else {
+10 -1
View File
@@ -47,6 +47,7 @@ public:
[[nodiscard]] virtual int external_listen_port() const { return m_listenPort; }
[[nodiscard]] bool connect_to_peer(bool is_v6, const raw_ip& ip, int port);
[[nodiscard]] bool connect_to_peer(const std::string& domain, int port);
[[nodiscard]] bool connect_to_peer(Client* client);
virtual void on_connect_failed(bool /*is_v6*/, const raw_ip& /*ip*/, int /*port*/) {}
@@ -80,6 +81,8 @@ public:
void asan_poison_this() const;
FORCEINLINE bool isV6() const { return m_addressType == AddressType::IPv6; }
char* m_readBuf;
uint32_t m_readBufSize;
@@ -91,7 +94,13 @@ public:
uv_tcp_t m_socket;
bool m_isV6;
enum class AddressType {
IPv4,
IPv6,
DomainName
};
AddressType m_addressType;
bool m_isIncoming;
bool m_readBufInUse;
bool m_isClosing;