Browse Source

Merge pull request #881 from majestrate/ntcp-socks

NTCP SOCKS/HTTP Proxy support
pull/883/head
orignal 7 years ago committed by GitHub
parent
commit
36c4719570
  1. 1
      daemon/Daemon.cpp
  2. 1
      libi2pd/Config.cpp
  3. 397
      libi2pd/NTCPSession.cpp
  4. 34
      libi2pd/NTCPSession.h
  5. 62
      libi2pd/Transports.cpp

1
daemon/Daemon.cpp

@ -269,6 +269,7 @@ namespace i2p
LogPrint(eLogInfo, "Daemon: starting Transports"); LogPrint(eLogInfo, "Daemon: starting Transports");
if(!ssu) LogPrint(eLogInfo, "Daemon: ssu disabled"); if(!ssu) LogPrint(eLogInfo, "Daemon: ssu disabled");
if(!ntcp) LogPrint(eLogInfo, "Daemon: ntcp disabled"); if(!ntcp) LogPrint(eLogInfo, "Daemon: ntcp disabled");
i2p::transport::transports.Start(ntcp, ssu); i2p::transport::transports.Start(ntcp, ssu);
if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU()) { if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU()) {
LogPrint(eLogInfo, "Daemon: Transports started"); LogPrint(eLogInfo, "Daemon: Transports started");

1
libi2pd/Config.cpp

@ -57,6 +57,7 @@ namespace config {
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100") ("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100")
("ntcp", value<bool>()->zero_tokens()->default_value(true), "Enable NTCP transport") ("ntcp", value<bool>()->zero_tokens()->default_value(true), "Enable NTCP transport")
("ssu", value<bool>()->zero_tokens()->default_value(true), "Enable SSU transport") ("ssu", value<bool>()->zero_tokens()->default_value(true), "Enable SSU transport")
("ntcpproxy", value<std::string>()->default_value(""), "proxy url for ntcp transport")
#ifdef _WIN32 #ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')") ("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
("insomnia", value<bool>()->zero_tokens()->default_value(false), "Prevent system from sleeping") ("insomnia", value<bool>()->zero_tokens()->default_value(false), "Prevent system from sleeping")

397
libi2pd/NTCPSession.cpp

@ -12,6 +12,7 @@
#include "Transports.h" #include "Transports.h"
#include "NetDb.hpp" #include "NetDb.hpp"
#include "NTCPSession.h" #include "NTCPSession.h"
#include "HTTP.h"
#ifdef WITH_EVENTS #ifdef WITH_EVENTS
#include "Event.h" #include "Event.h"
#endif #endif
@ -116,7 +117,7 @@ namespace transport
m_Establisher->phase1.HXxorHI[i] ^= ident[i]; m_Establisher->phase1.HXxorHI[i] ^= ident[i];
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (), boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase1Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); std::bind(&NTCPSession::HandlePhase1Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
} }
void NTCPSession::ServerLogin () void NTCPSession::ServerLogin ()
@ -132,7 +133,7 @@ namespace transport
{ {
(void) bytes_transferred; (void) bytes_transferred;
if (ecode) if (ecode)
{ {
LogPrint (eLogInfo, "NTCP: couldn't send Phase 1 message: ", ecode.message ()); LogPrint (eLogInfo, "NTCP: couldn't send Phase 1 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
Terminate (); Terminate ();
@ -149,7 +150,7 @@ namespace transport
{ {
(void) bytes_transferred; (void) bytes_transferred;
if (ecode) if (ecode)
{ {
LogPrint (eLogInfo, "NTCP: phase 1 read error: ", ecode.message ()); LogPrint (eLogInfo, "NTCP: phase 1 read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
Terminate (); Terminate ();
@ -185,10 +186,10 @@ namespace transport
s->CreateAESKey (s->m_Establisher->phase1.pubKey); s->CreateAESKey (s->m_Establisher->phase1.pubKey);
}).share (); }).share ();
m_Server.GetService ().post ([s, keyCreated]() m_Server.GetService ().post ([s, keyCreated]()
{ {
keyCreated.get (); keyCreated.get ();
s->SendPhase2 (); s->SendPhase2 ();
}); });
#endif #endif
} }
} }
@ -210,7 +211,7 @@ namespace transport
m_Encryption.Encrypt ((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); m_Encryption.Encrypt ((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (), boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase2Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsB)); std::bind(&NTCPSession::HandlePhase2Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsB));
} }
@ -218,7 +219,7 @@ namespace transport
{ {
(void) bytes_transferred; (void) bytes_transferred;
if (ecode) if (ecode)
{ {
LogPrint (eLogInfo, "NTCP: Couldn't send Phase 2 message: ", ecode.message ()); LogPrint (eLogInfo, "NTCP: Couldn't send Phase 2 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
Terminate (); Terminate ();
@ -235,7 +236,7 @@ namespace transport
{ {
(void) bytes_transferred; (void) bytes_transferred;
if (ecode) if (ecode)
{ {
LogPrint (eLogInfo, "NTCP: Phase 2 read error: ", ecode.message (), ". Wrong ident assumed"); LogPrint (eLogInfo, "NTCP: Phase 2 read error: ", ecode.message (), ". Wrong ident assumed");
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
@ -318,20 +319,20 @@ namespace transport
s.Insert (m_Establisher->phase1.pubKey, 256); // x s.Insert (m_Establisher->phase1.pubKey, 256); // x
s.Insert (m_Establisher->phase2.pubKey, 256); // y s.Insert (m_Establisher->phase2.pubKey, 256); // y
s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident
s.Insert (tsA); // tsA s.Insert (tsA); // tsA
s.Insert (m_Establisher->phase2.encrypted.timestamp, 4); // tsB s.Insert (m_Establisher->phase2.encrypted.timestamp, 4); // tsB
s.Sign (keys, buf); s.Sign (keys, buf);
m_Encryption.Encrypt(m_ReceiveBuffer, len, m_ReceiveBuffer); m_Encryption.Encrypt(m_ReceiveBuffer, len, m_ReceiveBuffer);
boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, len), boost::asio::transfer_all (), boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, len), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase3Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsA)); std::bind(&NTCPSession::HandlePhase3Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsA));
} }
void NTCPSession::HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA) void NTCPSession::HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA)
{ {
(void) bytes_transferred; (void) bytes_transferred;
if (ecode) if (ecode)
{ {
LogPrint (eLogInfo, "NTCP: Couldn't send Phase 3 message: ", ecode.message ()); LogPrint (eLogInfo, "NTCP: Couldn't send Phase 3 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
Terminate (); Terminate ();
@ -351,7 +352,7 @@ namespace transport
void NTCPSession::HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB) void NTCPSession::HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB)
{ {
if (ecode) if (ecode)
{ {
LogPrint (eLogInfo, "NTCP: Phase 3 read error: ", ecode.message ()); LogPrint (eLogInfo, "NTCP: Phase 3 read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
Terminate (); Terminate ();
@ -389,7 +390,7 @@ namespace transport
void NTCPSession::HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen) void NTCPSession::HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen)
{ {
if (ecode) if (ecode)
{ {
LogPrint (eLogInfo, "NTCP: Phase 3 extra read error: ", ecode.message ()); LogPrint (eLogInfo, "NTCP: Phase 3 extra read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
Terminate (); Terminate ();
@ -444,21 +445,21 @@ namespace transport
s.Insert (tsA); // tsA s.Insert (tsA); // tsA
s.Insert (tsB); // tsB s.Insert (tsB); // tsB
auto& keys = i2p::context.GetPrivateKeys (); auto& keys = i2p::context.GetPrivateKeys ();
auto signatureLen = keys.GetPublic ()->GetSignatureLen (); auto signatureLen = keys.GetPublic ()->GetSignatureLen ();
s.Sign (keys, m_ReceiveBuffer); s.Sign (keys, m_ReceiveBuffer);
size_t paddingSize = signatureLen & 0x0F; // %16 size_t paddingSize = signatureLen & 0x0F; // %16
if (paddingSize > 0) signatureLen += (16 - paddingSize); if (paddingSize > 0) signatureLen += (16 - paddingSize);
m_Encryption.Encrypt (m_ReceiveBuffer, signatureLen, m_ReceiveBuffer); m_Encryption.Encrypt (m_ReceiveBuffer, signatureLen, m_ReceiveBuffer);
boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (), boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase4Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); std::bind(&NTCPSession::HandlePhase4Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
} }
void NTCPSession::HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred) void NTCPSession::HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{ {
(void) bytes_transferred; (void) bytes_transferred;
if (ecode) if (ecode)
{ {
LogPrint (eLogWarning, "NTCP: Couldn't send Phase 4 message: ", ecode.message ()); LogPrint (eLogWarning, "NTCP: Couldn't send Phase 4 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
Terminate (); Terminate ();
@ -478,7 +479,7 @@ namespace transport
void NTCPSession::HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA) void NTCPSession::HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA)
{ {
if (ecode) if (ecode)
{ {
LogPrint (eLogError, "NTCP: Phase 4 read error: ", ecode.message (), ". Check your clock"); LogPrint (eLogError, "NTCP: Phase 4 read error: ", ecode.message (), ". Check your clock");
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
@ -673,13 +674,13 @@ namespace transport
m_NextMessage = nullptr; m_NextMessage = nullptr;
} }
return true; return true;
} }
void NTCPSession::Send (std::shared_ptr<i2p::I2NPMessage> msg) void NTCPSession::Send (std::shared_ptr<i2p::I2NPMessage> msg)
{ {
m_IsSending = true; m_IsSending = true;
boost::asio::async_write (m_Socket, CreateMsgBuffer (msg), boost::asio::transfer_all (), boost::asio::async_write (m_Socket, CreateMsgBuffer (msg), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector<std::shared_ptr<I2NPMessage> >{ msg })); std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector<std::shared_ptr<I2NPMessage> >{ msg }));
} }
boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg) boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg)
@ -726,7 +727,7 @@ namespace transport
for (const auto& it: msgs) for (const auto& it: msgs)
bufs.push_back (CreateMsgBuffer (it)); bufs.push_back (CreateMsgBuffer (it));
boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (), boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (),
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs)); std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs));
} }
void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs) void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs)
@ -734,7 +735,7 @@ namespace transport
(void) msgs; (void) msgs;
m_IsSending = false; m_IsSending = false;
if (ecode) if (ecode)
{ {
LogPrint (eLogWarning, "NTCP: Couldn't send msgs: ", ecode.message ()); LogPrint (eLogWarning, "NTCP: Couldn't send msgs: ", ecode.message ());
// we shouldn't call Terminate () here, because HandleReceive takes care // we shouldn't call Terminate () here, because HandleReceive takes care
// TODO: 'delete this' statement in Terminate () must be eliminated later // TODO: 'delete this' statement in Terminate () must be eliminated later
@ -788,7 +789,8 @@ namespace transport
//----------------------------------------- //-----------------------------------------
NTCPServer::NTCPServer (): NTCPServer::NTCPServer ():
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
m_TerminationTimer (m_Service), m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr) m_TerminationTimer (m_Service), m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr),
m_ProxyType(eNoProxy), m_Resolver(m_Service), m_ProxyEndpoint(nullptr)
{ {
} }
@ -803,48 +805,63 @@ namespace transport
{ {
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&NTCPServer::Run, this)); m_Thread = new std::thread (std::bind (&NTCPServer::Run, this));
// create acceptors // we are using a proxy, don't create any acceptors
auto& addresses = context.GetRouterInfo ().GetAddresses (); if(UsingProxy())
for (const auto& address: addresses)
{ {
if (!address) continue; // TODO: resolve proxy until it is resolved
if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP) boost::asio::ip::tcp::resolver::query q(m_ProxyAddress, std::to_string(m_ProxyPort));
boost::system::error_code e;
auto itr = m_Resolver.resolve(q, e);
if(e)
{ {
if (address->host.is_v4()) LogPrint(eLogError, "NTCP: Failed to resolve proxy ", e.message());
}
else
{
m_ProxyEndpoint = new boost::asio::ip::tcp::endpoint(*itr);
}
}
else
{
// create acceptors
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address: addresses)
{
if (!address) continue;
if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
{ {
try if (address->host.is_v4())
{ {
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service, try
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port)); {
} catch ( std::exception & ex ) { m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port));
/** fail to bind ip4 */ } catch ( std::exception & ex ) {
LogPrint(eLogError, "NTCP: Failed to bind to ip4 port ",address->port, ex.what()); /** fail to bind ip4 */
continue; LogPrint(eLogError, "NTCP: Failed to bind to ip4 port ",address->port, ex.what());
continue;
}
LogPrint (eLogInfo, "NTCP: Start listening TCP port ", address->port);
auto conn = std::make_shared<NTCPSession>(*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this, conn, std::placeholders::_1));
} }
else if (address->host.is_v6() && context.SupportsV6 ())
LogPrint (eLogInfo, "NTCP: Start listening TCP port ", address->port);
auto conn = std::make_shared<NTCPSession>(*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
conn, std::placeholders::_1));
}
else if (address->host.is_v6() && context.SupportsV6 ())
{
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
try
{ {
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6()); m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true)); try
{
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port)); m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCPV6Acceptor->listen (); m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
LogPrint (eLogInfo, "NTCP: Start listening V6 TCP port ", address->port); m_NTCPV6Acceptor->listen ();
auto conn = std::make_shared<NTCPSession> (*this);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, LogPrint (eLogInfo, "NTCP: Start listening V6 TCP port ", address->port);
this, conn, std::placeholders::_1)); auto conn = std::make_shared<NTCPSession> (*this);
} catch ( std::exception & ex ) { m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, this, conn, std::placeholders::_1));
LogPrint(eLogError, "NTCP: failed to bind to ip6 port ", address->port); } catch ( std::exception & ex ) {
continue; LogPrint(eLogError, "NTCP: failed to bind to ip6 port ", address->port);
continue;
}
} }
} }
} }
@ -869,14 +886,14 @@ namespace transport
{ {
m_IsRunning = false; m_IsRunning = false;
m_TerminationTimer.cancel (); m_TerminationTimer.cancel ();
if (m_NTCPAcceptor) if (m_NTCPAcceptor)
{ {
delete m_NTCPAcceptor; delete m_NTCPAcceptor;
m_NTCPAcceptor = nullptr; m_NTCPAcceptor = nullptr;
} }
if (m_NTCPV6Acceptor) if (m_NTCPV6Acceptor)
{ {
delete m_NTCPV6Acceptor; delete m_NTCPV6Acceptor;
m_NTCPV6Acceptor = nullptr; m_NTCPV6Acceptor = nullptr;
} }
m_Service.stop (); m_Service.stop ();
@ -886,6 +903,11 @@ namespace transport
delete m_Thread; delete m_Thread;
m_Thread = nullptr; m_Thread = nullptr;
} }
if(m_ProxyEndpoint)
{
delete m_ProxyEndpoint;
m_ProxyEndpoint = nullptr;
}
} }
} }
@ -956,7 +978,7 @@ namespace transport
if (error != boost::asio::error::operation_aborted) if (error != boost::asio::error::operation_aborted)
{ {
conn = std::make_shared<NTCPSession> (*this); conn = std::make_shared<NTCPSession> (*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this, m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
conn, std::placeholders::_1)); conn, std::placeholders::_1));
} }
@ -983,31 +1005,56 @@ namespace transport
if (error != boost::asio::error::operation_aborted) if (error != boost::asio::error::operation_aborted)
{ {
conn = std::make_shared<NTCPSession> (*this); conn = std::make_shared<NTCPSession> (*this);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, this, m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, this,
conn, std::placeholders::_1)); conn, std::placeholders::_1));
} }
} }
void NTCPServer::Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn) void NTCPServer::Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCPSession> conn)
{ {
LogPrint (eLogDebug, "NTCP: Connecting to ", address ,":", port); LogPrint (eLogDebug, "NTCP: Connecting to ", address ,":", port);
m_Service.post([=]() m_Service.post([=]() {
{
if (this->AddNTCPSession (conn)) if (this->AddNTCPSession (conn))
{ {
auto timer = std::make_shared<boost::asio::deadline_timer>(m_Service); auto timer = std::make_shared<boost::asio::deadline_timer>(m_Service);
timer->expires_from_now (boost::posix_time::seconds(NTCP_CONNECT_TIMEOUT)); timer->expires_from_now (boost::posix_time::seconds(NTCP_CONNECT_TIMEOUT));
timer->async_wait ([conn](const boost::system::error_code& ecode) timer->async_wait ([conn](const boost::system::error_code& ecode) {
if (ecode != boost::asio::error::operation_aborted)
{ {
if (ecode != boost::asio::error::operation_aborted) LogPrint (eLogInfo, "NTCP: Not connected in ", NTCP_CONNECT_TIMEOUT, " seconds");
{ conn->Terminate ();
LogPrint (eLogInfo, "NTCP: Not connected in ", NTCP_CONNECT_TIMEOUT, " seconds"); }
conn->Terminate (); });
} conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn, timer));
}); }
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), });
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn, timer)); }
void NTCPServer::ConnectWithProxy (const std::string& host, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<NTCPSession> conn)
{
if(m_ProxyEndpoint == nullptr)
{
return;
}
m_Service.post([=]() {
if (this->AddNTCPSession (conn))
{
auto timer = std::make_shared<boost::asio::deadline_timer>(m_Service);
auto timeout = NTCP_CONNECT_TIMEOUT * 5;
conn->SetTerminationTimeout(timeout * 2);
timer->expires_from_now (boost::posix_time::seconds(timeout));
timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) {
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogInfo, "NTCP: Not connected in ", timeout, " seconds");
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate ();
}
});
conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCPServer::HandleProxyConnect, this, std::placeholders::_1, conn, timer, host, port, addrtype));
} }
}); });
} }
@ -1016,7 +1063,7 @@ namespace transport
{ {
timer->cancel (); timer->cancel ();
if (ecode) if (ecode)
{ {
LogPrint (eLogInfo, "NTCP: Connect error ", ecode.message ()); LogPrint (eLogInfo, "NTCP: Connect error ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
@ -1031,6 +1078,191 @@ namespace transport
} }
} }
void NTCPServer::UseProxy(ProxyType proxytype, const std::string & addr, uint16_t port)
{
m_ProxyType = proxytype;
m_ProxyAddress = addr;
m_ProxyPort = port;
}
void NTCPServer::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype)
{
if(ecode)
{
LogPrint(eLogWarning, "NTCP: failed to connect to proxy ", ecode.message());
timer->cancel();
conn->Terminate();
return;
}
if(m_ProxyType == eSocksProxy)
{
// TODO: support username/password auth etc
uint8_t buff[3] = {0x05, 0x01, 0x00};
boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(), [=] (const boost::system::error_code & ec, std::size_t transferred) {
(void) transferred;
if(ec)
{
LogPrint(eLogWarning, "NTCP: socks5 write error ", ec.message());
}
});
uint8_t readbuff[2];
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff, 2), [=](const boost::system::error_code & ec, std::size_t transferred) {
if(ec)
{
LogPrint(eLogError, "NTCP: socks5 read error ", ec.message());
timer->cancel();
conn->Terminate();
return;
}
else if(transferred == 2)
{
if(readbuff[1] == 0x00)
{
AfterSocksHandshake(conn, timer, host, port, addrtype);
return;
}
else if (readbuff[1] == 0xff)
{
LogPrint(eLogError, "NTCP: socks5 proxy rejected authentication");
timer->cancel();
conn->Terminate();
return;
}
}
LogPrint(eLogError, "NTCP: socks5 server gave invalid response");
timer->cancel();
conn->Terminate();
});
}
else if(m_ProxyType == eHTTPProxy)
{
i2p::http::HTTPReq req;
req.method = "CONNECT";
req.version ="HTTP/1.1";
if(addrtype == eIP6Address)
req.uri = "[" + host + "]:" + std::to_string(port);
else
req.uri = host + ":" + std::to_string(port);
boost::asio::streambuf writebuff;
std::ostream out(&writebuff);
out << req.to_string();
boost::asio::async_write(conn->GetSocket(), writebuff.data(), boost::asio::transfer_all(), [=](const boost::system::error_code & ec, std::size_t transferred) {
(void) transferred;
if(ec)
LogPrint(eLogError, "NTCP: http proxy write error ", ec.message());
});
boost::asio::streambuf * readbuff = new boost::asio::streambuf;
boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n", [=] (const boost::system::error_code & ec, std::size_t transferred) {
if(ec)
{
LogPrint(eLogError, "NTCP: http proxy read error ", ec.message());
timer->cancel();
conn->Terminate();
}
else
{
readbuff->commit(transferred);
i2p::http::HTTPRes res;
if(res.parse(boost::asio::buffer_cast<const char*>(readbuff->data()), readbuff->size()) > 0)
{
if(res.code == 200)
{
timer->cancel();
conn->ClientLogin();
delete readbuff;
return;
}
else
{
LogPrint(eLogError, "NTCP: http proxy rejected request ", res.code);
}
}
else
LogPrint(eLogError, "NTCP: http proxy gave malformed response");
timer->cancel();
conn->Terminate();
delete readbuff;
}
});
}
else
LogPrint(eLogError, "NTCP: unknown proxy type, invalid state");
}
void NTCPServer::AfterSocksHandshake(std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype)
{
// build request
size_t sz = 0;
uint8_t buff[256];
uint8_t readbuff[256];
buff[0] = 0x05;
buff[1] = 0x01;
buff[2] = 0x00;
if(addrtype == eIP4Address)
{
buff[3] = 0x01;
auto addr = boost::asio::ip::address::from_string(host).to_v4();
auto addrbytes = addr.to_bytes();
auto addrsize = addrbytes.size();
memcpy(buff+4, addrbytes.data(), addrsize);
}
else if (addrtype == eIP6Address)
{
buff[3] = 0x04;
auto addr = boost::asio::ip::address::from_string(host).to_v6();
auto addrbytes = addr.to_bytes();
auto addrsize = addrbytes.size();
memcpy(buff+4, addrbytes.data(), addrsize);
}
else if (addrtype == eHostname)
{
buff[3] = 0x03;
size_t addrsize = host.size();
sz = addrsize + 1 + 4;
if (2 + sz > sizeof(buff))
{
// too big
return;
}
buff[4] = (uint8_t) addrsize;
memcpy(buff+4, host.c_str(), addrsize);
}
htobe16buf(buff+sz, port);
sz += 2;
boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, sz), boost::asio::transfer_all(), [=](const boost::system::error_code & ec, std::size_t written) {
if(ec)
{
LogPrint(eLogError, "NTCP: failed to write handshake to socks proxy ", ec.message());
return;
}
});
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff, sz), [=](const boost::system::error_code & e, std::size_t transferred) {
if(e)
{
LogPrint(eLogError, "NTCP: socks proxy read error ", e.message());
}
else if(transferred == sz)
{
if( readbuff[1] == 0x00)
{
timer->cancel();
conn->ClientLogin();
return;
}
}
if(!e)
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
timer->cancel();
conn->Terminate();
});
}
void NTCPServer::ScheduleTermination () void NTCPServer::ScheduleTermination ()
{ {
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_CHECK_TIMEOUT)); m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_CHECK_TIMEOUT));
@ -1045,15 +1277,14 @@ namespace transport
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
// established // established
for (auto& it: m_NTCPSessions) for (auto& it: m_NTCPSessions)
if (it.second->IsTerminationTimeoutExpired (ts)) if (it.second->IsTerminationTimeoutExpired (ts))
{ {
auto session = it.second; auto session = it.second;
// Termniate modifies m_NTCPSession, so we postpone it // Termniate modifies m_NTCPSession, so we postpone it
m_Service.post ([session] m_Service.post ([session] {
{
LogPrint (eLogDebug, "NTCP: No activity for ", session->GetTerminationTimeout (), " seconds"); LogPrint (eLogDebug, "NTCP: No activity for ", session->GetTerminationTimeout (), " seconds");
session->Terminate (); session->Terminate ();
}); });
} }
// pending // pending
for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();) for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();)

