diff --git a/Config.cpp b/Config.cpp index 81c2a2ce..0de4d0aa 100644 --- a/Config.cpp +++ b/Config.cpp @@ -149,6 +149,8 @@ namespace config { ("socksproxy.address", value()->default_value("127.0.0.1"), "SOCKS Proxy listen address") ("socksproxy.port", value()->default_value(4447), "SOCKS Proxy listen port") ("socksproxy.keys", value()->default_value(""), "File to persist SOCKS Proxy keys") + ("socksproxy.outproxy", value()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy") + ("socksproxy.outproxyport", value()->default_value(9050), "Upstream outproxy port for SOCKS Proxy") ; options_description sam("SAM bridge options"); diff --git a/I2PService.cpp b/I2PService.cpp index 85582c0e..0aefc172 100644 --- a/I2PService.cpp +++ b/I2PService.cpp @@ -33,6 +33,138 @@ namespace client } } + TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr upstream, std::shared_ptr 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 () { m_Acceptor.listen (); diff --git a/I2PService.h b/I2PService.h index 9d91b086..251a379a 100644 --- a/I2PService.h +++ b/I2PService.h @@ -76,6 +76,30 @@ namespace client std::atomic 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 { + public: + TCPIPPipe(I2PService * owner, std::shared_ptr upstream, std::shared_ptr 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 m_up, m_down; + }; + /* TODO: support IPv6 too */ //This is a service that listens for connections on the IP network and interacts with I2P class TCPIPAcceptor: public I2PService diff --git a/SOCKS.cpp b/SOCKS.cpp index 27b23df1..e95eccbe 100644 --- a/SOCKS.cpp +++ b/SOCKS.cpp @@ -9,6 +9,7 @@ #include "ClientContext.h" #include "I2PEndian.h" #include "I2PTunnel.h" +#include "I2PService.h" namespace i2p { @@ -17,6 +18,10 @@ namespace proxy 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 SOCKS_FORWARDER_BUFFER_SIZE = 8192; + + static const size_t SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE = 8; + struct SOCKSDnsAddress { uint8_t size; @@ -51,7 +56,10 @@ namespace proxy GET5_IPV6, GET5_HOST_SIZE, GET5_HOST, - DONE + READY, + UPSTREAM_RESOLVE, + UPSTREAM_CONNECT, + UPSTREAM_HANDSHAKE }; enum authMethods { @@ -109,6 +117,7 @@ namespace proxy 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 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port); + boost::asio::const_buffers_1 GenerateUpstreamRequest(); bool Socks5ChooseAuth(); void SocksRequestFailed(errTypes error); void SocksRequestSuccess(); @@ -116,12 +125,29 @@ namespace proxy void SentSocksDone(const boost::system::error_code & ecode); void SentSocksResponse(const boost::system::error_code & ecode); void HandleStreamRequestComplete (std::shared_ptr stream); + void ForwardSOCKS(); - uint8_t m_sock_buff[socks_buffer_size]; - std::shared_ptr m_sock; + void SocksUpstreamSuccess(); + 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 m_sock, m_upstreamSock; std::shared_ptr m_stream; 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_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 std::size_t m_remaining_data_len; //Size of the data left to be sent uint32_t m_4aip; //Used in 4a requests @@ -133,16 +159,25 @@ namespace proxy socksVersions m_socksv; //Socks version cmdTypes m_cmd; // Command requested 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: - SOCKSHandler(SOCKSServer * parent, std::shared_ptr sock) : - I2PServiceHandler(parent), m_sock(sock), m_stream(nullptr), - m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4) + SOCKSHandler(SOCKSServer * parent, std::shared_ptr sock, const std::string & upstreamAddr, const uint16_t upstreamPort, const bool useUpstream) : + I2PServiceHandler(parent), + 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); } + ~SOCKSHandler() { Terminate(); } void Handle() { AsyncSockRead(); } }; - + void SOCKSHandler::AsyncSockRead() { LogPrint(eLogDebug, "SOCKS: async sock read"); @@ -164,6 +199,12 @@ namespace proxy m_sock->close(); m_sock = nullptr; } + if (m_upstreamSock) + { + LogPrint(eLogDebug, "SOCKS: closing upstream socket"); + m_upstreamSock->close(); + m_upstreamSock = nullptr; + } if (m_stream) { LogPrint(eLogDebug, "SOCKS: closing stream"); @@ -210,6 +251,37 @@ namespace proxy 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() { m_response[0] = '\x05'; //Version @@ -219,14 +291,14 @@ namespace proxy { LogPrint(eLogWarning, "SOCKS: v5 authentication negotiation failed"); 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; } else { LogPrint(eLogDebug, "SOCKS: v5 choosing authentication method: ", m_authchosen); 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; } } @@ -249,7 +321,7 @@ namespace proxy break; } 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() @@ -271,7 +343,7 @@ namespace proxy break; } 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) { @@ -313,13 +385,6 @@ namespace proxy SocksRequestFailed(SOCKS5_ADDR_UNSUP); 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; } @@ -386,7 +451,7 @@ namespace proxy { switch (m_socksv) { - case SOCKS5: EnterState(DONE); break; + case SOCKS5: EnterState(READY); break; case SOCKS4: EnterState(GET_IPV4); break; } } @@ -407,7 +472,7 @@ namespace proxy if (!*sock_buff) { if( m_4aip == 0 || m_4aip > 255 ) - EnterState(DONE); + EnterState(READY); else EnterState(GET4A_HOST); } @@ -415,7 +480,7 @@ namespace proxy case GET4A_HOST: if (!*sock_buff) { - EnterState(DONE); + EnterState(READY); break; } if (m_address.dns.size >= max_socks_hostname_size) @@ -476,7 +541,7 @@ namespace proxy } sock_buff++; len--; - if (m_state == DONE) + if (m_state == READY) { m_remaining_data_len = len; m_remaining_data = sock_buff; @@ -498,11 +563,23 @@ namespace proxy if (HandleData(m_sock_buff, len)) { - if (m_state == DONE) + if (m_state == READY) { - LogPrint(eLogInfo, "SOCKS: requested ", m_address.dns.ToString(), ":" , m_port); - GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete, - shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port); + const std::string addr = m_address.dns.ToString(); + LogPrint(eLogInfo, "SOCKS: requested ", addr, ":" , 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 AsyncSockRead(); @@ -556,17 +633,161 @@ namespace proxy 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(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 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(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(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 localDestination) : TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()) { + m_UseUpstreamProxy = false; + if (outAddress.length() > 0) + SetUpstreamProxy(outAddress, outPort); } std::shared_ptr SOCKSServer::CreateHandler(std::shared_ptr socket) { - return std::make_shared (this, socket); + return std::make_shared (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; + } } } diff --git a/SOCKS.h b/SOCKS.h index 82f068fb..d417c4a0 100644 --- a/SOCKS.h +++ b/SOCKS.h @@ -15,14 +15,21 @@ namespace proxy { 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 localDestination = nullptr); ~SOCKSServer() {}; + void SetUpstreamProxy(const std::string & addr, const uint16_t port); + protected: // Implements TCPIPAcceptor std::shared_ptr CreateHandler(std::shared_ptr socket); const char* GetName() { return "SOCKS"; } + + private: + std::string m_UpstreamProxyAddress; + uint16_t m_UpstreamProxyPort; + bool m_UseUpstreamProxy; }; typedef SOCKSServer SOCKSProxy;