|
|
@ -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; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|