34
libi2pd/NTCPSession.h

@ -131,6 +131,21 @@ namespace transport
{ {
public: public:
enum RemoteAddressType
{
eIP4Address,
eIP6Address,
eHostname
};
enum ProxyType
{
eNoProxy,
eSocksProxy,
eHTTPProxy
};
NTCPServer (); NTCPServer ();
~NTCPServer (); ~NTCPServer ();
@ -140,10 +155,15 @@ namespace transport
bool AddNTCPSession (std::shared_ptr<NTCPSession> session); bool AddNTCPSession (std::shared_ptr<NTCPSession> session);
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session); void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident); std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn); void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<NTCPSession> conn);
void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCPSession> conn);
bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; };
bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; };
bool NetworkIsReady() const { return IsBoundV4() || IsBoundV6() || UsingProxy(); };
bool UsingProxy() const { return m_ProxyType != eNoProxy; };
bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; }; void UseProxy(ProxyType proxy, const std::string & address, uint16_t port);
bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; };
boost::asio::io_service& GetService () { return m_Service; }; boost::asio::io_service& GetService () { return m_Service; };
@ -155,6 +175,9 @@ namespace transport
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer); void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype);
void AfterSocksHandshake(std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype);
// timer // timer
void ScheduleTermination (); void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode); void HandleTerminationTimer (const boost::system::error_code& ecode);
@ -170,6 +193,11 @@ namespace transport
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions; // access from m_Thread only std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions; // access from m_Thread only
std::list<std::shared_ptr<NTCPSession> > m_PendingIncomingSessions; std::list<std::shared_ptr<NTCPSession> > m_PendingIncomingSessions;
ProxyType m_ProxyType;
std::string m_ProxyAddress;
uint16_t m_ProxyPort;
boost::asio::ip::tcp::resolver m_Resolver;
boost::asio::ip::tcp::endpoint * m_ProxyEndpoint;
public: public:
// for HTTP/I2PControl // for HTTP/I2PControl

