diff --git a/libi2pd/NTCPSession.cpp b/libi2pd/NTCPSession.cpp index 96d95ab6..c7fc2d6b 100644 --- a/libi2pd/NTCPSession.cpp +++ b/libi2pd/NTCPSession.cpp @@ -12,6 +12,7 @@ #include "Transports.h" #include "NetDb.hpp" #include "NTCPSession.h" +#include "HTTP.h" #ifdef WITH_EVENTS #include "Event.h" #endif @@ -789,7 +790,7 @@ namespace transport NTCPServer::NTCPServer (): m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_TerminationTimer (m_Service), m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr), - m_UseSocks(false), m_Resolver(m_Service), m_SocksEndpoint(nullptr) + m_ProxyType(eNoProxy), m_Resolver(m_Service), m_ProxyEndpoint(nullptr) { } @@ -804,11 +805,11 @@ namespace transport { m_IsRunning = true; m_Thread = new std::thread (std::bind (&NTCPServer::Run, this)); - // we are using a socks proxy, don't create any acceptors - if(m_UseSocks) + // we are using a proxy, don't create any acceptors + if(UsingProxy()) { - // TODO: resolve socks proxy until it is resolved - boost::asio::ip::tcp::resolver::query q(m_SocksAddress, std::to_string(m_SocksPort)); + // 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) @@ -817,7 +818,7 @@ namespace transport } else { - m_SocksEndpoint = new boost::asio::ip::tcp::endpoint(*itr); + m_ProxyEndpoint = new boost::asio::ip::tcp::endpoint(*itr); } } else @@ -902,10 +903,10 @@ namespace transport delete m_Thread; m_Thread = nullptr; } - if(m_SocksEndpoint) + if(m_ProxyEndpoint) { - delete m_SocksEndpoint; - m_SocksEndpoint = nullptr; + delete m_ProxyEndpoint; + m_ProxyEndpoint = nullptr; } } } @@ -1031,13 +1032,12 @@ namespace transport }); } - void NTCPServer::ConnectSocks (const std::string& host, uint16_t port, std::shared_ptr conn) + void NTCPServer::ConnectWithProxy (const std::string& host, uint16_t port, RemoteAddressType addrtype, std::shared_ptr conn) { - if(m_SocksEndpoint == nullptr) + if(m_ProxyEndpoint == nullptr) { return; } - LogPrint (eLogDebug, "NTCP: Connecting to ", host ,":", port, " Via socks proxy"); m_Service.post([=]() { if (this->AddNTCPSession (conn)) { @@ -1054,7 +1054,7 @@ namespace transport conn->Terminate (); } }); - conn->GetSocket ().async_connect (*m_SocksEndpoint, std::bind (&NTCPServer::HandleSocksConnect, this, std::placeholders::_1, conn, timer, host, port)); + conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCPServer::HandleProxyConnect, this, std::placeholders::_1, conn, timer, host, port, addrtype)); } }); } @@ -1078,46 +1078,161 @@ namespace transport } } - void NTCPServer::UseSocksProxy(const std::string & addr, uint16_t port) + void NTCPServer::UseProxy(ProxyType proxytype, const std::string & addr, uint16_t port) { - m_UseSocks = true; - m_SocksAddress = addr; - m_SocksPort = port; + m_ProxyType = proxytype; + m_ProxyAddress = addr; + m_ProxyPort = port; } - void NTCPServer::HandleSocksConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port) + void NTCPServer::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype) { if(ecode) { - LogPrint(eLogInfo, "NTCP: Socks Proxy connect error ", ecode.message()); + LogPrint(eLogWarning, "NTCP: failed to connect to proxy ", ecode.message()); + timer->cancel(); + conn->Terminate(); return; } - LogPrint(eLogDebug, "NTCP: connecting via socks proxy to ",host, ":", port); - uint8_t readbuff[8]; - // build socks4a request - size_t addrsz = host.size(); - size_t sz = 8 + 1 + 4 + addrsz + 1; - uint8_t buff[256]; - if(sz > 256) + if(m_ProxyType == eSocksProxy) { - // hostname too big - return; + // 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(); + }); } - buff[0] = 4; - buff[1] = 1; - htobe16buf(buff+2, port); - buff[4] = 0; - buff[5] = 0; - buff[6] = 0; - buff[7] = 1; - buff[8] = 105; // i - buff[9] = 50; // 2 - buff[10] = 112; // p - buff[11] = 100; // d - buff[12] = 0; - memcpy(buff+12, host.c_str(), addrsz); - buff[12+addrsz] = 0; + 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, 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; + boost::asio::async_read_until(conn->GetSocket(), readbuff, "\r\n\r\n", [=, &readbuff] (const boost::system::error_code & ec, std::size_t transferred) { + (void) 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(readbuff.data()), readbuff.size()) > 0) + { + if(res.code == 200) + { + timer->cancel(); + conn->ClientLogin(); + return; + } + else + { + LogPrint(eLogError, "NTCP: http proxy rejected request ", res.code); + } + } + else + LogPrint(eLogError, "NTCP: http proxy gave malformed response"); + timer->cancel(); + conn->Terminate(); + } + }); + } + else + LogPrint(eLogError, "NTCP: unknown proxy type, invalid state"); + } + + void NTCPServer::AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr 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) { @@ -1126,21 +1241,24 @@ namespace transport } }); - boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff, 8), [=](const boost::system::error_code & e, std::size_t transferred) { - if(transferred == 8) + 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) { - if( readbuff[1] == 0x5a) - { - timer->cancel(); - conn->ClientLogin(); - } - else - { - timer->cancel(); - conn->Terminate(); - i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); - } + timer->cancel(); + conn->ClientLogin(); + return; + } } + if(!e) + i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); + timer->cancel(); + conn->Terminate(); }); } diff --git a/libi2pd/NTCPSession.h b/libi2pd/NTCPSession.h index e7bd53fb..a45f06f7 100644 --- a/libi2pd/NTCPSession.h +++ b/libi2pd/NTCPSession.h @@ -131,6 +131,21 @@ namespace transport { public: + enum RemoteAddressType + { + eIP4Address, + eIP6Address, + eHostname + }; + + enum ProxyType + { + eNoProxy, + eSocksProxy, + eHTTPProxy + }; + + NTCPServer (); ~NTCPServer (); @@ -140,15 +155,15 @@ namespace transport bool AddNTCPSession (std::shared_ptr session); void RemoveNTCPSession (std::shared_ptr session); std::shared_ptr FindNTCPSession (const i2p::data::IdentHash& ident); - void ConnectSocks (const std::string& addr, uint16_t port, std::shared_ptr conn); + void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr conn); void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn); bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; }; bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; }; - bool NetworkIsReady() const { return IsBoundV4() || IsBoundV6() || m_UseSocks; }; - bool UsingSocksProxy() const { return m_UseSocks; }; + bool NetworkIsReady() const { return IsBoundV4() || IsBoundV6() || UsingProxy(); }; + bool UsingProxy() const { return m_ProxyType != eNoProxy; }; - void UseSocksProxy(const std::string & address, uint16_t port); + void UseProxy(ProxyType proxy, const std::string & address, uint16_t port); boost::asio::io_service& GetService () { return m_Service; }; @@ -160,7 +175,8 @@ namespace transport void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); - void HandleSocksConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port); + void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype); + void AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype); // timer void ScheduleTermination (); @@ -177,11 +193,11 @@ namespace transport std::map > m_NTCPSessions; // access from m_Thread only std::list > m_PendingIncomingSessions; - bool m_UseSocks; - std::string m_SocksAddress; - uint16_t m_SocksPort; + ProxyType m_ProxyType; + std::string m_ProxyAddress; + uint16_t m_ProxyPort; boost::asio::ip::tcp::resolver m_Resolver; - boost::asio::ip::tcp::endpoint * m_SocksEndpoint; + boost::asio::ip::tcp::endpoint * m_ProxyEndpoint; public: // for HTTP/I2PControl diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 4091c413..21007408 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -151,14 +151,20 @@ namespace transport { if(proxyurl.parse(ntcpproxy)) { - if(proxyurl.schema == "socks") + if(proxyurl.schema == "socks" || proxyurl.schema == "http") { m_NTCPServer = new NTCPServer(); - m_NTCPServer->UseSocksProxy(proxyurl.host, proxyurl.port) ; + + 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 socks proxy"); + LogPrint(eLogError, "Transports: NTCP failed to start with proxy"); m_NTCPServer->Stop(); delete m_NTCPServer; m_NTCPServer = nullptr; @@ -169,7 +175,6 @@ namespace transport } else LogPrint(eLogError, "Transports: invalid NTCP proxy url ", ntcpproxy); - return; } @@ -379,10 +384,15 @@ namespace transport if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ()) { auto s = std::make_shared (*m_NTCPServer, peer.router); - if(m_NTCPServer->UsingSocksProxy()) + if(m_NTCPServer->UsingProxy()) { + NTCPServer::RemoteAddressType remote = NTCPServer::eIP4Address; std::string addr = address->host.to_string(); - m_NTCPServer->ConnectSocks(addr, address->port, s); + + 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); @@ -393,10 +403,10 @@ namespace transport { if (address->addressString.length () > 0) // trying to resolve { - if(m_NTCPServer->UsingSocksProxy()) + if(m_NTCPServer->UsingProxy()) { auto s = std::make_shared (*m_NTCPServer, peer.router); - m_NTCPServer->ConnectSocks(address->addressString, address->port, s); + m_NTCPServer->ConnectWithProxy(address->addressString, address->port, NTCPServer::eHostname, s); } else {