Browse Source

Merge pull request #395 from PurpleI2P/openssl

socks outproxy
pull/400/head
orignal 9 years ago
parent
commit
efefa8caf5
  1. 2
      Config.cpp
  2. 24
      Destination.cpp
  3. 2
      Destination.h
  4. 2
      I2NPProtocol.cpp
  5. 2
      I2NPProtocol.h
  6. 132
      I2PService.cpp
  7. 24
      I2PService.h
  8. 281
      SOCKS.cpp
  9. 9
      SOCKS.h
  10. 9
      Streaming.cpp
  11. 7
      docs/build_notes_unix.md

2
Config.cpp

@ -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");

24
Destination.cpp

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

2
Destination.h

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

2
I2NPProtocol.cpp

@ -157,7 +157,7 @@ namespace i2p
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills, 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 (); int cnt = excludedFloodfills.size ();
auto m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage (); auto m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage ();

2
I2NPProtocol.h

@ -222,7 +222,7 @@ namespace tunnel
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr); uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills, 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> 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); std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);

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

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

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

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

9
Streaming.cpp

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

7
docs/build_notes_unix.md

@ -96,6 +96,9 @@ miniupnpc-devel
FreeBSD 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. Branch 9.X has gcc v4.2, that knows nothing about required c++11 standart.
Required ports: Required ports:
@ -110,10 +113,6 @@ export CC=/usr/local/bin/gcc47
export CXX=/usr/local/bin/g++47 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 CMake Options
------------- -------------

Loading…
Cancel
Save