62
libi2pd/Transports.cpp

@ -5,6 +5,7 @@
#include "NetDb.hpp" #include "NetDb.hpp"
#include "Transports.h" #include "Transports.h"
#include "Config.h" #include "Config.h"
#include "HTTP.h"
#ifdef WITH_EVENTS #ifdef WITH_EVENTS
#include "Event.h" #include "Event.h"
#include "util.h" #include "util.h"
@ -144,6 +145,39 @@ namespace transport
m_DHKeysPairSupplier.Start (); m_DHKeysPairSupplier.Start ();
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this)); m_Thread = new std::thread (std::bind (&Transports::Run, this));
std::string ntcpproxy; i2p::config::GetOption("ntcpproxy", ntcpproxy);
i2p::http::URL proxyurl;
if(ntcpproxy.size() && enableNTCP)
{
if(proxyurl.parse(ntcpproxy))
{
if(proxyurl.schema == "socks" || proxyurl.schema == "http")
{
m_NTCPServer = new NTCPServer();
NTCPServer::ProxyType proxytype = NTCPServer::eSocksProxy;
if (proxyurl.schema == "http")
proxytype = NTCPServer::eHTTPProxy;
m_NTCPServer->UseProxy(proxytype, proxyurl.host, proxyurl.port) ;
m_NTCPServer->Start();
if(!m_NTCPServer->NetworkIsReady())
{
LogPrint(eLogError, "Transports: NTCP failed to start with proxy");
m_NTCPServer->Stop();
delete m_NTCPServer;
m_NTCPServer = nullptr;
}
}
else
LogPrint(eLogError, "Transports: unsupported NTCP proxy URL ", ntcpproxy);
}
else
LogPrint(eLogError, "Transports: invalid NTCP proxy url ", ntcpproxy);
return;
}
// create acceptors // create acceptors
auto& addresses = context.GetRouterInfo ().GetAddresses (); auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address : addresses) for (const auto& address : addresses)
@ -343,14 +377,25 @@ namespace transport
if (!address->host.is_unspecified ()) // we have address now if (!address->host.is_unspecified ()) // we have address now
#else #else
boost::system::error_code ecode; boost::system::error_code ecode;
address->host.to_string (ecode); address->host.to_string (ecode);
if (!ecode) if (!ecode)
#endif #endif
{ {
if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ()) if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ())
{ {
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router); auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
m_NTCPServer->Connect (address->host, address->port, s); if(m_NTCPServer->UsingProxy())
{
NTCPServer::RemoteAddressType remote = NTCPServer::eIP4Address;
std::string addr = address->host.to_string();
if(address->host.is_v6())
remote = NTCPServer::eIP6Address;
m_NTCPServer->ConnectWithProxy(addr, address->port, remote, s);
}
else
m_NTCPServer->Connect (address->host, address->port, s);
return true; return true;
} }
} }
@ -358,8 +403,16 @@ namespace transport
{ {
if (address->addressString.length () > 0) // trying to resolve if (address->addressString.length () > 0) // trying to resolve
{ {
LogPrint (eLogDebug, "Transports: Resolving NTCP ", address->addressString); if(m_NTCPServer->UsingProxy())
NTCPResolve (address->addressString, ident); {
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
m_NTCPServer->ConnectWithProxy(address->addressString, address->port, NTCPServer::eHostname, s);
}
else
{
LogPrint (eLogDebug, "Transports: Resolving NTCP ", address->addressString);
NTCPResolve (address->addressString, ident);
}
return true; return true;
} }
} }
@ -814,4 +867,3 @@ namespace transport
} }
} }
} }

Loading…
Cancel
Save