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
+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 {