mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-01-22 13:27:17 +01:00
initial support for out proxy via local upstream socks proxy
This commit is contained in:
parent
dbdc7279c4
commit
1a05bcb295
|
@ -149,6 +149,8 @@ namespace config {
|
||||||
("socksproxy.address", value<std::string>()->default_value("127.0.0.1"), "SOCKS Proxy listen address")
|
("socksproxy.address", value<std::string>()->default_value("127.0.0.1"), "SOCKS Proxy listen address")
|
||||||
("socksproxy.port", value<uint16_t>()->default_value(4447), "SOCKS Proxy listen port")
|
("socksproxy.port", value<uint16_t>()->default_value(4447), "SOCKS Proxy listen port")
|
||||||
("socksproxy.keys", value<std::string>()->default_value(""), "File to persist SOCKS Proxy keys")
|
("socksproxy.keys", value<std::string>()->default_value(""), "File to persist SOCKS Proxy keys")
|
||||||
|
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy")
|
||||||
|
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
|
||||||
;
|
;
|
||||||
|
|
||||||
options_description sam("SAM bridge options");
|
options_description sam("SAM bridge options");
|
||||||
|
|
132
I2PService.cpp
132
I2PService.cpp
|
@ -33,6 +33,138 @@ namespace client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream) {}
|
||||||
|
|
||||||
|
TCPIPPipe::~TCPIPPipe()
|
||||||
|
{
|
||||||
|
Terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPIPPipe::Start()
|
||||||
|
{
|
||||||
|
AsyncReceiveUpstream();
|
||||||
|
AsyncReceiveDownstream();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPIPPipe::Terminate()
|
||||||
|
{
|
||||||
|
if(Kill()) return;
|
||||||
|
Done(shared_from_this());
|
||||||
|
if (m_up) {
|
||||||
|
if (m_up->is_open()) {
|
||||||
|
m_up->close();
|
||||||
|
}
|
||||||
|
m_up = nullptr;
|
||||||
|
}
|
||||||
|
if (m_down) {
|
||||||
|
if (m_down->is_open()) {
|
||||||
|
m_down->close();
|
||||||
|
}
|
||||||
|
m_down = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPIPPipe::AsyncReceiveUpstream()
|
||||||
|
{
|
||||||
|
if (m_up) {
|
||||||
|
m_up->async_read_some(boost::asio::buffer(m_upstream_to_down_buf, TCP_IP_PIPE_BUFFER_SIZE),
|
||||||
|
std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
|
||||||
|
std::placeholders::_1, std::placeholders::_2));
|
||||||
|
} else {
|
||||||
|
LogPrint(eLogError, "TCPIPPipe: no upstream socket for read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPIPPipe::AsyncReceiveDownstream()
|
||||||
|
{
|
||||||
|
if (m_down) {
|
||||||
|
m_down->async_read_some(boost::asio::buffer(m_downstream_to_up_buf, TCP_IP_PIPE_BUFFER_SIZE),
|
||||||
|
std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
|
||||||
|
std::placeholders::_1, std::placeholders::_2));
|
||||||
|
} else {
|
||||||
|
LogPrint(eLogError, "TCPIPPipe: no downstream socket for read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
if (m_up) {
|
||||||
|
LogPrint(eLogDebug, "TCPIPPipe: write upstream ", (int)len);
|
||||||
|
boost::asio::async_write(*m_up, boost::asio::buffer(buf, len),
|
||||||
|
boost::asio::transfer_all(),
|
||||||
|
std::bind(&TCPIPPipe::HandleUpstreamWrite,
|
||||||
|
shared_from_this(),
|
||||||
|
std::placeholders::_1)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
LogPrint(eLogError, "tcpip pipe upstream socket null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
if (m_down) {
|
||||||
|
LogPrint(eLogDebug, "TCPIPPipe: write downstream ", (int)len);
|
||||||
|
boost::asio::async_write(*m_down, boost::asio::buffer(buf, len),
|
||||||
|
boost::asio::transfer_all(),
|
||||||
|
std::bind(&TCPIPPipe::HandleDownstreamWrite,
|
||||||
|
shared_from_this(),
|
||||||
|
std::placeholders::_1)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
LogPrint(eLogError, "tcpip pipe downstream socket null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
||||||
|
{
|
||||||
|
LogPrint(eLogDebug, "TCPIPPipe downstream got ", (int) bytes_transfered);
|
||||||
|
if (ecode) {
|
||||||
|
LogPrint(eLogError, "TCPIPPipe Downstream read error:" , ecode.message());
|
||||||
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
|
Terminate();
|
||||||
|
} else {
|
||||||
|
if (bytes_transfered > 0 ) {
|
||||||
|
memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered);
|
||||||
|
UpstreamWrite(m_upstream_buf, bytes_transfered);
|
||||||
|
}
|
||||||
|
AsyncReceiveDownstream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
|
||||||
|
if (ecode) {
|
||||||
|
LogPrint(eLogError, "TCPIPPipe Downstream write error:" , ecode.message());
|
||||||
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
|
Terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
|
||||||
|
if (ecode) {
|
||||||
|
LogPrint(eLogError, "TCPIPPipe Upstream write error:" , ecode.message());
|
||||||
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
|
Terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
||||||
|
{
|
||||||
|
LogPrint(eLogDebug, "TCPIPPipe upstream got ", (int) bytes_transfered);
|
||||||
|
if (ecode) {
|
||||||
|
LogPrint(eLogError, "TCPIPPipe Upstream read error:" , ecode.message());
|
||||||
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
|
Terminate();
|
||||||
|
} else {
|
||||||
|
if (bytes_transfered > 0 ) {
|
||||||
|
memcpy(m_upstream_buf, m_upstream_to_down_buf, bytes_transfered);
|
||||||
|
DownstreamWrite(m_upstream_buf, bytes_transfered);
|
||||||
|
}
|
||||||
|
AsyncReceiveUpstream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TCPIPAcceptor::Start ()
|
void TCPIPAcceptor::Start ()
|
||||||
{
|
{
|
||||||
m_Acceptor.listen ();
|
m_Acceptor.listen ();
|
||||||
|
|
24
I2PService.h
24
I2PService.h
|
@ -76,6 +76,30 @@ namespace client
|
||||||
std::atomic<bool> m_Dead; //To avoid cleaning up multiple times
|
std::atomic<bool> m_Dead; //To avoid cleaning up multiple times
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192;
|
||||||
|
|
||||||
|
// bidirectional pipe for 2 tcp/ip sockets
|
||||||
|
class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this<TCPIPPipe> {
|
||||||
|
public:
|
||||||
|
TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream);
|
||||||
|
~TCPIPPipe();
|
||||||
|
void Start();
|
||||||
|
protected:
|
||||||
|
void Terminate();
|
||||||
|
void AsyncReceiveUpstream();
|
||||||
|
void AsyncReceiveDownstream();
|
||||||
|
void HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred);
|
||||||
|
void HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred);
|
||||||
|
void HandleUpstreamWrite(const boost::system::error_code & ecode);
|
||||||
|
void HandleDownstreamWrite(const boost::system::error_code & ecode);
|
||||||
|
void UpstreamWrite(const uint8_t * buf, size_t len);
|
||||||
|
void DownstreamWrite(const uint8_t * buf, size_t len);
|
||||||
|
private:
|
||||||
|
uint8_t m_upstream_to_down_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[TCP_IP_PIPE_BUFFER_SIZE];
|
||||||
|
uint8_t m_upstream_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_buf[TCP_IP_PIPE_BUFFER_SIZE];
|
||||||
|
std::shared_ptr<boost::asio::ip::tcp::socket> m_up, m_down;
|
||||||
|
};
|
||||||
|
|
||||||
/* TODO: support IPv6 too */
|
/* TODO: support IPv6 too */
|
||||||
//This is a service that listens for connections on the IP network and interacts with I2P
|
//This is a service that listens for connections on the IP network and interacts with I2P
|
||||||
class TCPIPAcceptor: public I2PService
|
class TCPIPAcceptor: public I2PService
|
||||||
|
|
281
SOCKS.cpp
281
SOCKS.cpp
|
@ -9,6 +9,7 @@
|
||||||
#include "ClientContext.h"
|
#include "ClientContext.h"
|
||||||
#include "I2PEndian.h"
|
#include "I2PEndian.h"
|
||||||
#include "I2PTunnel.h"
|
#include "I2PTunnel.h"
|
||||||
|
#include "I2PService.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
|
@ -17,6 +18,10 @@ namespace proxy
|
||||||
static const size_t socks_buffer_size = 8192;
|
static const size_t socks_buffer_size = 8192;
|
||||||
static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse
|
static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse
|
||||||
|
|
||||||
|
static const size_t SOCKS_FORWARDER_BUFFER_SIZE = 8192;
|
||||||
|
|
||||||
|
static const size_t SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE = 8;
|
||||||
|
|
||||||
struct SOCKSDnsAddress
|
struct SOCKSDnsAddress
|
||||||
{
|
{
|
||||||
uint8_t size;
|
uint8_t size;
|
||||||
|
@ -51,7 +56,10 @@ namespace proxy
|
||||||
GET5_IPV6,
|
GET5_IPV6,
|
||||||
GET5_HOST_SIZE,
|
GET5_HOST_SIZE,
|
||||||
GET5_HOST,
|
GET5_HOST,
|
||||||
DONE
|
READY,
|
||||||
|
UPSTREAM_RESOLVE,
|
||||||
|
UPSTREAM_CONNECT,
|
||||||
|
UPSTREAM_HANDSHAKE
|
||||||
};
|
};
|
||||||
enum authMethods
|
enum authMethods
|
||||||
{
|
{
|
||||||
|
@ -109,6 +117,7 @@ namespace proxy
|
||||||
boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method);
|
boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method);
|
||||||
boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port);
|
boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port);
|
||||||
boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port);
|
boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port);
|
||||||
|
boost::asio::const_buffers_1 GenerateUpstreamRequest();
|
||||||
bool Socks5ChooseAuth();
|
bool Socks5ChooseAuth();
|
||||||
void SocksRequestFailed(errTypes error);
|
void SocksRequestFailed(errTypes error);
|
||||||
void SocksRequestSuccess();
|
void SocksRequestSuccess();
|
||||||
|
@ -116,12 +125,29 @@ namespace proxy
|
||||||
void SentSocksDone(const boost::system::error_code & ecode);
|
void SentSocksDone(const boost::system::error_code & ecode);
|
||||||
void SentSocksResponse(const boost::system::error_code & ecode);
|
void SentSocksResponse(const boost::system::error_code & ecode);
|
||||||
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
|
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
|
||||||
|
void ForwardSOCKS();
|
||||||
|
|
||||||
uint8_t m_sock_buff[socks_buffer_size];
|
void SocksUpstreamSuccess();
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
|
void AsyncUpstreamSockRead();
|
||||||
|
void SendUpstreamRequest();
|
||||||
|
void HandleUpstreamData(uint8_t * buff, std::size_t len);
|
||||||
|
void HandleUpstreamSockSend(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
||||||
|
void HandleUpstreamSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
||||||
|
void HandleUpstreamConnected(const boost::system::error_code & ecode,
|
||||||
|
boost::asio::ip::tcp::resolver::iterator itr);
|
||||||
|
void HandleUpstreamResolved(const boost::system::error_code & ecode,
|
||||||
|
boost::asio::ip::tcp::resolver::iterator itr);
|
||||||
|
|
||||||
|
boost::asio::ip::tcp::resolver m_proxy_resolver;
|
||||||
|
uint8_t m_sock_buff[socks_buffer_size];
|
||||||
|
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock, m_upstreamSock;
|
||||||
std::shared_ptr<i2p::stream::Stream> m_stream;
|
std::shared_ptr<i2p::stream::Stream> m_stream;
|
||||||
uint8_t *m_remaining_data; //Data left to be sent
|
uint8_t *m_remaining_data; //Data left to be sent
|
||||||
|
uint8_t *m_remaining_upstream_data; //upstream data left to be forwarded
|
||||||
uint8_t m_response[7+max_socks_hostname_size];
|
uint8_t m_response[7+max_socks_hostname_size];
|
||||||
|
uint8_t m_upstream_response[SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE];
|
||||||
|
uint8_t m_upstream_request[14+max_socks_hostname_size];
|
||||||
|
std::size_t m_upstream_response_len;
|
||||||
address m_address; //Address
|
address m_address; //Address
|
||||||
std::size_t m_remaining_data_len; //Size of the data left to be sent
|
std::size_t m_remaining_data_len; //Size of the data left to be sent
|
||||||
uint32_t m_4aip; //Used in 4a requests
|
uint32_t m_4aip; //Used in 4a requests
|
||||||
|
@ -133,16 +159,25 @@ namespace proxy
|
||||||
socksVersions m_socksv; //Socks version
|
socksVersions m_socksv; //Socks version
|
||||||
cmdTypes m_cmd; // Command requested
|
cmdTypes m_cmd; // Command requested
|
||||||
state m_state;
|
state m_state;
|
||||||
|
const bool m_UseUpstreamProxy; // do we want to use the upstream proxy for non i2p addresses?
|
||||||
|
const std::string m_UpstreamProxyAddress;
|
||||||
|
const uint16_t m_UpstreamProxyPort;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SOCKSHandler(SOCKSServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
|
SOCKSHandler(SOCKSServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock, const std::string & upstreamAddr, const uint16_t upstreamPort, const bool useUpstream) :
|
||||||
I2PServiceHandler(parent), m_sock(sock), m_stream(nullptr),
|
I2PServiceHandler(parent),
|
||||||
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4)
|
m_proxy_resolver(parent->GetService()),
|
||||||
|
m_sock(sock), m_stream(nullptr),
|
||||||
|
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4),
|
||||||
|
m_UseUpstreamProxy(useUpstream),
|
||||||
|
m_UpstreamProxyAddress(upstreamAddr),
|
||||||
|
m_UpstreamProxyPort(upstreamPort)
|
||||||
{ m_address.ip = 0; EnterState(GET_SOCKSV); }
|
{ m_address.ip = 0; EnterState(GET_SOCKSV); }
|
||||||
|
|
||||||
~SOCKSHandler() { Terminate(); }
|
~SOCKSHandler() { Terminate(); }
|
||||||
void Handle() { AsyncSockRead(); }
|
void Handle() { AsyncSockRead(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
void SOCKSHandler::AsyncSockRead()
|
void SOCKSHandler::AsyncSockRead()
|
||||||
{
|
{
|
||||||
LogPrint(eLogDebug, "SOCKS: async sock read");
|
LogPrint(eLogDebug, "SOCKS: async sock read");
|
||||||
|
@ -164,6 +199,12 @@ namespace proxy
|
||||||
m_sock->close();
|
m_sock->close();
|
||||||
m_sock = nullptr;
|
m_sock = nullptr;
|
||||||
}
|
}
|
||||||
|
if (m_upstreamSock)
|
||||||
|
{
|
||||||
|
LogPrint(eLogDebug, "SOCKS: closing upstream socket");
|
||||||
|
m_upstreamSock->close();
|
||||||
|
m_upstreamSock = nullptr;
|
||||||
|
}
|
||||||
if (m_stream)
|
if (m_stream)
|
||||||
{
|
{
|
||||||
LogPrint(eLogDebug, "SOCKS: closing stream");
|
LogPrint(eLogDebug, "SOCKS: closing stream");
|
||||||
|
@ -210,6 +251,37 @@ namespace proxy
|
||||||
return boost::asio::const_buffers_1(m_response,size);
|
return boost::asio::const_buffers_1(m_response,size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::asio::const_buffers_1 SOCKSHandler::GenerateUpstreamRequest()
|
||||||
|
{
|
||||||
|
size_t upstreamRequestSize = 0;
|
||||||
|
// TODO: negotiate with upstream
|
||||||
|
// SOCKS 4a
|
||||||
|
m_upstream_request[0] = '\x04'; //version
|
||||||
|
m_upstream_request[1] = m_cmd;
|
||||||
|
htobe16buf(m_upstream_request+2, m_port);
|
||||||
|
m_upstream_request[4] = 0;
|
||||||
|
m_upstream_request[5] = 0;
|
||||||
|
m_upstream_request[6] = 0;
|
||||||
|
m_upstream_request[7] = 1;
|
||||||
|
// user id
|
||||||
|
m_upstream_request[8] = 'i';
|
||||||
|
m_upstream_request[9] = '2';
|
||||||
|
m_upstream_request[10] = 'p';
|
||||||
|
m_upstream_request[11] = 'd';
|
||||||
|
m_upstream_request[12] = 0;
|
||||||
|
upstreamRequestSize += 13;
|
||||||
|
if (m_address.dns.size <= max_socks_hostname_size - ( upstreamRequestSize + 1) ) {
|
||||||
|
// bounds check okay
|
||||||
|
memcpy(m_upstream_request + upstreamRequestSize, m_address.dns.value, m_address.dns.size);
|
||||||
|
upstreamRequestSize += m_address.dns.size;
|
||||||
|
// null terminate
|
||||||
|
m_upstream_request[++upstreamRequestSize] = 0;
|
||||||
|
} else {
|
||||||
|
LogPrint(eLogError, "SOCKS: BUG!!! m_addr.dns.sizs > max_socks_hostname - ( upstreamRequestSize + 1 ) )");
|
||||||
|
}
|
||||||
|
return boost::asio::const_buffers_1(m_upstream_request, upstreamRequestSize);
|
||||||
|
}
|
||||||
|
|
||||||
bool SOCKSHandler::Socks5ChooseAuth()
|
bool SOCKSHandler::Socks5ChooseAuth()
|
||||||
{
|
{
|
||||||
m_response[0] = '\x05'; //Version
|
m_response[0] = '\x05'; //Version
|
||||||
|
@ -219,14 +291,14 @@ namespace proxy
|
||||||
{
|
{
|
||||||
LogPrint(eLogWarning, "SOCKS: v5 authentication negotiation failed");
|
LogPrint(eLogWarning, "SOCKS: v5 authentication negotiation failed");
|
||||||
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
|
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
|
||||||
shared_from_this(), std::placeholders::_1));
|
shared_from_this(), std::placeholders::_1));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogPrint(eLogDebug, "SOCKS: v5 choosing authentication method: ", m_authchosen);
|
LogPrint(eLogDebug, "SOCKS: v5 choosing authentication method: ", m_authchosen);
|
||||||
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse,
|
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse,
|
||||||
shared_from_this(), std::placeholders::_1));
|
shared_from_this(), std::placeholders::_1));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,7 +321,7 @@ namespace proxy
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
|
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
|
||||||
shared_from_this(), std::placeholders::_1));
|
shared_from_this(), std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SOCKSHandler::SocksRequestSuccess()
|
void SOCKSHandler::SocksRequestSuccess()
|
||||||
|
@ -271,7 +343,7 @@ namespace proxy
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone,
|
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone,
|
||||||
shared_from_this(), std::placeholders::_1));
|
shared_from_this(), std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) {
|
void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) {
|
||||||
|
@ -313,13 +385,6 @@ namespace proxy
|
||||||
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
|
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//TODO: we may want to support other domains
|
|
||||||
if(m_addrtype == ADDR_DNS && m_address.dns.ToString().find(".i2p") == std::string::npos)
|
|
||||||
{
|
|
||||||
LogPrint(eLogError, "SOCKS: invalid hostname: ", m_address.dns.ToString());
|
|
||||||
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,7 +451,7 @@ namespace proxy
|
||||||
{
|
{
|
||||||
switch (m_socksv)
|
switch (m_socksv)
|
||||||
{
|
{
|
||||||
case SOCKS5: EnterState(DONE); break;
|
case SOCKS5: EnterState(READY); break;
|
||||||
case SOCKS4: EnterState(GET_IPV4); break;
|
case SOCKS4: EnterState(GET_IPV4); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -407,7 +472,7 @@ namespace proxy
|
||||||
if (!*sock_buff)
|
if (!*sock_buff)
|
||||||
{
|
{
|
||||||
if( m_4aip == 0 || m_4aip > 255 )
|
if( m_4aip == 0 || m_4aip > 255 )
|
||||||
EnterState(DONE);
|
EnterState(READY);
|
||||||
else
|
else
|
||||||
EnterState(GET4A_HOST);
|
EnterState(GET4A_HOST);
|
||||||
}
|
}
|
||||||
|
@ -415,7 +480,7 @@ namespace proxy
|
||||||
case GET4A_HOST:
|
case GET4A_HOST:
|
||||||
if (!*sock_buff)
|
if (!*sock_buff)
|
||||||
{
|
{
|
||||||
EnterState(DONE);
|
EnterState(READY);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (m_address.dns.size >= max_socks_hostname_size)
|
if (m_address.dns.size >= max_socks_hostname_size)
|
||||||
|
@ -476,7 +541,7 @@ namespace proxy
|
||||||
}
|
}
|
||||||
sock_buff++;
|
sock_buff++;
|
||||||
len--;
|
len--;
|
||||||
if (m_state == DONE)
|
if (m_state == READY)
|
||||||
{
|
{
|
||||||
m_remaining_data_len = len;
|
m_remaining_data_len = len;
|
||||||
m_remaining_data = sock_buff;
|
m_remaining_data = sock_buff;
|
||||||
|
@ -498,11 +563,23 @@ namespace proxy
|
||||||
|
|
||||||
if (HandleData(m_sock_buff, len))
|
if (HandleData(m_sock_buff, len))
|
||||||
{
|
{
|
||||||
if (m_state == DONE)
|
if (m_state == READY)
|
||||||
{
|
{
|
||||||
LogPrint(eLogInfo, "SOCKS: requested ", m_address.dns.ToString(), ":" , m_port);
|
const std::string addr = m_address.dns.ToString();
|
||||||
GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete,
|
LogPrint(eLogInfo, "SOCKS: requested ", addr, ":" , m_port);
|
||||||
shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port);
|
const size_t addrlen = addr.size();
|
||||||
|
// does it end with .i2p?
|
||||||
|
if ( addr.rfind(".i2p") == addrlen - 4) {
|
||||||
|
// yes it does, make an i2p session
|
||||||
|
GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete,
|
||||||
|
shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port);
|
||||||
|
} else if (m_UseUpstreamProxy) {
|
||||||
|
// forward it to upstream proxy
|
||||||
|
ForwardSOCKS();
|
||||||
|
} else {
|
||||||
|
// no upstream proxy
|
||||||
|
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
AsyncSockRead();
|
AsyncSockRead();
|
||||||
|
@ -556,17 +633,161 @@ namespace proxy
|
||||||
SocksRequestFailed(SOCKS5_HOST_UNREACH);
|
SocksRequestFailed(SOCKS5_HOST_UNREACH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SOCKSHandler::ForwardSOCKS()
|
||||||
|
{
|
||||||
|
LogPrint(eLogInfo, "SOCKS: forwarding to upstream");
|
||||||
|
EnterState(UPSTREAM_RESOLVE);
|
||||||
|
auto & service = GetOwner()->GetService();
|
||||||
|
boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress,boost::lexical_cast<std::string>(m_UpstreamProxyPort) );
|
||||||
|
m_proxy_resolver.async_resolve(q, std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(),
|
||||||
|
std::placeholders::_1, std::placeholders::_2));
|
||||||
|
|
||||||
SOCKSServer::SOCKSServer(const std::string& address, int port, const std::string& outAddress, int outPort,
|
}
|
||||||
std::shared_ptr<i2p::client::ClientDestination> localDestination) :
|
|
||||||
|
void SOCKSHandler::AsyncUpstreamSockRead()
|
||||||
|
{
|
||||||
|
LogPrint(eLogDebug, "SOCKS: async upstream sock read");
|
||||||
|
if (m_upstreamSock) {
|
||||||
|
m_upstreamSock->async_read_some(boost::asio::buffer(m_upstream_response, SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE),
|
||||||
|
std::bind(&SOCKSHandler::HandleUpstreamSockRecv, shared_from_this(),
|
||||||
|
std::placeholders::_1, std::placeholders::_2));
|
||||||
|
} else {
|
||||||
|
LogPrint(eLogError, "SOCKS: no upstream socket for read");
|
||||||
|
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SOCKSHandler::HandleUpstreamSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
||||||
|
{
|
||||||
|
if (ecode) {
|
||||||
|
if (m_state == UPSTREAM_HANDSHAKE ) {
|
||||||
|
// we are trying to handshake but it failed
|
||||||
|
SocksRequestFailed(SOCKS5_NET_UNREACH);
|
||||||
|
} else {
|
||||||
|
LogPrint(eLogError, "SOCKS: bad state when reading from upstream: ", (int) m_state);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HandleUpstreamData(m_upstream_response, bytes_transfered);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SOCKSHandler::SocksUpstreamSuccess()
|
||||||
|
{
|
||||||
|
LogPrint(eLogInfo, "SOCKS: upstream success");
|
||||||
|
boost::asio::const_buffers_1 response(nullptr, 0);
|
||||||
|
switch (m_socksv)
|
||||||
|
{
|
||||||
|
case SOCKS4:
|
||||||
|
LogPrint(eLogInfo, "SOCKS: v4 connection success");
|
||||||
|
response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port);
|
||||||
|
break;
|
||||||
|
case SOCKS5:
|
||||||
|
LogPrint(eLogInfo, "SOCKS: v5 connection success");
|
||||||
|
//HACK only 16 bits passed in port as SOCKS5 doesn't allow for more
|
||||||
|
response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, m_address, m_port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_sock->send(response);
|
||||||
|
auto forwarder = std::make_shared<i2p::client::TCPIPPipe>(GetOwner(), m_sock, m_upstreamSock);
|
||||||
|
m_upstreamSock = nullptr;
|
||||||
|
m_sock = nullptr;
|
||||||
|
GetOwner()->AddHandler(forwarder);
|
||||||
|
forwarder->Start();
|
||||||
|
Terminate();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SOCKSHandler::HandleUpstreamData(uint8_t * dataptr, std::size_t len)
|
||||||
|
{
|
||||||
|
if (m_state == UPSTREAM_HANDSHAKE) {
|
||||||
|
m_upstream_response_len += len;
|
||||||
|
// handle handshake data
|
||||||
|
if (m_upstream_response_len < SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE) {
|
||||||
|
// too small, continue reading
|
||||||
|
AsyncUpstreamSockRead();
|
||||||
|
} else if (len == SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE) {
|
||||||
|
// just right
|
||||||
|
uint8_t resp = m_upstream_response[1];
|
||||||
|
if (resp == SOCKS4_OK) {
|
||||||
|
// we have connected !
|
||||||
|
SocksUpstreamSuccess();
|
||||||
|
} else {
|
||||||
|
// upstream failure
|
||||||
|
LogPrint(eLogError, "SOCKS: upstream proxy failure: ", (int) resp);
|
||||||
|
// TODO: runtime error?
|
||||||
|
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// too big
|
||||||
|
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// invalid state
|
||||||
|
LogPrint(eLogError, "SOCKS: invalid state reading from upstream: ", (int) m_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SOCKSHandler::SendUpstreamRequest()
|
||||||
|
{
|
||||||
|
LogPrint(eLogInfo, "SOCKS: negotiating with upstream proxy");
|
||||||
|
EnterState(UPSTREAM_HANDSHAKE);
|
||||||
|
if (m_upstreamSock) {
|
||||||
|
boost::asio::write(*m_upstreamSock,
|
||||||
|
GenerateUpstreamRequest());
|
||||||
|
AsyncUpstreamSockRead();
|
||||||
|
} else {
|
||||||
|
LogPrint(eLogError, "SOCKS: no upstream socket to send handshake to");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SOCKSHandler::HandleUpstreamConnected(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr)
|
||||||
|
{
|
||||||
|
if (ecode) {
|
||||||
|
LogPrint(eLogWarning, "SOCKS: could not connect to upstream proxy: ", ecode.message());
|
||||||
|
SocksRequestFailed(SOCKS5_NET_UNREACH);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LogPrint(eLogInfo, "SOCKS: connected to upstream proxy");
|
||||||
|
SendUpstreamRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SOCKSHandler::HandleUpstreamResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr)
|
||||||
|
{
|
||||||
|
if (ecode) {
|
||||||
|
// error resolving
|
||||||
|
LogPrint(eLogWarning, "SOCKS: upstream proxy", m_UpstreamProxyAddress, " not resolved: ", ecode.message());
|
||||||
|
SocksRequestFailed(SOCKS5_NET_UNREACH);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LogPrint(eLogInfo, "SOCKS: upstream proxy resolved");
|
||||||
|
EnterState(UPSTREAM_CONNECT);
|
||||||
|
auto & service = GetOwner()->GetService();
|
||||||
|
m_upstreamSock = std::make_shared<boost::asio::ip::tcp::socket>(service);
|
||||||
|
boost::asio::async_connect(*m_upstreamSock, itr,
|
||||||
|
std::bind(&SOCKSHandler::HandleUpstreamConnected,
|
||||||
|
shared_from_this(), std::placeholders::_1, std::placeholders::_2));
|
||||||
|
}
|
||||||
|
|
||||||
|
SOCKSServer::SOCKSServer(const std::string& address, int port, const std::string& outAddress, uint16_t outPort,
|
||||||
|
std::shared_ptr<i2p::client::ClientDestination> localDestination) :
|
||||||
TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
|
TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
|
||||||
{
|
{
|
||||||
|
m_UseUpstreamProxy = false;
|
||||||
|
if (outAddress.length() > 0)
|
||||||
|
SetUpstreamProxy(outAddress, outPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<i2p::client::I2PServiceHandler> SOCKSServer::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
std::shared_ptr<i2p::client::I2PServiceHandler> SOCKSServer::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
||||||
{
|
{
|
||||||
return std::make_shared<SOCKSHandler> (this, socket);
|
return std::make_shared<SOCKSHandler> (this, socket, m_UpstreamProxyAddress, m_UpstreamProxyPort, m_UseUpstreamProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SOCKSServer::SetUpstreamProxy(const std::string & addr, const uint16_t port)
|
||||||
|
{
|
||||||
|
m_UpstreamProxyAddress = addr;
|
||||||
|
m_UpstreamProxyPort = port;
|
||||||
|
m_UseUpstreamProxy = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
9
SOCKS.h
9
SOCKS.h
|
@ -15,14 +15,21 @@ namespace proxy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
SOCKSServer(const std::string& address, int port, const std::string& outAddress, int outPort,
|
SOCKSServer(const std::string& address, int port, const std::string& outAddress, uint16_t outPort,
|
||||||
std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
|
std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
|
||||||
~SOCKSServer() {};
|
~SOCKSServer() {};
|
||||||
|
|
||||||
|
void SetUpstreamProxy(const std::string & addr, const uint16_t port);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Implements TCPIPAcceptor
|
// Implements TCPIPAcceptor
|
||||||
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||||
const char* GetName() { return "SOCKS"; }
|
const char* GetName() { return "SOCKS"; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_UpstreamProxyAddress;
|
||||||
|
uint16_t m_UpstreamProxyPort;
|
||||||
|
bool m_UseUpstreamProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef SOCKSServer SOCKSProxy;
|
typedef SOCKSServer SOCKSProxy;
|
||||||
|
|
Loading…
Reference in a new issue