Merge pull request #395 from PurpleI2P/openssl

socks outproxy
This commit is contained in:
orignal 2016-02-27 16:16:15 -05:00
commit efefa8caf5
11 changed files with 443 additions and 51 deletions

View File

@ -149,6 +149,8 @@ namespace config {
("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.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");

View File

@ -654,13 +654,14 @@ namespace client
bool ClientDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request)
{
auto replyTunnel = m_Pool->GetNextInboundTunnel ();
if (!replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found");
auto outboundTunnel = m_Pool->GetNextOutboundTunnel ();
if (!outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found");
if (!request->replyTunnel || !request->replyTunnel->IsEstablished ())
request->replyTunnel = m_Pool->GetNextInboundTunnel ();
if (!request->replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found");
if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ())
request->outboundTunnel = m_Pool->GetNextOutboundTunnel ();
if (!request->outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found");
if (replyTunnel && outboundTunnel)
if (request->replyTunnel && request->outboundTunnel)
{
request->excluded.insert (nextFloodfill->GetIdentHash ());
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
@ -673,8 +674,8 @@ namespace client
auto msg = WrapMessage (nextFloodfill,
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
replyTunnel.get (), replyKey, replyTag));
outboundTunnel->SendTunnelDataMsg (
request->replyTunnel, replyKey, replyTag));
request->outboundTunnel->SendTunnelDataMsg (
{
i2p::tunnel::TunnelMessageBlock
{
@ -704,7 +705,12 @@ namespace client
{
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
if (floodfill)
done = !SendLeaseSetRequest (dest, floodfill, it->second);
{
// reset tunnels, because one them might fail
it->second->outboundTunnel = nullptr;
it->second->replyTunnel = nullptr;
done = !SendLeaseSetRequest (dest, floodfill, it->second);
}
else
done = true;
}

View File

@ -61,6 +61,8 @@ namespace client
uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer;
RequestComplete requestComplete;
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
};

View File

@ -157,7 +157,7 @@ namespace i2p
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag)
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag)
{
int cnt = excludedFloodfills.size ();
auto m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage ();

View File

@ -222,7 +222,7 @@ namespace tunnel
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);

View File

@ -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 ()
{
m_Acceptor.listen ();

View File

@ -76,6 +76,30 @@ namespace client
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 */
//This is a service that listens for connections on the IP network and interacts with I2P
class TCPIPAcceptor: public I2PService

281
SOCKS.cpp
View File

@ -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<i2p::stream::Stream> stream);
void ForwardSOCKS();
uint8_t m_sock_buff[socks_buffer_size];
std::shared_ptr<boost::asio::ip::tcp::socket> 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<boost::asio::ip::tcp::socket> m_sock, m_upstreamSock;
std::shared_ptr<i2p::stream::Stream> 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<boost::asio::ip::tcp::socket> sock) :
I2PServiceHandler(parent), m_sock(sock), m_stream(nullptr),
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4)
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_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<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 ())
{
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)
{
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;
}
}
}

View File

@ -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<i2p::client::ClientDestination> localDestination = nullptr);
~SOCKSServer() {};
void SetUpstreamProxy(const std::string & addr, const uint16_t port);
protected:
// Implements TCPIPAcceptor
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
const char* GetName() { return "SOCKS"; }
private:
std::string m_UpstreamProxyAddress;
uint16_t m_UpstreamProxyPort;
bool m_UseUpstreamProxy;
};
typedef SOCKSServer SOCKSProxy;

View File

@ -621,7 +621,7 @@ namespace stream
}
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (!m_CurrentRemoteLease || ts >= m_CurrentRemoteLease->endDate - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000)
if (!m_CurrentRemoteLease || ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD)
UpdateCurrentRemoteLease (true);
if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD)
{
@ -743,8 +743,8 @@ namespace stream
if (leases.empty ())
{
expired = false;
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // time to re-request
leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // time to request
leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold
}
if (!leases.empty ())
{
@ -772,8 +772,7 @@ namespace stream
{
m_RemoteLeaseSet = nullptr;
m_CurrentRemoteLease = nullptr;
// re-request expired
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ());
// we have requested expired before, no need to do it twice
}
}
else

View File

@ -96,6 +96,9 @@ miniupnpc-devel
FreeBSD
-------
For 10.X use clang. You would also need boost and openssl ports.
Type gmake, it invokes Makefile.bsd, make necessary changes there is required.
Branch 9.X has gcc v4.2, that knows nothing about required c++11 standart.
Required ports:
@ -110,10 +113,6 @@ export CC=/usr/local/bin/gcc47
export CXX=/usr/local/bin/g++47
```
Branch 10.X has more reliable clang version, that can finally build i2pd,
but I still recommend to use gcc, otherwise you will fight it's bugs by
your own.
CMake Options
-------------