impement UDP associate in SOCKS proxy

This commit is contained in:
orignal 2025-10-26 18:39:53 -04:00
parent ab6dbe620c
commit 689a35c142
2 changed files with 66 additions and 19 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -20,6 +20,7 @@
#include "I2PService.h" #include "I2PService.h"
#include "util.h" #include "util.h"
#include "Socks5.h" #include "Socks5.h"
#include "UDPTunnel.h"
namespace i2p namespace i2p
{ {
@ -172,6 +173,7 @@ namespace proxy
const bool m_UseUpstreamProxy; // do we want to use the upstream proxy for non i2p addresses? const bool m_UseUpstreamProxy; // do we want to use the upstream proxy for non i2p addresses?
const std::string m_UpstreamProxyAddress; const std::string m_UpstreamProxyAddress;
const uint16_t m_UpstreamProxyPort; const uint16_t m_UpstreamProxyPort;
std::unique_ptr<i2p::client::I2PUDPClientTunnel> m_UDPTunnel;
public: public:
@ -229,6 +231,11 @@ namespace proxy
LogPrint(eLogDebug, "SOCKS: Closing stream"); LogPrint(eLogDebug, "SOCKS: Closing stream");
m_stream.reset (); m_stream.reset ();
} }
if (m_UDPTunnel)
{
m_UDPTunnel->Stop ();
m_UDPTunnel = nullptr;
}
Done(shared_from_this()); Done(shared_from_this());
} }
@ -343,10 +350,20 @@ namespace proxy
break; break;
case SOCKS5: case SOCKS5:
LogPrint(eLogInfo, "SOCKS: v5 connection success"); LogPrint(eLogInfo, "SOCKS: v5 connection success");
auto s = i2p::client::context.GetAddressBook().ToAddress(GetOwner()->GetLocalDestination()->GetIdentHash()); if (m_cmd == CMD_UDP && m_UDPTunnel)
address ad; ad.dns.FromString(s); {
// HACK only 16 bits passed in port as SOCKS5 doesn't allow for more address ad;
response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID()); // TODO: implement ipv6
ad.ip = m_UDPTunnel->GetLocalEndpoint ().address ().to_v4 ().to_uint ();
response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_IPV4, ad, m_UDPTunnel->GetLocalEndpoint ().port ());
}
else
{
auto s = i2p::client::context.GetAddressBook().ToAddress(GetOwner()->GetLocalDestination()->GetIdentHash());
address ad; ad.dns.FromString(s);
// HACK only 16 bits passed in port as SOCKS5 doesn't allow for more
response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream ? m_stream->GetRecvStreamID() : 0);
}
break; break;
} }
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, shared_from_this(), std::placeholders::_1)); boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, shared_from_this(), std::placeholders::_1));
@ -369,7 +386,7 @@ namespace proxy
bool SOCKSHandler::ValidateSOCKSRequest() bool SOCKSHandler::ValidateSOCKSRequest()
{ {
if ( m_cmd != CMD_CONNECT ) if ( m_cmd != CMD_CONNECT && m_cmd != CMD_UDP)
{ {
// TODO: we need to support binds and other shit! // TODO: we need to support binds and other shit!
LogPrint(eLogError, "SOCKS: Unsupported command: ", m_cmd); LogPrint(eLogError, "SOCKS: Unsupported command: ", m_cmd);
@ -619,17 +636,39 @@ namespace proxy
LogPrint(eLogInfo, "SOCKS: Requested ", addr, ":" , m_port); LogPrint(eLogInfo, "SOCKS: Requested ", addr, ":" , m_port);
const size_t addrlen = addr.size(); const size_t addrlen = addr.size();
// does it end with .i2p? // does it end with .i2p?
if ( addr.rfind(".i2p") == addrlen - 4) { if (addr.rfind(".i2p") == addrlen - 4)
// yes it does, make an i2p session {
GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete, // yes it does
shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port); switch (m_cmd)
} else if (m_UseUpstreamProxy) { {
case CMD_CONNECT:
//make an i2p session
GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port);
break;
case CMD_UDP:
{
// create UDP client tunnel
LogPrint (eLogInfo, "SOCKS: New UDP associate connection");
const auto& localEndpoint = ((SOCKSServer *)GetOwner ())->GetLocalEndpoint ();
m_UDPTunnel = std::make_unique<i2p::client::I2PUDPClientTunnel>("", addr,
boost::asio::ip::udp::endpoint (localEndpoint.address (), localEndpoint.port ()), // use proxy endpoint TODO: select UDP port
GetOwner ()->GetLocalDestination (), m_port, false, i2p::datagram::eDatagramV3);
boost::asio::post (GetOwner ()->GetService (), [this](void)
{
SocksRequestSuccess();
});
break;
}
default: ;
}
}
else if (m_UseUpstreamProxy)
// forward it to upstream proxy // forward it to upstream proxy
ForwardSOCKS(); ForwardSOCKS();
} else { else
// no upstream proxy // no upstream proxy
SocksRequestFailed(SOCKS5_ADDR_UNSUP); SocksRequestFailed(SOCKS5_ADDR_UNSUP);
}
} }
else else
AsyncSockRead(); AsyncSockRead();
@ -648,10 +687,18 @@ namespace proxy
if (!ecode) if (!ecode)
{ {
if (Kill()) return; if (Kill()) return;
LogPrint (eLogInfo, "SOCKS: New I2PTunnel connection"); if (m_cmd == CMD_CONNECT)
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, m_stream); {
GetOwner()->AddHandler (connection); LogPrint (eLogInfo, "SOCKS: New I2PTunnel connection");
connection->I2PConnect (m_remaining_data,m_remaining_data_len); auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, m_stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (m_remaining_data,m_remaining_data_len);
}
else if (m_cmd == CMD_UDP && m_UDPTunnel)
{
LogPrint (eLogInfo, "SOCKS: Start UDP tunnel");
m_UDPTunnel->Start ();
}
Done(shared_from_this()); Done(shared_from_this());
} }
else else

View file

@ -147,7 +147,6 @@ namespace client
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions (); std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions ();
bool IsLocalDestination (const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); } bool IsLocalDestination (const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); }
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; } std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
inline void SetLocalDestination (std::shared_ptr<ClientDestination> dest) inline void SetLocalDestination (std::shared_ptr<ClientDestination> dest)
{ {
@ -155,7 +154,8 @@ namespace client
if (dest) dest->Acquire (); if (dest) dest->Acquire ();
m_LocalDest = dest; m_LocalDest = dest;
} }
const boost::asio::ip::udp::endpoint& GetLocalEndpoint () const { return m_LocalEndpoint; };
void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
private: private: