Browse Source

add ntcp2proxy support

pull/1478/head
unlnown542a 5 years ago
parent
commit
4f0da87a7a
  1. 1
      libi2pd/Config.cpp
  2. 317
      libi2pd/NTCP2.cpp
  3. 29
      libi2pd/NTCP2.h
  4. 70
      libi2pd/Transports.cpp

1
libi2pd/Config.cpp

@ -61,6 +61,7 @@ namespace config {
("ntcp", value<bool>()->default_value(false), "Enable NTCP transport (default: disabled)") ("ntcp", value<bool>()->default_value(false), "Enable NTCP transport (default: disabled)")
("ssu", value<bool>()->default_value(true), "Enable SSU transport (default: enabled)") ("ssu", value<bool>()->default_value(true), "Enable SSU transport (default: enabled)")
("ntcpproxy", value<std::string>()->default_value(""), "Proxy URL for NTCP transport") ("ntcpproxy", value<std::string>()->default_value(""), "Proxy URL for NTCP transport")
("ntcp2proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 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", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)")

317
libi2pd/NTCP2.cpp

@ -20,6 +20,8 @@
#include "Transports.h" #include "Transports.h"
#include "NetDb.hpp" #include "NetDb.hpp"
#include "NTCP2.h" #include "NTCP2.h"
#include "HTTP.h"
#include "util.h"
namespace i2p namespace i2p
{ {
@ -375,6 +377,78 @@ namespace transport
} }
} }
void NTCP2Server::AfterSocksHandshake(std::shared_ptr<NTCP2Session> 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+5, 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, "NTCP2: failed to write handshake to socks proxy ", ec.message());
return;
}
});
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff, 10), [=](const boost::system::error_code & e, std::size_t transferred) {
if(e)
{
LogPrint(eLogError, "NTCP2: 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();
});
}
NTCP2Session::~NTCP2Session () NTCP2Session::~NTCP2Session ()
{ {
delete[] m_NextReceivedBuffer; delete[] m_NextReceivedBuffer;
@ -1094,6 +1168,150 @@ namespace transport
EncryptAndSendNextBuffer (payloadLen); EncryptAndSendNextBuffer (payloadLen);
} }
void NTCP2Server::UseProxy(ProxyType proxytype, const std::string & addr, uint16_t port)
{
m_ProxyType = proxytype;
m_ProxyAddress = addr;
m_ProxyPort = port;
}
void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype)
{
if(ecode)
{
LogPrint(eLogWarning, "NTCP2: 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, "NTCP2: 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)
{
LogPrint(eLogError, "NTCP2: ", transferred);
if(ec)
{
LogPrint(eLogError, "NTCP2: socks5 read error ", ec.message());
timer->cancel();
conn->Terminate();
return;
}
else if(transferred == 2)
{
if(readbuff[1] == 0xba)
{
AfterSocksHandshake(conn, timer, host, port, addrtype);
return;
}
else if (readbuff[1] == 0xff)
{
LogPrint(eLogError, "NTCP2: socks5 proxy rejected authentication");
timer->cancel();
conn->Terminate();
return;
}
LogPrint(eLogError, "NTCP2:", readbuff[1]);
}
LogPrint(eLogError, "NTCP2: 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, "NTCP2: 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, "NTCP2: 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, "NTCP2: http proxy rejected request ", res.code);
}
}
else
LogPrint(eLogError, "NTCP2: http proxy gave malformed response");
timer->cancel();
conn->Terminate();
delete readbuff;
}
});
}
else
LogPrint(eLogError, "NTCP2: unknown proxy type, invalid state");
}
void NTCP2Server::ConnectWithProxy (const std::string& host, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<NTCP2Session> conn)
{
if(m_ProxyEndpoint == nullptr)
{
return;
}
GetService().post([=]() {
if (this->AddNTCP2Session (conn))
{
auto timer = std::make_shared<boost::asio::deadline_timer>(GetService());
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, "NTCP2: Not connected in ", timeout, " seconds");
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate ();
}
});
conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer, host, port, addrtype));
}
});
}
void NTCP2Session::SendTermination (NTCP2TerminationReason reason) void NTCP2Session::SendTermination (NTCP2TerminationReason reason)
{ {
if (!m_SendKey || !m_SendSipKey) return; if (!m_SendKey || !m_SendSipKey) return;
@ -1142,6 +1360,7 @@ namespace transport
NTCP2Server::NTCP2Server (): NTCP2Server::NTCP2Server ():
RunnableServiceWithWork ("NTCP2"), RunnableServiceWithWork ("NTCP2"),
m_ProxyType(eNoProxy), m_Resolver(GetService ()), m_ProxyEndpoint(nullptr),
m_TerminationTimer (GetService ()) m_TerminationTimer (GetService ())
{ {
} }
@ -1156,45 +1375,69 @@ namespace transport
if (!IsRunning ()) if (!IsRunning ())
{ {
StartIOService (); StartIOService ();
auto& addresses = context.GetRouterInfo ().GetAddresses (); if(UsingProxy())
for (const auto& address: addresses)
{ {
if (!address) continue; LogPrint(eLogError, "NTCP2: USING PROXY ");
if (address->IsPublishedNTCP2 ()) // TODO: resolve proxy until it is resolved
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)
{
LogPrint(eLogError, "NTCP2: Failed to resolve proxy ", e.message());
}
else
{ {
if (address->host.is_v4()) m_ProxyEndpoint = new boost::asio::ip::tcp::endpoint(*itr);
if (m_ProxyEndpoint)
{ {
try LogPrint(eLogError, "NTCP2: m_ProxyEndpoint %s", m_ProxyEndpoint);
{
m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port)));
}
catch ( std::exception & ex )
{
LogPrint(eLogError, "NTCP2: Failed to bind to ip4 port ",address->port, ex.what());
continue;
}
LogPrint (eLogInfo, "NTCP2: Start listening TCP port ", address->port);
auto conn = std::make_shared<NTCP2Session>(*this);
m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1));
} }
else if (address->host.is_v6() && context.SupportsV6 ()) }
}
else
{
LogPrint(eLogError, "NTCP2: NOTUSING PROXY ");
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address: addresses)
{
if (!address) continue;
if (address->IsPublishedNTCP2 ())
{ {
m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ())); if (address->host.is_v4())
try {
try
{
m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port)));
}
catch ( std::exception & ex )
{
LogPrint(eLogError, "NTCP2: Failed to bind to ip4 port ",address->port, ex.what());
continue;
}
LogPrint (eLogInfo, "NTCP2: Start listening TCP port ", address->port);
auto conn = std::make_shared<NTCP2Session>(*this);
m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1));
}
else if (address->host.is_v6() && context.SupportsV6 ())
{ {
m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ()));
m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); try
m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); {
m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port)); m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCP2V6Acceptor->listen (); m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true));
LogPrint (eLogInfo, "NTCP2: Start listening V6 TCP port ", address->port); m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
auto conn = std::make_shared<NTCP2Session> (*this); m_NTCP2V6Acceptor->listen ();
m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1));
} catch ( std::exception & ex ) { LogPrint (eLogInfo, "NTCP2: Start listening V6 TCP port ", address->port);
LogPrint(eLogError, "NTCP2: failed to bind to ip6 port ", address->port); auto conn = std::make_shared<NTCP2Session> (*this);
continue; m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1));
} catch ( std::exception & ex ) {
LogPrint(eLogError, "NTCP2: failed to bind to ip6 port ", address->port);
continue;
}
} }
} }
} }
@ -1216,7 +1459,14 @@ namespace transport
m_NTCP2Sessions.clear (); m_NTCP2Sessions.clear ();
if (IsRunning ()) if (IsRunning ())
{
m_TerminationTimer.cancel (); m_TerminationTimer.cancel ();
if(m_ProxyEndpoint)
{
delete m_ProxyEndpoint;
m_ProxyEndpoint = nullptr;
}
}
StopIOService (); StopIOService ();
} }
@ -1391,4 +1641,3 @@ namespace transport
} }
} }
} }

29
libi2pd/NTCP2.h

@ -126,6 +126,7 @@ namespace transport
{ {
public: public:
NTCP2Session (NTCP2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr); NTCP2Session (NTCP2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
~NTCP2Session (); ~NTCP2Session ();
void Terminate (); void Terminate ();
@ -220,6 +221,20 @@ namespace transport
{ {
public: public:
enum RemoteAddressType
{
eIP4Address,
eIP6Address,
eHostname
};
enum ProxyType
{
eNoProxy,
eSocksProxy,
eHTTPProxy
};
NTCP2Server (); NTCP2Server ();
~NTCP2Server (); ~NTCP2Server ();
@ -231,14 +246,22 @@ namespace transport
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session); void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
std::shared_ptr<NTCP2Session> FindNTCP2Session (const i2p::data::IdentHash& ident); std::shared_ptr<NTCP2Session> FindNTCP2Session (const i2p::data::IdentHash& ident);
void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<NTCP2Session> conn);
void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCP2Session> conn); void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCP2Session> conn);
void AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype);
bool UsingProxy() const { return m_ProxyType != eNoProxy; };
void UseProxy(ProxyType proxy, const std::string & address, uint16_t port);
private: private:
void HandleAccept (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error); void HandleAccept (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
void HandleAcceptV6 (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error); void HandleAcceptV6 (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer); void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype);
// timer // timer
void ScheduleTermination (); void ScheduleTermination ();
@ -251,6 +274,12 @@ namespace transport
std::map<i2p::data::IdentHash, std::shared_ptr<NTCP2Session> > m_NTCP2Sessions; std::map<i2p::data::IdentHash, std::shared_ptr<NTCP2Session> > m_NTCP2Sessions;
std::list<std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions; std::list<std::shared_ptr<NTCP2Session> > 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

70
libi2pd/Transports.cpp

@ -157,6 +157,7 @@ namespace transport
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); std::string ntcpproxy; i2p::config::GetOption("ntcpproxy", ntcpproxy);
std::string ntcp2proxy; i2p::config::GetOption("ntcp2proxy", ntcp2proxy);
i2p::http::URL proxyurl; i2p::http::URL proxyurl;
uint16_t softLimit, hardLimit, threads; uint16_t softLimit, hardLimit, threads;
i2p::config::GetOption("limits.ntcpsoft", softLimit); i2p::config::GetOption("limits.ntcpsoft", softLimit);
@ -200,8 +201,31 @@ namespace transport
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2) if (ntcp2)
{ {
m_NTCP2Server = new NTCP2Server (); if(ntcp2proxy.size())
m_NTCP2Server->Start (); {
if(proxyurl.parse(ntcp2proxy))
{
if(proxyurl.schema == "socks" || proxyurl.schema == "http")
{
m_NTCP2Server = new NTCP2Server ();
NTCP2Server::ProxyType proxytype = NTCP2Server::eSocksProxy;
if (proxyurl.schema == "http")
proxytype = NTCP2Server::eHTTPProxy;
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port) ;
m_NTCP2Server->Start();
}
else
LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy);
}
else
LogPrint(eLogError, "Transports: invalid NTCP2 proxy url ", ntcp2proxy);
return;
}
// m_NTCP2Server = new NTCP2Server ();
// m_NTCP2Server->Start ();
} }
// create acceptors // create acceptors
@ -405,21 +429,33 @@ namespace transport
{ {
if (peer.router) // we have RI already if (peer.router) // we have RI already
{ {
if (!peer.numAttempts) // NTCP2 if (!peer.numAttempts) // NTCP2
{ {
peer.numAttempts++; peer.numAttempts++;
if (m_NTCP2Server) // we support NTCP2 if (m_NTCP2Server) // we support NTCP2
{ {
// NTCP2 have priority over NTCP // NTCP2 have priority over NTCP
auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only
if (address) if (address)
{ {
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router); auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router);
m_NTCP2Server->Connect (address->host, address->port, s);
return true; if(m_NTCP2Server->UsingProxy())
} {
} NTCP2Server::RemoteAddressType remote = NTCP2Server::eIP4Address;
} std::string addr = address->host.to_string();
if(address->host.is_v6())
remote = NTCP2Server::eIP6Address;
m_NTCP2Server->ConnectWithProxy(addr, address->port, remote, s);
}
else
m_NTCP2Server->Connect (address->host, address->port, s);
return true;
}
}
}
if (peer.numAttempts == 1) // NTCP1 if (peer.numAttempts == 1) // NTCP1
{ {
peer.numAttempts++; peer.numAttempts++;

Loading…
Cancel
Save