From e384ec32b83f9ef4ac1c1947840259e810005f76 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 15 Nov 2016 15:40:09 -0500 Subject: [PATCH 01/37] unbreak i2lua build --- Tunnel.cpp | 6 ++++++ TunnelPool.cpp | 10 ++++++++++ TunnelPool.h | 11 +++++++++++ 3 files changed, 27 insertions(+) diff --git a/Tunnel.cpp b/Tunnel.cpp index e1f5c035..e271052e 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -612,6 +612,7 @@ namespace tunnel for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();) { auto tunnel = it->second; + auto pool = tunnel->GetTunnelPool(); switch (tunnel->GetState ()) { case eTunnelStatePending: @@ -637,6 +638,8 @@ namespace tunnel #ifdef WITH_EVENTS EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed); #endif + // for i2lua + if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultTimeout); // delete it = pendingTunnels.erase (it); m_NumFailedTunnelCreations++; @@ -649,6 +652,9 @@ namespace tunnel #ifdef WITH_EVENTS EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed); #endif + // for i2lua + if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultRejected); + it = pendingTunnels.erase (it); m_NumFailedTunnelCreations++; break; diff --git a/TunnelPool.cpp b/TunnelPool.cpp index ccd4c12c..f8e34e7c 100644 --- a/TunnelPool.cpp +++ b/TunnelPool.cpp @@ -81,6 +81,8 @@ namespace tunnel } if (m_LocalDestination) m_LocalDestination->SetLeaseSetUpdated (); + + OnTunnelBuildResult(createdTunnel, eBuildResultOkay); } void TunnelPool::TunnelExpired (std::shared_ptr expiredTunnel) @@ -109,6 +111,8 @@ namespace tunnel std::unique_lock l(m_OutboundTunnelsMutex); m_OutboundTunnels.insert (createdTunnel); } + OnTunnelBuildResult(createdTunnel, eBuildResultOkay); + //CreatePairedInboundTunnel (createdTunnel); } @@ -576,5 +580,11 @@ namespace tunnel } return tun; } + + void TunnelPool::OnTunnelBuildResult(std::shared_ptr tunnel, TunnelBuildResult result) + { + auto peers = tunnel->GetPeers(); + if(m_CustomPeerSelector) m_CustomPeerSelector->OnBuildResult(peers, tunnel->IsInbound(), result); + } } } diff --git a/TunnelPool.h b/TunnelPool.h index 6a73bd67..9e2a3e24 100644 --- a/TunnelPool.h +++ b/TunnelPool.h @@ -23,12 +23,21 @@ namespace tunnel class InboundTunnel; class OutboundTunnel; + + enum TunnelBuildResult { + eBuildResultOkay, // tunnel was built okay + eBuildResultRejected, // tunnel build was explicitly rejected + eBuildResultTimeout // tunnel build timed out + }; + /** interface for custom tunnel peer selection algorithm */ struct ITunnelPeerSelector { typedef std::shared_ptr Peer; typedef std::vector TunnelPath; + virtual bool SelectPeers(TunnelPath & peers, int hops, bool isInbound) = 0; + virtual bool OnBuildResult(TunnelPath & peers, bool isInbound, TunnelBuildResult result) = 0; }; typedef std::shared_ptr TunnelPeerSelector; @@ -79,6 +88,8 @@ namespace tunnel /** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */ std::shared_ptr GetLowestLatencyInboundTunnel(std::shared_ptr exclude=nullptr) const; std::shared_ptr GetLowestLatencyOutboundTunnel(std::shared_ptr exclude=nullptr) const; + + void OnTunnelBuildResult(std::shared_ptr tunnel, TunnelBuildResult result); private: From e270f90f8d808deb994876c257448bc32a3673c0 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 17 Nov 2016 10:36:27 -0500 Subject: [PATCH 02/37] try fixing udp tunnel (probably broken) --- I2PTunnel.cpp | 124 +++++++++++++++++++++++++++++--------------------- I2PTunnel.h | 20 +++++--- 2 files changed, 86 insertions(+), 58 deletions(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 93bcff0c..01a3e7ca 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -531,36 +531,50 @@ namespace client void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) { std::lock_guard lock(m_SessionsMutex); - auto session = ObtainUDPSession(from, toPort, fromPort); - session->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); - session->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); + auto & session = ObtainUDPSession(from, toPort, fromPort); + session.IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); + session.LastActivity = i2p::util::GetMillisecondsSinceEpoch(); } void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) { std::lock_guard lock(m_SessionsMutex); uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); - std::remove_if(m_Sessions.begin(), m_Sessions.end(), [now, delta](const UDPSession * u) -> bool { - return now - u->LastActivity >= delta; + std::remove_if(m_Sessions.begin(), m_Sessions.end(), [now, delta](const UDPSession & u) -> bool { + return now - u.LastActivity >= delta; }); } - - UDPSession * I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort) + + void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) { + std::lock_guard lock(m_SessionsMutex); + uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); + std::vector removePorts; + for (const auto & s : m_Sessions) { + if (now - std::get<1>(s.second) >= delta) + removePorts.push_back(s.first); + } + for(auto port : removePorts) { + m_Sessions.erase(port); + } + } + + UDPSession & I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort) { auto ih = from.GetIdentHash(); - for ( UDPSession * s : m_Sessions ) + for ( UDPSession & s : m_Sessions ) { - if ( s->Identity == ih) + if ( s.Identity == ih) { /** found existing session */ - LogPrint(eLogDebug, "UDPServer: found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32()); + LogPrint(eLogDebug, "UDPServer: found session ", s.IPSocket.local_endpoint(), " ", ih.ToBase32()); return s; } } /** create new udp session */ boost::asio::ip::udp::endpoint ep(m_LocalAddress, 0); - m_Sessions.push_back(new UDPSession(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort)); - return m_Sessions.back(); + m_Sessions.push_back(UDPSession(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort)); + auto & back = m_Sessions.back(); + return back; } UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint, @@ -568,7 +582,6 @@ namespace client boost::asio::ip::udp::endpoint endpoint, const i2p::data::IdentHash * to, uint16_t ourPort, uint16_t theirPort) : m_Destination(localDestination->GetDatagramDestination()), - m_Service(localDestination->GetService()), IPSocket(localDestination->GetService(), localEndpoint), SendEndpoint(endpoint), LastActivity(i2p::util::GetMillisecondsSinceEpoch()), @@ -602,9 +615,8 @@ namespace client I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr localDestination, - const boost::asio::ip::address& localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port) : + boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port) : m_Name(name), - LocalPort(port), m_LocalAddress(localAddress), m_RemoteEndpoint(forwardTo) { @@ -630,16 +642,16 @@ namespace client { std::vector > sessions; std::lock_guard lock(m_SessionsMutex); - for ( UDPSession * s : m_Sessions ) + for ( UDPSession & s : m_Sessions ) { - if (!s->m_Destination) continue; - auto info = s->m_Destination->GetInfoForRemote(s->Identity); + if (!s.m_Destination) continue; + auto info = s.m_Destination->GetInfoForRemote(s.Identity); if(!info) continue; auto sinfo = std::make_shared(); sinfo->Name = m_Name; sinfo->LocalIdent = std::make_shared(m_LocalDest->GetIdentHash().data()); - sinfo->RemoteIdent = std::make_shared(s->Identity.data()); + sinfo->RemoteIdent = std::make_shared(s.Identity.data()); sinfo->CurrentIBGW = info->IBGW; sinfo->CurrentOBEP = info->OBEP; sessions.push_back(sinfo); @@ -652,13 +664,12 @@ namespace client std::shared_ptr localDestination, uint16_t remotePort) : m_Name(name), - m_Session(nullptr), m_RemoteDest(remoteDest), m_LocalDest(localDestination), m_LocalEndpoint(localEndpoint), m_RemoteIdent(nullptr), m_ResolveThread(nullptr), - LocalPort(localEndpoint.port()), + m_LocalSocket(localDestination->GetService(), localEndpoint), RemotePort(remotePort), m_cancel_resolve(false) { @@ -675,29 +686,34 @@ namespace client m_LocalDest->Start(); if (m_ResolveThread == nullptr) m_ResolveThread = new std::thread(std::bind(&I2PUDPClientTunnel::TryResolving, this)); + RecvFromLocal(); + } + + void I2PUDPClientTunnel::RecvFromLocal() + { + m_LocalSocket.async_receive_from(boost::asio::buffer(m_RecvBuff, I2P_UDP_MAX_MTU), + m_RecvEndpoint, std::bind(&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2)); } + void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred) + { + if(!m_RemoteIdent) return; // drop, remote not resolved + auto remotePort = m_RecvEndpoint.port(); + auto itr = m_Sessions.find(remotePort); + if (itr == m_Sessions.end()) { + // track new udp convo + m_Sessions[remotePort] = {boost::asio::ip::udp::endpoint(m_RecvEndpoint), 0}; + } + // send off to remote i2p destination + m_LocalDest->GetDatagramDestination()->SendDatagramTo(m_RecvBuff, transferred, *m_RemoteIdent, remotePort, RemotePort); + // mark convo as active + std::get<1>(m_Sessions[remotePort]) = i2p::util::GetMillisecondsSinceEpoch(); + } + std::vector > I2PUDPClientTunnel::GetSessions() { + // TODO: implement std::vector > infos; - if(m_Session && m_LocalDest) - { - auto s = m_Session; - if (s->m_Destination) - { - auto info = m_Session->m_Destination->GetInfoForRemote(s->Identity); - if(info) - { - auto sinfo = std::make_shared(); - sinfo->Name = m_Name; - sinfo->LocalIdent = std::make_shared(m_LocalDest->GetIdentHash().data()); - sinfo->RemoteIdent = std::make_shared(s->Identity.data()); - sinfo->CurrentIBGW = info->IBGW; - sinfo->CurrentOBEP = info->OBEP; - infos.push_back(sinfo); - } - } - } return infos; } @@ -717,26 +733,28 @@ namespace client return; } LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32()); - // delete existing session - if(m_Session) delete m_Session; - - boost::asio::ip::udp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 0); - m_Session = new UDPSession(m_LocalEndpoint, m_LocalDest, ep, m_RemoteIdent, LocalPort, RemotePort); } void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) { if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent) { - // address match - if(m_Session) + auto itr = m_Sessions.find(toPort); + // found convo ? + if(itr != m_Sessions.end()) { - // tell session - LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32()); - m_Session->IPSocket.send_to(boost::asio::buffer(buf, len), m_Session->FromEndpoint); + // found convo + if (len > 0) { + LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32()); + uint8_t sendbuf[len]; + memcpy(sendbuf, buf, len); + m_LocalSocket.send_to(boost::asio::buffer(buf, len), std::get<0>(itr->second)); + // mark convo as active + std::get<1>(itr->second) = i2p::util::GetMillisecondsSinceEpoch(); + } } else - LogPrint(eLogWarning, "UDP Client: no session"); + LogPrint(eLogWarning, "UDP Client: not tracking udp session using port ", (int) toPort); } else LogPrint(eLogWarning, "UDP Client: unwarrented traffic from ", from.GetIdentHash().ToBase32()); @@ -747,7 +765,11 @@ namespace client auto dgram = m_LocalDest->GetDatagramDestination(); if (dgram) dgram->ResetReceiver(); - if (m_Session) delete m_Session; + m_Sessions.clear(); + + if(m_LocalSocket.is_open()) + m_LocalSocket.close(); + m_cancel_resolve = true; if(m_ResolveThread) diff --git a/I2PTunnel.h b/I2PTunnel.h index e6f0e84f..8e2b8cb4 100644 --- a/I2PTunnel.h +++ b/I2PTunnel.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -141,7 +142,6 @@ namespace client struct UDPSession { i2p::datagram::DatagramDestination * m_Destination; - boost::asio::io_service & m_Service; boost::asio::ip::udp::socket IPSocket; i2p::data::IdentHash Identity; boost::asio::ip::udp::endpoint FromEndpoint; @@ -189,7 +189,7 @@ namespace client public: I2PUDPServerTunnel(const std::string & name, std::shared_ptr localDestination, - const boost::asio::ip::address & localAddress, + boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port); ~I2PUDPServerTunnel(); /** expire stale udp conversations */ @@ -202,15 +202,14 @@ namespace client private: void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - UDPSession * ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); + UDPSession & ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); private: const std::string m_Name; - const uint16_t LocalPort; boost::asio::ip::address m_LocalAddress; boost::asio::ip::udp::endpoint m_RemoteEndpoint; std::mutex m_SessionsMutex; - std::vector m_Sessions; + std::vector m_Sessions; std::shared_ptr m_LocalDest; }; @@ -228,18 +227,25 @@ namespace client bool IsLocalDestination(const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); } std::shared_ptr GetLocalDestination () const { return m_LocalDest; } + void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); private: + typedef std::tuple UDPConvo; + void RecvFromLocal(); + void HandleRecvFromLocal(const boost::system::error_code & e, std::size_t transferred); void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void TryResolving(); const std::string m_Name; - UDPSession * m_Session; + std::mutex m_SessionsMutex; + std::map m_Sessions; // maps i2p port -> local udp convo const std::string m_RemoteDest; std::shared_ptr m_LocalDest; const boost::asio::ip::udp::endpoint m_LocalEndpoint; i2p::data::IdentHash * m_RemoteIdent; std::thread * m_ResolveThread; - uint16_t LocalPort; + boost::asio::ip::udp::socket m_LocalSocket; + boost::asio::ip::udp::endpoint m_RecvEndpoint; + uint8_t m_RecvBuff[I2P_UDP_MAX_MTU]; uint16_t RemotePort; bool m_cancel_resolve; }; From 81276cb7f528f96ff32b7a940e76de8cef54155d Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 17 Nov 2016 10:43:27 -0500 Subject: [PATCH 03/37] unbreak (maybe?) --- I2PTunnel.cpp | 8 ++++---- I2PTunnel.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 01a3e7ca..795807fe 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -550,7 +550,7 @@ namespace client uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); std::vector removePorts; for (const auto & s : m_Sessions) { - if (now - std::get<1>(s.second) >= delta) + if (now - s.second.second >= delta) removePorts.push_back(s.first); } for(auto port : removePorts) { @@ -707,7 +707,7 @@ namespace client // send off to remote i2p destination m_LocalDest->GetDatagramDestination()->SendDatagramTo(m_RecvBuff, transferred, *m_RemoteIdent, remotePort, RemotePort); // mark convo as active - std::get<1>(m_Sessions[remotePort]) = i2p::util::GetMillisecondsSinceEpoch(); + m_Sessions[remotePort].second = i2p::util::GetMillisecondsSinceEpoch(); } std::vector > I2PUDPClientTunnel::GetSessions() @@ -748,9 +748,9 @@ namespace client LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32()); uint8_t sendbuf[len]; memcpy(sendbuf, buf, len); - m_LocalSocket.send_to(boost::asio::buffer(buf, len), std::get<0>(itr->second)); + m_LocalSocket.send_to(boost::asio::buffer(buf, len), itr->second.first); // mark convo as active - std::get<1>(itr->second) = i2p::util::GetMillisecondsSinceEpoch(); + itr->second.second = i2p::util::GetMillisecondsSinceEpoch(); } } else diff --git a/I2PTunnel.h b/I2PTunnel.h index 8e2b8cb4..ac282582 100644 --- a/I2PTunnel.h +++ b/I2PTunnel.h @@ -230,7 +230,7 @@ namespace client void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); private: - typedef std::tuple UDPConvo; + typedef std::pair UDPConvo; void RecvFromLocal(); void HandleRecvFromLocal(const boost::system::error_code & e, std::size_t transferred); void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); From 9286e4794bf2a6cd78c24d6faf2c8bc469dc600a Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 17 Nov 2016 11:10:42 -0500 Subject: [PATCH 04/37] add logging --- I2PTunnel.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 795807fe..366a510d 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -697,7 +697,10 @@ namespace client void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred) { - if(!m_RemoteIdent) return; // drop, remote not resolved + if(!m_RemoteIdent) { + LogPrint(eLogWarning, "UDP Client: remote endpoint not resolved yet"); + return; // drop, remote not resolved + } auto remotePort = m_RecvEndpoint.port(); auto itr = m_Sessions.find(remotePort); if (itr == m_Sessions.end()) { @@ -705,6 +708,7 @@ namespace client m_Sessions[remotePort] = {boost::asio::ip::udp::endpoint(m_RecvEndpoint), 0}; } // send off to remote i2p destination + LogPrint(eLogDebug, "UDP Client: send ", transferred, " to ", m_RemoteIdent->ToBase32(), ":", RemotePort); m_LocalDest->GetDatagramDestination()->SendDatagramTo(m_RecvBuff, transferred, *m_RemoteIdent, remotePort, RemotePort); // mark convo as active m_Sessions[remotePort].second = i2p::util::GetMillisecondsSinceEpoch(); From 3d07ddfba52a9d21f2b4e12430695b7dbbae9337 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 17 Nov 2016 11:13:40 -0500 Subject: [PATCH 05/37] read more than 1 udp packet --- I2PTunnel.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 366a510d..22376842 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -697,8 +697,13 @@ namespace client void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred) { + if(ec) { + LogPrint(eLogError, "UDP Client: ", ec.message()); + return; + } if(!m_RemoteIdent) { LogPrint(eLogWarning, "UDP Client: remote endpoint not resolved yet"); + RecvFromLocal(); return; // drop, remote not resolved } auto remotePort = m_RecvEndpoint.port(); @@ -712,6 +717,7 @@ namespace client m_LocalDest->GetDatagramDestination()->SendDatagramTo(m_RecvBuff, transferred, *m_RemoteIdent, remotePort, RemotePort); // mark convo as active m_Sessions[remotePort].second = i2p::util::GetMillisecondsSinceEpoch(); + RecvFromLocal(); } std::vector > I2PUDPClientTunnel::GetSessions() From b7a2c11e819b9be6ad0684a6f35f018cbbe1257a Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 17 Nov 2016 11:37:48 -0500 Subject: [PATCH 06/37] use shared_ptr instead --- I2PTunnel.cpp | 28 ++++++++++++++-------------- I2PTunnel.h | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 22376842..af3f09c3 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -531,17 +531,17 @@ namespace client void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) { std::lock_guard lock(m_SessionsMutex); - auto & session = ObtainUDPSession(from, toPort, fromPort); - session.IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); - session.LastActivity = i2p::util::GetMillisecondsSinceEpoch(); + auto session = ObtainUDPSession(from, toPort, fromPort); + session->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); + session->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); } void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) { std::lock_guard lock(m_SessionsMutex); uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); - std::remove_if(m_Sessions.begin(), m_Sessions.end(), [now, delta](const UDPSession & u) -> bool { - return now - u.LastActivity >= delta; + std::remove_if(m_Sessions.begin(), m_Sessions.end(), [now, delta](const std::shared_ptr & u) -> bool { + return now - u->LastActivity >= delta; }); } @@ -558,21 +558,21 @@ namespace client } } - UDPSession & I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort) + std::shared_ptr I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort) { auto ih = from.GetIdentHash(); - for ( UDPSession & s : m_Sessions ) + for (auto & s : m_Sessions ) { - if ( s.Identity == ih) + if ( s->Identity == ih) { /** found existing session */ - LogPrint(eLogDebug, "UDPServer: found session ", s.IPSocket.local_endpoint(), " ", ih.ToBase32()); + LogPrint(eLogDebug, "UDPServer: found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32()); return s; } } /** create new udp session */ boost::asio::ip::udp::endpoint ep(m_LocalAddress, 0); - m_Sessions.push_back(UDPSession(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort)); + m_Sessions.push_back(std::make_shared(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort)); auto & back = m_Sessions.back(); return back; } @@ -642,16 +642,16 @@ namespace client { std::vector > sessions; std::lock_guard lock(m_SessionsMutex); - for ( UDPSession & s : m_Sessions ) + for (auto & s : m_Sessions ) { - if (!s.m_Destination) continue; - auto info = s.m_Destination->GetInfoForRemote(s.Identity); + if (!s->m_Destination) continue; + auto info = s->m_Destination->GetInfoForRemote(s->Identity); if(!info) continue; auto sinfo = std::make_shared(); sinfo->Name = m_Name; sinfo->LocalIdent = std::make_shared(m_LocalDest->GetIdentHash().data()); - sinfo->RemoteIdent = std::make_shared(s.Identity.data()); + sinfo->RemoteIdent = std::make_shared(s->Identity.data()); sinfo->CurrentIBGW = info->IBGW; sinfo->CurrentOBEP = info->OBEP; sessions.push_back(sinfo); diff --git a/I2PTunnel.h b/I2PTunnel.h index ac282582..b49efe40 100644 --- a/I2PTunnel.h +++ b/I2PTunnel.h @@ -202,14 +202,14 @@ namespace client private: void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - UDPSession & ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); + std::shared_ptr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); private: const std::string m_Name; boost::asio::ip::address m_LocalAddress; boost::asio::ip::udp::endpoint m_RemoteEndpoint; std::mutex m_SessionsMutex; - std::vector m_Sessions; + std::vector > m_Sessions; std::shared_ptr m_LocalDest; }; From 59f292333f7cb778e24ca1423b886e319c9da664 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 17 Nov 2016 11:42:23 -0500 Subject: [PATCH 07/37] use correct ports --- I2PTunnel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index af3f09c3..e5c8dc0b 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -605,7 +605,7 @@ namespace client { LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint); LastActivity = i2p::util::GetMillisecondsSinceEpoch(); - m_Destination->SendDatagramTo(m_Buffer, len, Identity, 0, 0); + m_Destination->SendDatagramTo(m_Buffer, len, Identity, LocalPort, RemotePort); Receive(); } else { LogPrint(eLogError, "UDPSession: ", ecode.message()); From f1370189b6ef6b4d29deb015365e6668ac31aad8 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 20 Nov 2016 09:25:56 -0500 Subject: [PATCH 08/37] initial outproxy support for http proxy --- Config.cpp | 3 +- Destination.cpp | 14 ++-- Destination.h | 2 +- HTTPProxy.cpp | 193 +++++++++++++++++++++++++++++++++++++++++++----- SOCKS.h | 1 + 5 files changed, 185 insertions(+), 28 deletions(-) diff --git a/Config.cpp b/Config.cpp index fb587e61..b994496b 100644 --- a/Config.cpp +++ b/Config.cpp @@ -88,7 +88,8 @@ namespace config { ("httpproxy.inbound.quantity", value()->default_value("5"), "HTTP proxy inbound tunnels quantity") ("httpproxy.outbound.quantity", value()->default_value("5"), "HTTP proxy outbound tunnels quantity") ("httpproxy.latency.min", value()->default_value("0"), "HTTP proxy min latency for tunnels") - ("httpproxy.latency.max", value()->default_value("0"), "HTTP proxy max latency for tunnels") + ("httpproxy.latency.max", value()->default_value("0"), "HTTP proxy max latency for tunnels") + ("httpproxy.outproxy", value()->default_value(""), "HTTP proxy upstream out proxy url") ; options_description socksproxy("SOCKS Proxy options"); diff --git a/Destination.cpp b/Destination.cpp index 9cf49336..634e59f6 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -97,14 +97,14 @@ namespace client while (m_IsRunning) { try - { + { m_Service.run (); } catch (std::exception& ex) { - LogPrint (eLogError, "Destination: runtime exception: ", ex.what ()); - } - } + LogPrint (eLogError, "Destination: Runtime Exception", ex.what ()); + } + } } bool LeaseSetDestination::Start () @@ -113,7 +113,7 @@ namespace client { m_IsRunning = true; m_Pool->SetLocalDestination (shared_from_this ()); - m_Pool->SetActive (true); + m_Pool->SetActive (true); m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ())); m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); @@ -137,7 +137,7 @@ namespace client { m_Pool->SetLocalDestination (nullptr); i2p::tunnel::tunnels.StopTunnelPool (m_Pool); - } + } m_Service.stop (); if (m_Thread) { @@ -622,8 +622,8 @@ namespace client if (done) { auto requestComplete = it->second; - m_LeaseSetRequests.erase (it); if (requestComplete) requestComplete->Complete (nullptr); + m_LeaseSetRequests.erase (it); } } } diff --git a/Destination.h b/Destination.h index 5b3ee655..bf622b85 100644 --- a/Destination.h +++ b/Destination.h @@ -137,7 +137,7 @@ namespace client volatile bool m_IsRunning; std::thread * m_Thread; boost::asio::io_service m_Service; - boost::asio::io_service::work m_Work; + boost::asio::io_service::work m_Work; mutable std::mutex m_RemoteLeaseSetsMutex; std::map > m_RemoteLeaseSets; std::map > m_LeaseSetRequests; diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index c40989d1..1750e582 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -64,15 +64,37 @@ namespace proxy { void HostNotFound(std::string & host); void SendProxyError(std::string & content); + void ForwardToUpstreamProxy(); + void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec); + void HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec); + + void HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transfered); + void HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transfered); + + typedef std::function ProxyResolvedHandler; + + void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler); + + void SocksProxySuccess(); + void HandoverToUpstreamProxy(); + uint8_t m_recv_chunk[8192]; std::string m_recv_buf; // from client std::string m_send_buf; // to upstream std::shared_ptr m_sock; - + std::shared_ptr m_proxysock; + boost::asio::ip::tcp::resolver m_proxy_resolver; + i2p::http::URL m_RequestURL; + i2p::http::URL m_ProxyURL; + std::string m_HTTPMethod; + uint8_t m_socks_buf[255+8]; // for socks request/response + public: HTTPReqHandler(HTTPProxy * parent, std::shared_ptr sock) : - I2PServiceHandler(parent), m_sock(sock) {} + I2PServiceHandler(parent), m_sock(sock), + m_proxysock(std::make_shared(parent->GetService())), + m_proxy_resolver(parent->GetService()) {} ~HTTPReqHandler() { Terminate(); } void Handle () { AsyncSockRead(); } /* overload */ }; @@ -97,6 +119,12 @@ namespace proxy { m_sock->close(); m_sock = nullptr; } + if(m_proxysock) + { + if(m_proxysock->is_open()) + m_proxysock->close(); + m_proxysock = nullptr; + } Done(shared_from_this()); } @@ -142,7 +170,7 @@ namespace proxy { << "\r\n"; res.body = ss.str(); std::string response = res.to_string(); - boost::asio::async_write(*m_sock, boost::asio::buffer(response), + boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); } @@ -199,7 +227,6 @@ namespace proxy { bool HTTPReqHandler::HandleRequest() { i2p::http::HTTPReq req; - i2p::http::URL url; std::string b64; int req_len = 0; @@ -216,14 +243,14 @@ namespace proxy { /* parsing success, now let's look inside request */ LogPrint(eLogDebug, "HTTPProxy: requested: ", req.uri); - url.parse(req.uri); + m_RequestURL.parse(req.uri); - if (ExtractAddressHelper(url, b64)) { - i2p::client::context.GetAddressBook ().InsertAddress (url.host, b64); - LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", url.host); - std::string full_url = url.to_string(); + if (ExtractAddressHelper(m_RequestURL, b64)) { + i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, b64); + LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", m_RequestURL.host); + std::string full_url = m_RequestURL.to_string(); std::stringstream ss; - ss << "Host " << url.host << " added to router's addressbook from helper. " + ss << "Host " << m_RequestURL.host << " added to router's addressbook from helper. " << "Click here to proceed."; GenericProxyInfo("Addresshelper found", ss.str().c_str()); return true; /* request processed */ @@ -231,11 +258,11 @@ namespace proxy { SanitizeHTTPRequest(req); - std::string dest_host = url.host; - uint16_t dest_port = url.port; + std::string dest_host = m_RequestURL.host; + uint16_t dest_port = m_RequestURL.port; /* always set port, even if missing in request */ if (!dest_port) { - dest_port = (url.schema == "https") ? 443 : 80; + dest_port = (m_RequestURL.schema == "https") ? 443 : 80; } /* detect dest_host, set proper 'Host' header in upstream request */ auto h = req.headers.find("Host"); @@ -267,16 +294,26 @@ namespace proxy { } /* TODO: outproxy handler here */ } else { - LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": not implemented yet"); - std::string message = "Host" + dest_host + "not inside I2P network, but outproxy support not implemented yet"; - GenericProxyError("Outproxy failure", message.c_str()); + std::string outproxyUrl; i2p::config::GetOption("httpproxy.outproxy", outproxyUrl); + if(outproxyUrl.size()) { + m_HTTPMethod = req.method; + LogPrint (eLogDebug, "HTTPProxy: use outproxy ", outproxyUrl); + if(m_ProxyURL.parse(outproxyUrl)) + ForwardToUpstreamProxy(); + else + GenericProxyError("Outproxy failure", "bad outproxy settings"); + } else { + LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": not implemented yet"); + std::string message = "Host" + dest_host + "not inside I2P network, but outproxy support not implemented yet"; + GenericProxyError("Outproxy failure", message.c_str()); + } return true; } /* make relative url */ - url.schema = ""; - url.host = ""; - req.uri = url.to_string(); + m_RequestURL.schema = ""; + m_RequestURL.host = ""; + req.uri = m_RequestURL.to_string(); /* drop original request from recv buffer */ m_recv_buf.erase(0, req_len); @@ -290,6 +327,124 @@ namespace proxy { return true; } + void HTTPReqHandler::ForwardToUpstreamProxy() + { + LogPrint(eLogDebug, "HTTPProxy: forward to upstream"); + // assume http if empty schema + if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") { + // handle upstream http proxy + if (!m_ProxyURL.port) m_ProxyURL.port = 80; + boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); + m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { + m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1)); + })); + } else if (m_ProxyURL.schema == "socks") { + // handle upstream socks proxy + if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified + boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); + m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { + m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1)); + })); + } else { + // unknown type, complain + GenericProxyError("unknown outproxy url", m_ProxyURL.to_string().c_str()); + } + } + + void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler) + { + if(ec) GenericProxyError("cannot resolve upstream proxy", ec.message().c_str()); + else handler(*it); + } + + void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec) + { + if(!ec) { + if(m_RequestURL.host.size() > 255) { + GenericProxyError("hostname too long", m_RequestURL.host.c_str()); + return; + } + uint16_t port = m_RequestURL.port; + LogPrint(eLogDebug, "HTTPProxy: connected to socks upstream"); + if(m_HTTPMethod == "CONNECT") { + std::string host = m_RequestURL.host; + std::size_t reqsize = 0; + m_socks_buf[0] = '\x04'; + m_socks_buf[1] = 1; + htobe16buf(m_socks_buf+2, port); + m_socks_buf[4] = 0; + m_socks_buf[5] = 0; + m_socks_buf[6] = 0; + m_socks_buf[7] = 1; + // user id + m_socks_buf[8] = 'i'; + m_socks_buf[9] = '2'; + m_socks_buf[10] = 'p'; + m_socks_buf[11] = 'd'; + m_socks_buf[12] = 0; + reqsize += 13; + memcpy(m_socks_buf+ reqsize, host.c_str(), host.size()); + reqsize += host.size(); + m_socks_buf[++reqsize] = 0; + m_proxysock->async_write_some(boost::asio::buffer(m_socks_buf, reqsize), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2)); + } else { + GenericProxyError("unsupported http method", m_HTTPMethod.c_str()); + } + } else GenericProxyError("cannot connect to upstream socks proxy", ec.message().c_str()); + } + + void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred) + { + if(ec) GenericProxyError("Cannot negotiate with socks proxy", ec.message().c_str()); + else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2)); + } + + void HTTPReqHandler::HandoverToUpstreamProxy() + { + auto connection = std::make_shared(GetOwner(), m_proxysock, m_sock); + m_sock = nullptr; + m_proxysock = nullptr; + GetOwner()->AddHandler(connection); + connection->Start(); + Terminate(); + } + + void HTTPReqHandler::SocksProxySuccess() + { + i2p::http::HTTPRes res; + res.code = 200; + std::string response = res.to_string(); + boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) { + if(ec) GenericProxyError("socks proxy error", ec.message().c_str()); + else HandoverToUpstreamProxy(); + }); + } + + void HTTPReqHandler::HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transferred) + { + if(!ec) + { + if(m_socks_buf[1] == 90) { + // success + SocksProxySuccess(); + } else { + std::stringstream ss; + ss << (int) m_socks_buf[1]; + std::string msg = ss.str(); + GenericProxyError("Socks Proxy error", msg.c_str()); + } + } + else GenericProxyError("No Reply From socks proxy", ec.message().c_str()); + } + + void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec) + { + if(!ec) { + LogPrint(eLogDebug, "HTTPProxy: connected to http upstream"); + GenericProxyError("cannot connect", "http out proxy not implemented"); + } else GenericProxyError("cannot connect to upstream http proxy", ec.message().c_str()); + } + /* will be called after some data received from client */ void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) { diff --git a/SOCKS.h b/SOCKS.h index d417c4a0..05106958 100644 --- a/SOCKS.h +++ b/SOCKS.h @@ -33,6 +33,7 @@ namespace proxy }; typedef SOCKSServer SOCKSProxy; + } } From 2a7748656744979f16e273d17027c836e31a33da Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 20 Nov 2016 09:30:46 -0500 Subject: [PATCH 09/37] tabify --- Destination.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Destination.h b/Destination.h index bf622b85..5b3ee655 100644 --- a/Destination.h +++ b/Destination.h @@ -137,7 +137,7 @@ namespace client volatile bool m_IsRunning; std::thread * m_Thread; boost::asio::io_service m_Service; - boost::asio::io_service::work m_Work; + boost::asio::io_service::work m_Work; mutable std::mutex m_RemoteLeaseSetsMutex; std::map > m_RemoteLeaseSets; std::map > m_LeaseSetRequests; From 03ff390685d51509c130719ff3276cb16ec4a612 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 20 Nov 2016 09:31:33 -0500 Subject: [PATCH 10/37] undo pedantic whitespaces --- Destination.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Destination.cpp b/Destination.cpp index 7a789700..7b2e896d 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -97,14 +97,14 @@ namespace client while (m_IsRunning) { try - { + { m_Service.run (); } catch (std::exception& ex) { - LogPrint (eLogError, "Destination: Runtime Exception", ex.what ()); - } - } + LogPrint (eLogError, "Destination: runtime exception: ", ex.what ()); + } + } } bool LeaseSetDestination::Start () @@ -113,7 +113,7 @@ namespace client { m_IsRunning = true; m_Pool->SetLocalDestination (shared_from_this ()); - m_Pool->SetActive (true); + m_Pool->SetActive (true); m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ())); m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); @@ -137,7 +137,7 @@ namespace client { m_Pool->SetLocalDestination (nullptr); i2p::tunnel::tunnels.StopTunnelPool (m_Pool); - } + } m_Service.stop (); if (m_Thread) { @@ -627,8 +627,8 @@ namespace client if (done) { auto requestComplete = it->second; - if (requestComplete) requestComplete->Complete (nullptr); m_LeaseSetRequests.erase (it); + if (requestComplete) requestComplete->Complete (nullptr); } } } From f168e4586c120ced819333a0c75009059d802269 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 20 Nov 2016 09:32:28 -0500 Subject: [PATCH 11/37] undo pedantic whitespace --- SOCKS.h | 1 - 1 file changed, 1 deletion(-) diff --git a/SOCKS.h b/SOCKS.h index 05106958..d417c4a0 100644 --- a/SOCKS.h +++ b/SOCKS.h @@ -33,7 +33,6 @@ namespace proxy }; typedef SOCKSServer SOCKSProxy; - } } From 01da9e3ca267bae0a441e7c12b3512c2e8a5921c Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 20 Nov 2016 12:13:11 -0500 Subject: [PATCH 12/37] fix outproxy --- HTTP.cpp | 17 ++++--- HTTP.h | 5 ++ HTTPProxy.cpp | 136 ++++++++++++++++++++++++++++--------------------- I2PService.cpp | 22 ++++---- I2PService.h | 6 +-- 5 files changed, 109 insertions(+), 77 deletions(-) diff --git a/HTTP.cpp b/HTTP.cpp index 08615e5b..201a4e3d 100644 --- a/HTTP.cpp +++ b/HTTP.cpp @@ -259,16 +259,21 @@ namespace http { return eoh + strlen(HTTP_EOH); } - std::string HTTPReq::to_string() { - std::stringstream ss; - ss << method << " " << uri << " " << version << CRLF; + void HTTPReq::write(std::ostream & o) { + o << method << " " << uri << " " << version << CRLF; for (auto & h : headers) { - ss << h.first << ": " << h.second << CRLF; + o << h.first << ": " << h.second << CRLF; } - ss << CRLF; - return ss.str(); + o << CRLF; } + std::string HTTPReq::to_string() + { + std::stringstream ss; + write(ss); + return ss.str(); + } + bool HTTPRes::is_chunked() { auto it = headers.find("Transfer-Encoding"); if (it == headers.end()) diff --git a/HTTP.h b/HTTP.h index 847cf347..581e4a34 100644 --- a/HTTP.h +++ b/HTTP.h @@ -82,6 +82,9 @@ namespace http { /** @brief Serialize HTTP request to string */ std::string to_string(); + + void write(std::ostream & o); + }; struct HTTPRes : HTTPMsg { @@ -116,6 +119,8 @@ namespace http { */ std::string to_string(); + void write(std::ostream & o); + /** @brief Checks that response declared as chunked data */ bool is_chunked(); diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index 1750e582..33a2a85c 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -84,11 +84,14 @@ namespace proxy { std::shared_ptr m_sock; std::shared_ptr m_proxysock; boost::asio::ip::tcp::resolver m_proxy_resolver; - i2p::http::URL m_RequestURL; i2p::http::URL m_ProxyURL; - std::string m_HTTPMethod; + i2p::http::URL m_RequestURL; uint8_t m_socks_buf[255+8]; // for socks request/response - + ssize_t m_req_len; + i2p::http::URL m_ClientRequestURL; + i2p::http::HTTPReq m_ClientRequest; + i2p::http::HTTPRes m_ClientResponse; + std::stringstream m_ClientRequestBuffer; public: HTTPReqHandler(HTTPProxy * parent, std::shared_ptr sock) : @@ -121,6 +124,7 @@ namespace proxy { } if(m_proxysock) { + LogPrint(eLogDebug, "HTTPProxy: close proxysock"); if(m_proxysock->is_open()) m_proxysock->close(); m_proxysock = nullptr; @@ -226,24 +230,22 @@ namespace proxy { */ bool HTTPReqHandler::HandleRequest() { - i2p::http::HTTPReq req; std::string b64; - int req_len = 0; - req_len = req.parse(m_recv_buf); + m_req_len = m_ClientRequest.parse(m_recv_buf); - if (req_len == 0) + if (m_req_len == 0) return false; /* need more data */ - if (req_len < 0) { + if (m_req_len < 0) { LogPrint(eLogError, "HTTPProxy: unable to parse request"); GenericProxyError("Invalid request", "Proxy unable to parse your request"); return true; /* parse error */ } /* parsing success, now let's look inside request */ - LogPrint(eLogDebug, "HTTPProxy: requested: ", req.uri); - m_RequestURL.parse(req.uri); + LogPrint(eLogDebug, "HTTPProxy: requested: ", m_ClientRequest.uri); + m_RequestURL.parse(m_ClientRequest.uri); if (ExtractAddressHelper(m_RequestURL, b64)) { i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, b64); @@ -256,7 +258,7 @@ namespace proxy { return true; /* request processed */ } - SanitizeHTTPRequest(req); + SanitizeHTTPRequest(m_ClientRequest); std::string dest_host = m_RequestURL.host; uint16_t dest_port = m_RequestURL.port; @@ -265,14 +267,14 @@ namespace proxy { dest_port = (m_RequestURL.schema == "https") ? 443 : 80; } /* detect dest_host, set proper 'Host' header in upstream request */ - auto h = req.headers.find("Host"); + auto h = m_ClientRequest.headers.find("Host"); if (dest_host != "") { /* absolute url, replace 'Host' header */ std::string h = dest_host; if (dest_port != 0 && dest_port != 80) h += ":" + std::to_string(dest_port); - req.add_header("Host", h, true); - } else if (h != req.headers.end()) { + m_ClientRequest.add_header("Host", h, true); + } else if (h != m_ClientRequest.headers.end()) { /* relative url and 'Host' header provided. transparent proxy mode? */ i2p::http::URL u; std::string t = "http://" + h->second; @@ -292,19 +294,17 @@ namespace proxy { HostNotFound(dest_host); return true; /* request processed */ } - /* TODO: outproxy handler here */ } else { std::string outproxyUrl; i2p::config::GetOption("httpproxy.outproxy", outproxyUrl); if(outproxyUrl.size()) { - m_HTTPMethod = req.method; LogPrint (eLogDebug, "HTTPProxy: use outproxy ", outproxyUrl); if(m_ProxyURL.parse(outproxyUrl)) ForwardToUpstreamProxy(); else GenericProxyError("Outproxy failure", "bad outproxy settings"); } else { - LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": not implemented yet"); - std::string message = "Host" + dest_host + "not inside I2P network, but outproxy support not implemented yet"; + LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outprxy enabled"); + std::string message = "Host" + dest_host + "not inside I2P network, but outproxy is not enabled"; GenericProxyError("Outproxy failure", message.c_str()); } return true; @@ -313,12 +313,12 @@ namespace proxy { /* make relative url */ m_RequestURL.schema = ""; m_RequestURL.host = ""; - req.uri = m_RequestURL.to_string(); + m_ClientRequest.uri = m_RequestURL.to_string(); /* drop original request from recv buffer */ - m_recv_buf.erase(0, req_len); + m_recv_buf.erase(0, m_req_len); /* build new buffer from modified request and data from original request */ - m_send_buf = req.to_string(); + m_send_buf = m_ClientRequest.to_string(); m_send_buf.append(m_recv_buf); /* connect to destination */ LogPrint(eLogDebug, "HTTPProxy: connecting to host ", dest_host, ":", dest_port); @@ -330,6 +330,17 @@ namespace proxy { void HTTPReqHandler::ForwardToUpstreamProxy() { LogPrint(eLogDebug, "HTTPProxy: forward to upstream"); + // build http requset + + m_ClientRequestURL = m_RequestURL; + LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host); + m_ClientRequestURL.schema = ""; + m_ClientRequestURL.host = ""; + m_ClientRequest.uri = m_ClientRequestURL.to_string(); + + m_ClientRequest.write(m_ClientRequestBuffer); + m_ClientRequestBuffer << m_recv_buf.substr(m_req_len); + // assume http if empty schema if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") { // handle upstream http proxy @@ -365,59 +376,67 @@ namespace proxy { return; } uint16_t port = m_RequestURL.port; + if(!port) port = 80; LogPrint(eLogDebug, "HTTPProxy: connected to socks upstream"); - if(m_HTTPMethod == "CONNECT") { - std::string host = m_RequestURL.host; - std::size_t reqsize = 0; - m_socks_buf[0] = '\x04'; - m_socks_buf[1] = 1; - htobe16buf(m_socks_buf+2, port); - m_socks_buf[4] = 0; - m_socks_buf[5] = 0; - m_socks_buf[6] = 0; - m_socks_buf[7] = 1; - // user id - m_socks_buf[8] = 'i'; - m_socks_buf[9] = '2'; - m_socks_buf[10] = 'p'; - m_socks_buf[11] = 'd'; - m_socks_buf[12] = 0; - reqsize += 13; - memcpy(m_socks_buf+ reqsize, host.c_str(), host.size()); - reqsize += host.size(); - m_socks_buf[++reqsize] = 0; - m_proxysock->async_write_some(boost::asio::buffer(m_socks_buf, reqsize), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2)); - } else { - GenericProxyError("unsupported http method", m_HTTPMethod.c_str()); - } + + std::string host = m_RequestURL.host; + std::size_t reqsize = 0; + m_socks_buf[0] = '\x04'; + m_socks_buf[1] = 1; + htobe16buf(m_socks_buf+2, port); + m_socks_buf[4] = 0; + m_socks_buf[5] = 0; + m_socks_buf[6] = 0; + m_socks_buf[7] = 1; + // user id + m_socks_buf[8] = 'i'; + m_socks_buf[9] = '2'; + m_socks_buf[10] = 'p'; + m_socks_buf[11] = 'd'; + m_socks_buf[12] = 0; + reqsize += 13; + memcpy(m_socks_buf+ reqsize, host.c_str(), host.size()); + reqsize += host.size(); + m_socks_buf[++reqsize] = 0; + boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2)); } else GenericProxyError("cannot connect to upstream socks proxy", ec.message().c_str()); } void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred) { + LogPrint(eLogDebug, "HTTPProxy: upstream socks handshake sent"); if(ec) GenericProxyError("Cannot negotiate with socks proxy", ec.message().c_str()); else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2)); } void HTTPReqHandler::HandoverToUpstreamProxy() { - auto connection = std::make_shared(GetOwner(), m_proxysock, m_sock); - m_sock = nullptr; - m_proxysock = nullptr; - GetOwner()->AddHandler(connection); - connection->Start(); - Terminate(); + LogPrint(eLogDebug, "HTTPProxy: handover to socks proxy"); + auto connection = std::make_shared(GetOwner(), m_proxysock, m_sock); + m_sock = nullptr; + m_proxysock = nullptr; + GetOwner()->AddHandler(connection); + connection->Start(); + Terminate(); } void HTTPReqHandler::SocksProxySuccess() { - i2p::http::HTTPRes res; - res.code = 200; - std::string response = res.to_string(); - boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) { - if(ec) GenericProxyError("socks proxy error", ec.message().c_str()); - else HandoverToUpstreamProxy(); - }); + if(m_ClientRequest.method == "CONNECT") { + m_ClientResponse.code = 200; + m_send_buf = m_ClientResponse.to_string(); + boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) { + if(ec) GenericProxyError("socks proxy error", ec.message().c_str()); + else HandoverToUpstreamProxy(); + }); + } else { + m_send_buf = m_ClientRequestBuffer.str(); + LogPrint(eLogDebug, "HTTPProxy: send ", m_send_buf.size(), " bytes"); + boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred) { + if(ec) GenericProxyError("failed to send request to upstream", ec.message().c_str()); + else HandoverToUpstreamProxy(); + }); + } } void HTTPReqHandler::HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transferred) @@ -429,6 +448,7 @@ namespace proxy { SocksProxySuccess(); } else { std::stringstream ss; + ss << "error code: "; ss << (int) m_socks_buf[1]; std::string msg = ss.str(); GenericProxyError("Socks Proxy error", msg.c_str()); diff --git a/I2PService.cpp b/I2PService.cpp index f5ebcb0c..efcf61aa 100644 --- a/I2PService.cpp +++ b/I2PService.cpp @@ -53,7 +53,6 @@ namespace client void TCPIPPipe::Terminate() { if(Kill()) return; - Done(shared_from_this()); if (m_up) { if (m_up->is_open()) { m_up->close(); @@ -66,6 +65,7 @@ namespace client } m_down = nullptr; } + Done(shared_from_this()); } void TCPIPPipe::AsyncReceiveUpstream() @@ -90,11 +90,11 @@ namespace client } } - void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len) + void TCPIPPipe::UpstreamWrite(size_t len) { if (m_up) { LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written"); - boost::asio::async_write(*m_up, boost::asio::buffer(buf, len), + boost::asio::async_write(*m_up, boost::asio::buffer(m_upstream_buf, len), boost::asio::transfer_all(), std::bind(&TCPIPPipe::HandleUpstreamWrite, shared_from_this(), @@ -105,11 +105,11 @@ namespace client } } - void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len) + void TCPIPPipe::DownstreamWrite(size_t len) { if (m_down) { LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written"); - boost::asio::async_write(*m_down, boost::asio::buffer(buf, len), + boost::asio::async_write(*m_down, boost::asio::buffer(m_downstream_buf, len), boost::asio::transfer_all(), std::bind(&TCPIPPipe::HandleDownstreamWrite, shared_from_this(), @@ -131,9 +131,8 @@ namespace client } else { if (bytes_transfered > 0 ) { memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered); - UpstreamWrite(m_upstream_buf, bytes_transfered); } - AsyncReceiveDownstream(); + UpstreamWrite(bytes_transfered); } } @@ -142,6 +141,8 @@ namespace client LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message()); if (ecode != boost::asio::error::operation_aborted) Terminate(); + } else { + AsyncReceiveUpstream(); } } @@ -150,6 +151,8 @@ namespace client LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message()); if (ecode != boost::asio::error::operation_aborted) Terminate(); + } else { + AsyncReceiveDownstream(); } } @@ -162,10 +165,9 @@ namespace client Terminate(); } else { if (bytes_transfered > 0 ) { - memcpy(m_upstream_buf, m_upstream_to_down_buf, bytes_transfered); - DownstreamWrite(m_upstream_buf, bytes_transfered); + memcpy(m_downstream_buf, m_upstream_to_down_buf, bytes_transfered); } - AsyncReceiveUpstream(); + DownstreamWrite(bytes_transfered); } } diff --git a/I2PService.h b/I2PService.h index 59746a6f..795b075e 100644 --- a/I2PService.h +++ b/I2PService.h @@ -77,7 +77,7 @@ namespace client std::atomic m_Dead; //To avoid cleaning up multiple times }; - const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192; + const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192 * 8; // bidirectional pipe for 2 tcp/ip sockets class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this { @@ -93,8 +93,8 @@ namespace client 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); + void UpstreamWrite(size_t len); + void DownstreamWrite(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]; From 4d5e9c52b2c7905fb09e09a977dab90e0feb0e20 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 7 Dec 2016 09:38:19 -0500 Subject: [PATCH 13/37] Use eddsa-sh512-ed25519 by default --- Identity.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Identity.h b/Identity.h index 49dada48..7a9d049a 100644 --- a/Identity.h +++ b/Identity.h @@ -133,7 +133,7 @@ namespace data size_t FromBase64(const std::string& s); std::string ToBase64 () const; - static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1); + static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); private: From 975265b0afe539994a0638caad7d70e328299323 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 7 Dec 2016 11:52:20 -0500 Subject: [PATCH 14/37] more --- Event.cpp | 35 +++++++++++++++++++++--- Event.h | 18 ++++++++++++- NTCPSession.cpp | 2 +- SSUData.cpp | 2 +- Transports.cpp | 2 +- Websocket.cpp | 72 +++++++++++++++++++++++++++++++++++++++++++------ 6 files changed, 116 insertions(+), 15 deletions(-) diff --git a/Event.cpp b/Event.cpp index e148538e..4bc6d594 100644 --- a/Event.cpp +++ b/Event.cpp @@ -17,15 +17,44 @@ namespace i2p void EventCore::QueueEvent(const EventType & ev) { - if(m_listener) - m_listener->HandleEvent(ev); + if(m_listener) m_listener->HandleEvent(ev); + } + + void EventCore::CollectEvent(const std::string & type, const std::string & ident, uint64_t val) + { + std::unique_lock lock(m_collect_mutex); + std::string key = type + "." + ident; + if (m_collected.find(key) == m_collected.end()) + { + m_collected[key] = {type, key, 0}; + } + m_collected[key].Val += val; + } + + void EventCore::PumpCollected(EventListener * listener) + { + std::unique_lock lock(m_collect_mutex); + if(listener) + { + for(const auto & ev : m_collected) { + listener->HandlePumpEvent({{"type", ev.second.Key}, {"ident", ev.second.Ident}}, ev.second.Val); + } + } + m_collected.clear(); } } } -void EmitEvent(const EventType & e) +void QueueIntEvent(const std::string & type, const std::string & ident, uint64_t val) { #ifdef WITH_EVENTS + i2p::event::core.CollectEvent(type, ident, val); +#endif +} + +void EmitEvent(const EventType & e) +{ +#if WITH_EVENTS i2p::event::core.QueueEvent(e); #endif } diff --git a/Event.h b/Event.h index 1ab37847..a9f97df2 100644 --- a/Event.h +++ b/Event.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include @@ -16,15 +18,27 @@ namespace i2p public: virtual ~EventListener() {}; virtual void HandleEvent(const EventType & ev) = 0; + /** @brief handle collected event when pumped */ + virtual void HandlePumpEvent(const EventType & ev, const uint64_t & val) = 0; }; class EventCore { public: void QueueEvent(const EventType & ev); + void CollectEvent(const std::string & type, const std::string & ident, uint64_t val); void SetListener(EventListener * l); - + void PumpCollected(EventListener * l); + private: + std::mutex m_collect_mutex; + struct CollectedEvent + { + std::string Key; + std::string Ident; + uint64_t Val; + }; + std::map m_collected; EventListener * m_listener = nullptr; }; #ifdef WITH_EVENTS @@ -32,6 +46,8 @@ namespace i2p #endif } } + +void QueueIntEvent(const std::string & type, const std::string & ident, uint64_t val); void EmitEvent(const EventType & ev); #endif diff --git a/NTCPSession.cpp b/NTCPSession.cpp index 81cfe687..1d3cd95b 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -610,7 +610,7 @@ namespace transport if (!m_NextMessage->IsExpired ()) { #ifdef WITH_EVENTS - EmitEvent({{"type", "transport.recvmsg"} , {"ident", GetIdentHashBase64()}, {"number", "1"}}); + QueueIntEvent("transport.recvmsg", GetIdentHashBase64(), 1); #endif m_Handler.PutNextMessage (m_NextMessage); } diff --git a/SSUData.cpp b/SSUData.cpp index ad38cf25..48e3e3f1 100644 --- a/SSUData.cpp +++ b/SSUData.cpp @@ -240,7 +240,7 @@ namespace transport if (!msg->IsExpired ()) { #ifdef WITH_EVENTS - EmitEvent({{"type", "transport.recvmsg"} , {"ident", m_Session.GetIdentHashBase64()}, {"number", "1"}}); + QueueIntEvent("transport.recvmsg", m_Session.GetIdentHashBase64(), 1); #endif m_Handler.PutNextMessage (msg); } diff --git a/Transports.cpp b/Transports.cpp index 1b9d52a1..efcd6742 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -249,7 +249,7 @@ namespace transport void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector >& msgs) { #ifdef WITH_EVENTS - EmitEvent({{"type" , "transport.sendmsg"}, {"ident", ident.ToBase64()}, {"number", std::to_string(msgs.size())}}); + QueueIntEvent("transport.send", ident.ToBase64(), msgs.size()); #endif m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs)); } diff --git a/Websocket.cpp b/Websocket.cpp index 0de44efe..a1a58c5f 100644 --- a/Websocket.cpp +++ b/Websocket.cpp @@ -2,6 +2,7 @@ #include "Log.h" #include +#include #include #include @@ -27,7 +28,11 @@ namespace i2p typedef ServerImpl::message_ptr MessagePtr; public: - WebsocketServerImpl(const std::string & addr, int port) : m_run(false), m_thread(nullptr) + WebsocketServerImpl(const std::string & addr, int port) : + m_run(false), + m_ws_thread(nullptr), + m_ev_thread(nullptr), + m_WebsocketTicker(m_Service) { m_server.init_asio(); m_server.set_open_handler(std::bind(&WebsocketServerImpl::ConnOpened, this, std::placeholders::_1)); @@ -44,7 +49,7 @@ namespace i2p void Start() { m_run = true; m_server.start_accept(); - m_thread = new std::thread([&] () { + m_ws_thread = new std::thread([&] () { while(m_run) { try { m_server.run(); @@ -53,16 +58,35 @@ namespace i2p } } }); + m_ev_thread = new std::thread([&] () { + while(m_run) { + try { + m_Service.run(); + break; + } catch (std::exception & e ) { + LogPrint(eLogError, "Websocket service: ", e.what()); + } + } + }); + ScheduleTick(); } void Stop() { m_run = false; + m_Service.stop(); m_server.stop(); - if(m_thread) { - m_thread->join(); - delete m_thread; + + if(m_ev_thread) { + m_ev_thread->join(); + delete m_ev_thread; } - m_thread = nullptr; + m_ev_thread = nullptr; + + if(m_ws_thread) { + m_ws_thread->join(); + delete m_ws_thread; + } + m_ws_thread = nullptr; } void ConnOpened(ServerConn c) @@ -82,11 +106,40 @@ namespace i2p (void) conn; (void) msg; } + + void HandleTick(const boost::system::error_code & ec) + { + + if(ec != boost::asio::error::operation_aborted) + LogPrint(eLogError, "Websocket ticker: ", ec.message()); + // pump collected events to us + i2p::event::core.PumpCollected(this); + ScheduleTick(); + } + + void ScheduleTick() + { + LogPrint(eLogDebug, "Websocket schedule tick"); + boost::posix_time::seconds dlt(1); + m_WebsocketTicker.expires_from_now(dlt); + m_WebsocketTicker.async_wait(std::bind(&WebsocketServerImpl::HandleTick, this, std::placeholders::_1)); + } + + /** @brief called from m_ev_thread */ + void HandlePumpEvent(const EventType & ev, const uint64_t & val) + { + EventType e; + for (const auto & i : ev) + e[i.first] = i.second; + + e["number"] = std::to_string(val); + HandleEvent(e); + } + /** @brief called from m_ws_thread */ void HandleEvent(const EventType & ev) { std::lock_guard lock(m_connsMutex); - LogPrint(eLogDebug, "websocket event"); boost::property_tree::ptree event; for (const auto & item : ev) { event.put(item.first, item.second); @@ -105,10 +158,13 @@ namespace i2p private: typedef std::set > ConnList; bool m_run; - std::thread * m_thread; + std::thread * m_ws_thread; + std::thread * m_ev_thread; std::mutex m_connsMutex; ConnList m_conns; ServerImpl m_server; + boost::asio::io_service m_Service; + boost::asio::deadline_timer m_WebsocketTicker; }; From 0d83a34cfdd20d1959dff1cb7cd4bfb89278d94a Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 9 Dec 2016 15:36:38 -0500 Subject: [PATCH 15/37] add initial WebSOCKS implementation --- Config.cpp | 9 +- Daemon.cpp | 10 ++ WebSocks.cpp | 411 +++++++++++++++++++++++++++++++++++++++++++ WebSocks.h | 27 +++ build/CMakeLists.txt | 1 + filelist.mk | 2 +- 6 files changed, 458 insertions(+), 2 deletions(-) create mode 100644 WebSocks.cpp create mode 100644 WebSocks.h diff --git a/Config.cpp b/Config.cpp index 6f4014ba..9e8fb657 100644 --- a/Config.cpp +++ b/Config.cpp @@ -200,7 +200,13 @@ namespace config { ("websockets.enabled", value()->default_value(false), "enable websocket server") ("websockets.address", value()->default_value("127.0.0.1"), "address to bind websocket server on") ("websockets.port", value()->default_value(7666), "port to bind websocket server on"); - + + options_description websocks("WebSOCKS options"); + websocks.add_options() + ("websocks.enabled", value()->default_value(false), "enable WebSOCKS server") + ("websocks.address", value()->default_value("127.0.0.1"), "address to bind WebSOCKS server on") + ("websocks.port", value()->default_value(7666), "port to bind WebSOCKS server on"); + m_OptionsDesc .add(general) .add(limits) @@ -217,6 +223,7 @@ namespace config { .add(addressbook) .add(trust) .add(websocket) + .add(websocks) ; } diff --git a/Daemon.cpp b/Daemon.cpp index a007995f..edd3dcf9 100644 --- a/Daemon.cpp +++ b/Daemon.cpp @@ -27,6 +27,7 @@ #include "Event.h" #include "Websocket.h" +#include "WebSocks.h" namespace i2p { @@ -43,6 +44,7 @@ namespace i2p std::unique_ptr UPnP; #ifdef WITH_EVENTS std::unique_ptr m_WebsocketServer; + std::unique_ptr m_WebSocksServer; #endif }; @@ -307,6 +309,14 @@ namespace i2p d.m_WebsocketServer->Start(); i2p::event::core.SetListener(d.m_WebsocketServer->ToListener()); } + bool websocks; i2p::config::GetOption("websocks.enabled", websocks); + if (websocks) { + std::string websocksAddr; i2p::config::GetOption("websocks.address", websocksAddr); + uint16_t websocksPort; i2p::config::GetOption("websocks.port", websocksPort); + LogPrint(eLogInfo, "Daemon: starting up WebSOCKS server at ", websocksAddr, ":", websocksPort); + d.m_WebSocksServer = std::unique_ptr(new i2p::client::WebSocks(websocksAddr, websocksPort)); + d.m_WebSocksServer->Start(); + } #endif return true; diff --git a/WebSocks.cpp b/WebSocks.cpp new file mode 100644 index 00000000..6787d390 --- /dev/null +++ b/WebSocks.cpp @@ -0,0 +1,411 @@ +#include "WebSocks.h" +#include "Log.h" + +#ifdef WITH_EVENTS +#include "ClientContext.h" +#include "Identity.h" +#include "Destination.h" +#include "Streaming.h" +#include + +#include +#include + +#include +#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) +#if !GCC47_BOOST149 +#include +#endif + +namespace i2p +{ +namespace client +{ + typedef websocketpp::server WebSocksServerImpl; + + typedef std::function)> StreamConnectFunc; + + + struct IWebSocksConn + { + virtual void Close() = 0; + virtual void GotMessage(const websocketpp::connection_hdl & conn, WebSocksServerImpl::message_ptr msg) = 0; + }; + + typedef std::shared_ptr WebSocksConn_ptr; + + WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent); + + class WebSocksImpl + { + + typedef std::mutex mutex_t; + typedef std::unique_lock lock_t; + + typedef std::shared_ptr Destination_t; + public: + + typedef WebSocksServerImpl ServerImpl; + typedef ServerImpl::message_ptr MessagePtr; + + WebSocksImpl(const std::string & addr, int port) : + m_Run(false), + m_Addr(addr), + m_Port(port), + m_Thread(nullptr) + { + m_Server.init_asio(); + m_Server.set_open_handler(std::bind(&WebSocksImpl::ConnOpened, this, std::placeholders::_1)); + i2p::data::PrivateKeys k = i2p::data::PrivateKeys::CreateRandomKeys(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); + m_Dest = std::make_shared(k, false); + } + + ServerImpl::connection_ptr GetConn(const websocketpp::connection_hdl & conn) + { + return m_Server.get_con_from_hdl(conn); + } + + void CloseConn(const websocketpp::connection_hdl & conn) + { + auto c = GetConn(conn); + if(c) c->close(websocketpp::close::status::normal, "closed"); + } + + void CreateStreamTo(const std::string & addr, int port, StreamConnectFunc complete) + { + auto & addressbook = i2p::client::context.GetAddressBook(); + i2p::data::IdentHash ident; + if(addressbook.GetIdentHash(addr, ident)) { + // address found + m_Dest->CreateStream(complete, ident, port); + } else { + // not found + complete(nullptr); + } + } + + void ConnOpened(websocketpp::connection_hdl conn) + { + m_Conns.push_back(CreateWebSocksConn(conn, this)); + } + + void Start() + { + if(m_Run) return; // already started + m_Server.listen(boost::asio::ip::address::from_string(m_Addr), m_Port); + m_Server.start_accept(); + m_Run = true; + m_Thread = new std::thread([&] (){ + while(m_Run) { + try { + m_Server.run(); + } catch( std::exception & ex) { + LogPrint(eLogError, "Websocks runtime exception: ", ex.what()); + } + } + }); + m_Dest->Start(); + } + + void Stop() + { + for(const auto & conn : m_Conns) + conn->Close(); + + m_Dest->Stop(); + m_Run = false; + m_Server.stop(); + if(m_Thread) { + m_Thread->join(); + delete m_Thread; + } + m_Thread = nullptr; + } + + private: + std::vector m_Conns; + bool m_Run; + ServerImpl m_Server; + std::string m_Addr; + int m_Port; + std::thread * m_Thread; + Destination_t m_Dest; + }; + + struct WebSocksConn : public IWebSocksConn + { + enum ConnState + { + eWSCInitial, + eWSCTryConnect, + eWSCFailConnect, + eWSCOkayConnect, + eWSCClose, + eWSCEnd + }; + + typedef WebSocksServerImpl ServerImpl; + typedef ServerImpl::message_ptr Message_t; + typedef websocketpp::connection_hdl ServerConn; + typedef std::shared_ptr Destination_t; + typedef std::shared_ptr StreamDest_t; + typedef std::shared_ptr Stream_t; + + ServerConn m_Conn; + Stream_t m_Stream; + ConnState m_State; + WebSocksImpl * m_Parent; + std::string m_RemoteAddr; + int m_RemotePort; + uint8_t m_RecvBuf[2048]; + + WebSocksConn(const ServerConn & conn, WebSocksImpl * parent) : + m_Conn(conn), + m_Stream(nullptr), + m_State(eWSCInitial), + m_Parent(parent) + { + + } + + ~WebSocksConn() + { + Close(); + } + + void EnterState(ConnState state) + { + LogPrint(eLogDebug, "websocks: state ", m_State, " -> ", state); + switch(m_State) + { + case eWSCInitial: + if (state == eWSCClose) { + m_State = eWSCClose; + // connection was opened but never used + LogPrint(eLogInfo, "websocks: connection closed but never used"); + Close(); + return; + } else if (state == eWSCTryConnect) { + // we will try to connect + m_State = eWSCTryConnect; + m_Parent->CreateStreamTo(m_RemoteAddr, m_RemotePort, std::bind(&WebSocksConn::ConnectResult, this, std::placeholders::_1)); + } else { + LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); + } + return; + case eWSCTryConnect: + if(state == eWSCOkayConnect) { + // we connected okay + LogPrint(eLogDebug, "websocks: connected to ", m_RemoteAddr, ":", m_RemotePort); + SendResponse(""); + m_State = eWSCOkayConnect; + StartForwarding(); + } else if(state == eWSCFailConnect) { + // we did not connect okay + LogPrint(eLogDebug, "websocks: failed to connect to ", m_RemoteAddr, ":", m_RemotePort); + SendResponse("failed to connect"); + m_State = eWSCFailConnect; + EnterState(eWSCInitial); + } else if(state == eWSCClose) { + // premature close + LogPrint(eLogWarning, "websocks: websocket connection closed prematurely"); + m_State = eWSCClose; + } else { + LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); + } + return; + case eWSCFailConnect: + if (state == eWSCInitial) { + // reset to initial state so we can try connecting again + m_RemoteAddr = ""; + m_RemotePort = 0; + LogPrint(eLogDebug, "websocks: reset websocket conn to initial state"); + m_State = eWSCInitial; + } else if (state == eWSCClose) { + // we are going to close the connection + m_State = eWSCClose; + Close(); + } else { + LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); + } + return; + case eWSCOkayConnect: + if(state == eWSCClose) { + // graceful close + m_State = eWSCClose; + Close(); + } else { + LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); + } + case eWSCClose: + if(state == eWSCEnd) { + LogPrint(eLogDebug, "websocks: socket ended"); + } else { + LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); + } + return; + default: + LogPrint(eLogError, "websocks: bad state ", m_State); + } + } + + void StartForwarding() + { + LogPrint(eLogDebug, "websocks: begin forwarding data"); + AsyncRecv(); + } + + void HandleAsyncRecv(const boost::system::error_code &ec, std::size_t n) + { + if(ec) { + // error + LogPrint(eLogWarning, "websocks: connection error ", ec.message()); + EnterState(eWSCClose); + } else { + // forward data + LogPrint(eLogDebug, "websocks recv ", n); + std::string str((char*)m_RecvBuf, n); + auto conn = m_Parent->GetConn(m_Conn); + if(!conn) { + LogPrint(eLogWarning, "websocks: connection is gone"); + EnterState(eWSCClose); + return; + } + conn->send(str); + AsyncRecv(); + } + } + + void AsyncRecv() + { + m_Stream->AsyncReceive( + boost::asio::buffer(m_RecvBuf, sizeof(m_RecvBuf)), + std::bind(&WebSocksConn::HandleAsyncRecv, this, std::placeholders::_1, std::placeholders::_2)); + } + + /** @brief send error message or empty string for success */ + void SendResponse(const std::string & errormsg) + { + boost::property_tree::ptree resp; + if(errormsg.size()) { + resp.put("error", errormsg); + resp.put("success", 0); + } else { + resp.put("success", 1); + } + std::ostringstream ss; + write_json(ss, resp); + auto conn = m_Parent->GetConn(m_Conn); + if(conn) conn->send(ss.str()); + } + + void ConnectResult(Stream_t stream) + { + m_Stream = stream; + if(m_State == eWSCClose) { + // premature close of websocket + Close(); + return; + } + if(m_Stream) { + // connect good + EnterState(eWSCOkayConnect); + } else { + // connect failed + EnterState(eWSCFailConnect); + } + } + + virtual void GotMessage(const websocketpp::connection_hdl & conn, WebSocksServerImpl::message_ptr msg) + { + (void) conn; + std::string payload = msg->get_payload(); + if(m_State == eWSCOkayConnect) + { + // forward to server + LogPrint(eLogDebug, "websocks: send ", payload.size()); + m_Stream->Send((uint8_t*)payload.c_str(), payload.size()); + } else if (m_State == eWSCInitial) { + // recv connect request + auto itr = payload.find(":"); + if(itr == std::string::npos) { + // no port + m_RemotePort = 0; + m_RemoteAddr = payload; + } else { + // includes port + m_RemotePort = std::stoi(payload.substr(itr+1)); + m_RemoteAddr = payload.substr(0, itr); + } + EnterState(eWSCTryConnect); + } else { + // wtf? + LogPrint(eLogWarning, "websocks: got message in invalid state ", m_State); + } + } + + virtual void Close() + { + if(m_State == eWSCClose) { + LogPrint(eLogDebug, "websocks: closing connection"); + if(m_Stream) m_Stream->Close(); + m_Parent->CloseConn(m_Conn); + EnterState(eWSCEnd); + } else { + EnterState(eWSCClose); + } + } + }; + + WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent) + { + auto ptr = std::make_shared(conn, parent); + auto c = parent->GetConn(conn); + c->set_message_handler(std::bind(&WebSocksConn::GotMessage, ptr.get(), std::placeholders::_1, std::placeholders::_2)); + return ptr; + } + +} +} +#else + +// no websocket support + +namespace i2p +{ +namespace client +{ + class WebSocksImpl + { + public: + WebSocks(const std::string & addr, int port) {} + ~WebSocks(){} + void Start() + { + LogPrint(eLogInfo, "WebSockets not enabled on compile time"); + } + + void Stop() {} + }; +} +} + +#endif +namespace i2p +{ +namespace client +{ + WebSocks::WebSocks(const std::string & addr, int port) : m_Impl(new WebSocksImpl(addr, port)) {} + WebSocks::~WebSocks() { delete m_Impl; } + + void WebSocks::Start() + { + m_Impl->Start(); + } + + void WebSocks::Stop() + { + m_Impl->Stop(); + } +} +} diff --git a/WebSocks.h b/WebSocks.h new file mode 100644 index 00000000..93943abd --- /dev/null +++ b/WebSocks.h @@ -0,0 +1,27 @@ +#ifndef WEBSOCKS_H_ +#define WEBSOCKS_H_ +#include + +namespace i2p +{ +namespace client +{ + + class WebSocksImpl; + + /** @brief websocket socks proxy server */ + class WebSocks + { + public: + WebSocks(const std::string & addr, int port); + ~WebSocks(); + + void Start(); + void Stop(); + + private: + WebSocksImpl * m_Impl; + }; +} +} +#endif diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index a1fd19c2..2baa02aa 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -98,6 +98,7 @@ set (CLIENT_SRC "${CMAKE_SOURCE_DIR}/HTTP.cpp" "${CMAKE_SOURCE_DIR}/HTTPProxy.cpp" "${CMAKE_SOURCE_DIR}/I2CP.cpp" + "${CMAKE_SOURCE_DIR}/WebSocks.cpp" ) if(WITH_WEBSOCKETS) diff --git a/filelist.mk b/filelist.mk index 76f58785..d5d703a6 100644 --- a/filelist.mk +++ b/filelist.mk @@ -9,7 +9,7 @@ LIB_SRC = \ LIB_CLIENT_SRC = \ AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp \ - SAM.cpp SOCKS.cpp HTTPProxy.cpp I2CP.cpp + SAM.cpp SOCKS.cpp HTTPProxy.cpp I2CP.cpp WebSocks.cpp # also: Daemon{Linux,Win32}.cpp will be added later DAEMON_SRC = \ From a59e07353639658238a81c0ae3667209b6223de3 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 11 Dec 2016 12:22:23 -0500 Subject: [PATCH 16/37] consmetic fixes --- WebSocks.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/WebSocks.cpp b/WebSocks.cpp index 6787d390..1b438b01 100644 --- a/WebSocks.cpp +++ b/WebSocks.cpp @@ -199,7 +199,6 @@ namespace client LogPrint(eLogDebug, "websocks: connected to ", m_RemoteAddr, ":", m_RemotePort); SendResponse(""); m_State = eWSCOkayConnect; - StartForwarding(); } else if(state == eWSCFailConnect) { // we did not connect okay LogPrint(eLogDebug, "websocks: failed to connect to ", m_RemoteAddr, ":", m_RemotePort); @@ -252,6 +251,8 @@ namespace client void StartForwarding() { LogPrint(eLogDebug, "websocks: begin forwarding data"); + uint8_t b[1]; + m_Stream->Send(b, 0); AsyncRecv(); } @@ -264,15 +265,17 @@ namespace client } else { // forward data LogPrint(eLogDebug, "websocks recv ", n); - std::string str((char*)m_RecvBuf, n); - auto conn = m_Parent->GetConn(m_Conn); - if(!conn) { - LogPrint(eLogWarning, "websocks: connection is gone"); - EnterState(eWSCClose); - return; - } - conn->send(str); - AsyncRecv(); + + std::string str((char*)m_RecvBuf, n); + auto conn = m_Parent->GetConn(m_Conn); + if(!conn) { + LogPrint(eLogWarning, "websocks: connection is gone"); + EnterState(eWSCClose); + return; + } + conn->send(str); + AsyncRecv(); + } } @@ -280,7 +283,7 @@ namespace client { m_Stream->AsyncReceive( boost::asio::buffer(m_RecvBuf, sizeof(m_RecvBuf)), - std::bind(&WebSocksConn::HandleAsyncRecv, this, std::placeholders::_1, std::placeholders::_2)); + std::bind(&WebSocksConn::HandleAsyncRecv, this, std::placeholders::_1, std::placeholders::_2), 60); } /** @brief send error message or empty string for success */ @@ -310,6 +313,7 @@ namespace client if(m_Stream) { // connect good EnterState(eWSCOkayConnect); + StartForwarding(); } else { // connect failed EnterState(eWSCFailConnect); @@ -323,7 +327,7 @@ namespace client if(m_State == eWSCOkayConnect) { // forward to server - LogPrint(eLogDebug, "websocks: send ", payload.size()); + LogPrint(eLogDebug, "websocks: forward ", payload.size()); m_Stream->Send((uint8_t*)payload.c_str(), payload.size()); } else if (m_State == eWSCInitial) { // recv connect request From 0396c4a4de370e5b2e3c1ca599f19cd098b3b5d6 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 12 Dec 2016 13:40:24 -0500 Subject: [PATCH 17/37] try fixing datagram --- AddressBook.cpp | 2 +- Datagram.cpp | 353 +++++++++++++++++------------------------------- Datagram.h | 74 +++++----- 3 files changed, 153 insertions(+), 276 deletions(-) diff --git a/AddressBook.cpp b/AddressBook.cpp index db424308..685acbc2 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -842,7 +842,7 @@ namespace client else memset (response + 8, 0, 32); // not found memset (response + 40, 0, 4); // set expiration time to zero - m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash (), toPort, fromPort); + m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash(), toPort, fromPort); } void AddressResolver::AddAddress (const std::string& name, const i2p::data::IdentHash& ident) diff --git a/Datagram.cpp b/Datagram.cpp index d0b0737a..a06a8e5d 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -22,9 +22,9 @@ namespace datagram { m_Sessions.clear(); } - - void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort, uint16_t toPort) - { + + void DatagramDestination::SendDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort) + { auto owner = m_Owner; std::vector v(MAX_DATAGRAM_SIZE); uint8_t * buf = v.data(); @@ -45,8 +45,7 @@ namespace datagram owner->Sign (buf1, len, signature); auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort); - auto session = ObtainSession(ident); - session->SendMsg(msg); + ObtainSession(identity)->SendMsg(msg); } @@ -69,6 +68,8 @@ namespace datagram if (verified) { + auto h = identity.GetIdentHash(); + ObtainSession(h)->Ack(); auto r = FindReceiver(toPort); if(r) r(identity, fromPort, toPort, buf + headerLen, len -headerLen); @@ -138,15 +139,15 @@ namespace datagram } } - std::shared_ptr DatagramDestination::ObtainSession(const i2p::data::IdentHash & ident) + std::shared_ptr DatagramDestination::ObtainSession(const i2p::data::IdentHash & identity) { std::shared_ptr session = nullptr; std::lock_guard lock(m_SessionsMutex); - auto itr = m_Sessions.find(ident); + auto itr = m_Sessions.find(identity); if (itr == m_Sessions.end()) { // not found, create new session - session = std::make_shared(m_Owner, ident); - m_Sessions[ident] = session; + session = std::make_shared(m_Owner, identity); + m_Sessions[identity] = session; } else { session = itr->second; } @@ -164,13 +165,13 @@ namespace datagram } DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination, - const i2p::data::IdentHash & remoteIdent) : + const i2p::data::IdentHash & remoteIdent) : m_LocalDestination(localDestination), - m_RemoteIdentity(remoteIdent), - m_LastUse(i2p::util::GetMillisecondsSinceEpoch ()), - m_LastPathChange(0), - m_LastSuccess(0) + m_RemoteIdent(remoteIdent), + m_SendQueueTimer(localDestination->GetService()) { + m_LastUse = i2p::util::GetMillisecondsSinceEpoch (); + ScheduleFlushSendQueue(); } void DatagramSession::SendMsg(std::shared_ptr msg) @@ -184,262 +185,150 @@ namespace datagram DatagramSession::Info DatagramSession::GetSessionInfo() const { if(!m_RoutingSession) - return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess); + return DatagramSession::Info(nullptr, nullptr, m_LastUse); auto routingPath = m_RoutingSession->GetSharedRoutingPath(); if (!routingPath) - return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess); + return DatagramSession::Info(nullptr, nullptr, m_LastUse); auto lease = routingPath->remoteLease; auto tunnel = routingPath->outboundTunnel; if(lease) { if(tunnel) - return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse, m_LastSuccess); + return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse); else - return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse, m_LastSuccess); + return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse); } else if(tunnel) - return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse, m_LastSuccess); + return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse); else - return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess); - } - - void DatagramSession::HandleSend(std::shared_ptr msg) - { - if(!m_RoutingSession) - { - // try to get one - if(m_RemoteLeaseSet) m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); - else - { - UpdateLeaseSet(msg); - return; - } - } - // do we have a routing session? - if(m_RoutingSession) - { - // should we switch paths? - if(ShouldUpdateRoutingPath ()) - { - LogPrint(eLogDebug, "DatagramSession: try getting new routing path"); - // try switching paths - auto path = GetNextRoutingPath(); - if(path) - UpdateRoutingPath (path); - else - ResetRoutingPath(); - } - auto routingPath = m_RoutingSession->GetSharedRoutingPath (); - // make sure we have a routing path - if (routingPath) - { - auto outboundTunnel = routingPath->outboundTunnel; - if (outboundTunnel) - { - if(outboundTunnel->IsEstablished()) - { - m_LastSuccess = i2p::util::GetMillisecondsSinceEpoch (); - // we have a routing path and routing session and the outbound tunnel we are using is good - // wrap message with routing session and send down routing path's outbound tunnel wrapped for the IBGW - auto m = m_RoutingSession->WrapSingleMessage(msg); - routingPath->outboundTunnel->SendTunnelDataMsg({i2p::tunnel::TunnelMessageBlock{ - i2p::tunnel::eDeliveryTypeTunnel, - routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, - m - }}); - return; - } - } - } - } - auto now = i2p::util::GetMillisecondsSinceEpoch (); - // if this path looks dead reset the routing path since we didn't seem to be able to get a path in time - if (m_LastPathChange && now - m_LastPathChange >= DATAGRAM_SESSION_PATH_TIMEOUT ) ResetRoutingPath(); - UpdateLeaseSet(msg); - - } - - void DatagramSession::UpdateRoutingPath(const std::shared_ptr & path) - { - if(m_RoutingSession == nullptr && m_RemoteLeaseSet) - m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); - if(!m_RoutingSession) return; - // set routing path and update time we last updated the routing path - m_RoutingSession->SetSharedRoutingPath (path); - m_LastPathChange = i2p::util::GetMillisecondsSinceEpoch (); + return DatagramSession::Info(nullptr, nullptr, m_LastUse); } - bool DatagramSession::ShouldUpdateRoutingPath() const + void DatagramSession::Ack() { - bool dead = m_RoutingSession == nullptr || m_RoutingSession->GetSharedRoutingPath () == nullptr; - auto now = i2p::util::GetMillisecondsSinceEpoch (); - // we need to rotate paths becuase the routing path is too old - // if (now - m_LastPathChange >= DATAGRAM_SESSION_PATH_SWITCH_INTERVAL) return true; - // too fast switching paths - if (now - m_LastPathChange < DATAGRAM_SESSION_PATH_MIN_LIFETIME ) return false; - // our path looks dead so we need to rotate paths - if (now - m_LastSuccess >= DATAGRAM_SESSION_PATH_TIMEOUT) return !dead; - // if we have a routing session and routing path we don't need to switch paths - return dead; + m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); + auto path = GetSharedRoutingPath(); + if(path) + path->updateTime = i2p::util::GetSecondsSinceEpoch (); } - - bool DatagramSession::ShouldSwitchLease() const - { - std::shared_ptr routingPath = nullptr; - std::shared_ptr currentLease = nullptr; - if(m_RoutingSession) - routingPath = m_RoutingSession->GetSharedRoutingPath (); - if(routingPath) - currentLease = routingPath->remoteLease; - if(currentLease) // if we have a lease return true if it's about to expire otherwise return false - return currentLease->ExpiresWithin( DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE ); - // we have no current lease, we should switch - return currentLease == nullptr; - } - - std::shared_ptr DatagramSession::GetNextRoutingPath() + std::shared_ptr DatagramSession::GetSharedRoutingPath () { - std::shared_ptr outboundTunnel = nullptr; - std::shared_ptr routingPath = nullptr; - // get existing routing path if we have one - if(m_RoutingSession) - routingPath = m_RoutingSession->GetSharedRoutingPath(); - // do we have an existing outbound tunnel and routing path? - if(routingPath && routingPath->outboundTunnel) - { - // is the outbound tunnel we are using good? - if (routingPath->outboundTunnel->IsEstablished()) - { - // ya so let's stick with it - outboundTunnel = routingPath->outboundTunnel; + if(!m_RoutingSession) { + if(!m_RemoteLeaseSet) { + m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); } - else - outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(routingPath->outboundTunnel); // no so we'll switch outbound tunnels + if(!m_RemoteLeaseSet) { + // no remote lease set + return nullptr; + } + m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); } - // do we have an outbound tunnel that works already ? - if(!outboundTunnel) - outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(); // no, let's get a new outbound tunnel as we probably just started - - if(outboundTunnel) - { - std::shared_ptr lease = nullptr; - // should we switch leases ? - if (ShouldSwitchLease ()) - { - // yes, get next available lease - lease = GetNextLease(); + auto path = m_RoutingSession->GetSharedRoutingPath(); + if(path) { + if (m_CurrentOutboundTunnel && !m_CurrentOutboundTunnel->IsEstablished()) { + // bad outbound tunnel, switch outbound tunnel + m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel); + path->outboundTunnel = m_CurrentOutboundTunnel; } - else if (routingPath) - { - if(routingPath->remoteLease) - { - if(routingPath->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE)) - lease = GetNextLease(); - else - lease = routingPath->remoteLease; + if(m_CurrentRemoteLease && ! m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) { + // bad lease, switch to next one + if(m_RemoteLeaseSet) { + auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&](const i2p::data::Lease& l) -> bool { + return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway || l.endDate <= m_CurrentRemoteLease->endDate; + }); + auto sz = ls.size(); + if (sz) { + auto idx = rand() % sz; + m_CurrentRemoteLease = ls[idx]; + } else { + // no more leases, bail + LogPrint(eLogWarning, "DatagramSession: no more valid remote leases to ", m_RemoteIdent.ToBase32()); + } + } else { + // no remote lease set? + LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32()); } + path->remoteLease = m_CurrentRemoteLease; } - else - lease = GetNextLease(); - if(lease) - { - // we have a valid lease to use and an outbound tunnel - // create new routing path - uint32_t now = i2p::util::GetSecondsSinceEpoch(); - routingPath = std::make_shared(i2p::garlic::GarlicRoutingPath{ - outboundTunnel, - lease, - 0, - now, - 0 - }); + } else { + // no current path, make one + path = std::make_shared(); + // switch outbound tunnel if bad + if(m_CurrentOutboundTunnel == nullptr || ! m_CurrentOutboundTunnel->IsEstablished()) { + m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel); + } + // switch lease if bad + if(m_CurrentRemoteLease == nullptr || m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) { + if(!m_RemoteLeaseSet) { + m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); + } + if(m_RemoteLeaseSet) { + // pick random next good lease + auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&] (const i2p::data::Lease & l) -> bool { + if(m_CurrentRemoteLease) + return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway; + return false; + }); + auto sz = ls.size(); + if(sz) { + auto idx = rand() % sz; + m_CurrentRemoteLease = ls[idx]; + } + } else { + // no remote lease set currently, bail + LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32()); + return nullptr; + } } - else // we don't have a new routing path to give - routingPath = nullptr; + path->outboundTunnel = m_CurrentOutboundTunnel; + path->remoteLease = m_CurrentRemoteLease; + m_RoutingSession->SetSharedRoutingPath(path); } - return routingPath; + return path; + } - void DatagramSession::ResetRoutingPath() + void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr ls) { - if(m_RoutingSession) - { - auto routingPath = m_RoutingSession->GetSharedRoutingPath(); - if(routingPath && routingPath->remoteLease) // we have a remote lease already specified and a routing path - { - // get outbound tunnel on this path - auto outboundTunnel = routingPath->outboundTunnel; - // is this outbound tunnel there and established - if (outboundTunnel && outboundTunnel->IsEstablished()) - m_InvalidIBGW.push_back(routingPath->remoteLease->tunnelGateway); // yes, let's mark remote lease as dead because the outbound tunnel seems fine - } - // reset the routing path - UpdateRoutingPath(nullptr); - } + // only update lease set if found and newer than previous lease set + uint64_t oldExpire = 0; + if(m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime(); + if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls; } - std::shared_ptr DatagramSession::GetNextLease() + void DatagramSession::HandleSend(std::shared_ptr msg) { - auto now = i2p::util::GetMillisecondsSinceEpoch (); - std::shared_ptr next = nullptr; - if(m_RemoteLeaseSet) + m_SendQueue.push_back(msg); + // flush queue right away if full + if(m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) FlushSendQueue(); + } + + void DatagramSession::FlushSendQueue () + { + + std::vector send; + auto routingPath = GetSharedRoutingPath(); + // if we don't have a routing path we will drop all queued messages + if(routingPath) { - std::vector exclude; - for(const auto & ident : m_InvalidIBGW) - exclude.push_back(ident); - // find get all leases that are not in our ban list and are not going to expire within our lease set handover window + fudge - auto leases = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding( [&exclude, now] (const i2p::data::Lease & l) -> bool { - if(exclude.size()) - { - auto end = std::end(exclude); - return std::find_if(exclude.begin(), end, [l, now] ( const i2p::data::IdentHash & ident) -> bool { - return ident == l.tunnelGateway; - }) != end; - } - else - return false; - }); - if(leases.size()) + for (const auto & msg : m_SendQueue) { - // pick random valid next lease - uint32_t idx = rand() % leases.size(); - next = leases[idx]; + auto m = m_RoutingSession->WrapSingleMessage(msg); + send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, m}); } - else - LogPrint(eLogWarning, "DatagramDestination: no leases to use"); + routingPath->outboundTunnel->SendTunnelDataMsg(send); } - return next; - } - - void DatagramSession::UpdateLeaseSet(std::shared_ptr msg) - { - LogPrint(eLogInfo, "DatagramSession: updating lease set"); - m_LocalDestination->RequestDestination(m_RemoteIdentity, std::bind(&DatagramSession::HandleGotLeaseSet, this, std::placeholders::_1, msg)); + m_SendQueue.clear(); + ScheduleFlushSendQueue(); } - void DatagramSession::HandleGotLeaseSet(std::shared_ptr remoteIdent, std::shared_ptr msg) + void DatagramSession::ScheduleFlushSendQueue() { - if(remoteIdent) - { - // update routing session - if(m_RoutingSession) - m_RoutingSession = nullptr; - m_RoutingSession = m_LocalDestination->GetRoutingSession(remoteIdent, true); - // clear invalid IBGW as we have a new lease set - m_InvalidIBGW.clear(); - m_RemoteLeaseSet = remoteIdent; - // update routing path - auto path = GetNextRoutingPath(); - if (path) - UpdateRoutingPath(path); - else - ResetRoutingPath(); - // send the message that was queued if it was provided - if(msg) - HandleSend(msg); - } + boost::posix_time::milliseconds dlt(100); + m_SendQueueTimer.expires_from_now(dlt); + m_SendQueueTimer.async_wait([&](const boost::system::error_code & ec) { if(ec) return; FlushSendQueue(); }); } } } diff --git a/Datagram.h b/Datagram.h index dc63cccb..8891f0cc 100644 --- a/Datagram.h +++ b/Datagram.h @@ -31,29 +31,33 @@ namespace datagram const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000; // milliseconds minimum time between path switches const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000; + // max 64 messages buffered in send queue for each datagram session + const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64; class DatagramSession { public: DatagramSession(i2p::client::ClientDestination * localDestination, - const i2p::data::IdentHash & remoteIdent); + const i2p::data::IdentHash & remoteIdent); + + + /** @brief ack the garlic routing path */ + void Ack(); /** send an i2np message to remote endpoint for this session */ void SendMsg(std::shared_ptr msg); /** get the last time in milliseconds for when we used this datagram session */ uint64_t LastActivity() const { return m_LastUse; } - /** get the last time in milliseconds when we successfully sent data */ - uint64_t LastSuccess() const { return m_LastSuccess; } + struct Info { std::shared_ptr IBGW; std::shared_ptr OBEP; const uint64_t activity; - const uint64_t success; - Info() : IBGW(nullptr), OBEP(nullptr), activity(0), success(0) {} - Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a, const uint64_t s) : - activity(a), - success(s) { + + Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {} + Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a) : + activity(a) { if(ibgw) IBGW = std::make_shared(ibgw); else IBGW = nullptr; if(obep) OBEP = std::make_shared(obep); @@ -63,44 +67,28 @@ namespace datagram Info GetSessionInfo() const; - private: - /** update our routing path we are using, mark that we have changed paths */ - void UpdateRoutingPath(const std::shared_ptr & path); + void FlushSendQueue(); + void ScheduleFlushSendQueue(); - /** return true if we should switch routing paths because of path lifetime or timeout otherwise false */ - bool ShouldUpdateRoutingPath() const; + void HandleSend(std::shared_ptr msg); + + std::shared_ptr GetSharedRoutingPath(); + + void HandleLeaseSetUpdated(std::shared_ptr ls); - /** return true if we should switch the lease for out routing path otherwise return false */ - bool ShouldSwitchLease() const; - - /** get next usable routing path, try reusing outbound tunnels */ - std::shared_ptr GetNextRoutingPath(); - /** - * mark current routing path as invalid and clear it - * if the outbound tunnel we were using was okay don't use the IBGW in the routing path's lease next time - */ - void ResetRoutingPath(); - - /** get next usable lease, does not fetch or update if expired or have no lease set */ - std::shared_ptr GetNextLease(); - - void HandleSend(std::shared_ptr msg); - void HandleGotLeaseSet(std::shared_ptr remoteIdent, - std::shared_ptr msg); - void UpdateLeaseSet(std::shared_ptr msg=nullptr); - private: i2p::client::ClientDestination * m_LocalDestination; - i2p::data::IdentHash m_RemoteIdentity; - std::shared_ptr m_RoutingSession; - // Ident hash of IBGW that are invalid - std::vector m_InvalidIBGW; - std::shared_ptr m_RemoteLeaseSet; - uint64_t m_LastUse; - uint64_t m_LastPathChange; - uint64_t m_LastSuccess; + i2p::data::IdentHash m_RemoteIdent; + std::shared_ptr m_RemoteLeaseSet; + std::shared_ptr m_RoutingSession; + std::shared_ptr m_CurrentRemoteLease; + std::shared_ptr m_CurrentOutboundTunnel; + boost::asio::deadline_timer m_SendQueueTimer; + std::vector > m_SendQueue; + uint64_t m_LastUse; + }; const size_t MAX_DATAGRAM_SIZE = 32768; @@ -112,9 +100,9 @@ namespace datagram DatagramDestination (std::shared_ptr owner); - ~DatagramDestination (); + ~DatagramDestination (); - void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0); + void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; }; @@ -130,7 +118,7 @@ namespace datagram private: - std::shared_ptr ObtainSession(const i2p::data::IdentHash & ident); + std::shared_ptr ObtainSession(const i2p::data::IdentHash & ident); std::shared_ptr CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); From 5640c96fd5d14ea115b5b8889a470cafba2c0cdb Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 12 Dec 2016 14:39:05 -0500 Subject: [PATCH 18/37] request lease set --- Datagram.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Datagram.cpp b/Datagram.cpp index a06a8e5d..ca13863e 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -221,6 +221,7 @@ namespace datagram } if(!m_RemoteLeaseSet) { // no remote lease set + m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1)); return nullptr; } m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); From 82e955ec026514a4aa5b9051159460722629dfa3 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 12 Dec 2016 18:54:31 -0500 Subject: [PATCH 19/37] fix --- Datagram.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Datagram.cpp b/Datagram.cpp index ca13863e..9d0bcbcd 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -233,7 +233,7 @@ namespace datagram m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel); path->outboundTunnel = m_CurrentOutboundTunnel; } - if(m_CurrentRemoteLease && ! m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) { + if(m_CurrentRemoteLease && m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) { // bad lease, switch to next one if(m_RemoteLeaseSet) { auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&](const i2p::data::Lease& l) -> bool { From 92dd68fca1ab540dcaddf706da1926184fae3205 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 12 Dec 2016 18:54:31 -0500 Subject: [PATCH 20/37] fix --- Datagram.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Datagram.cpp b/Datagram.cpp index ca13863e..9d0bcbcd 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -233,7 +233,7 @@ namespace datagram m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel); path->outboundTunnel = m_CurrentOutboundTunnel; } - if(m_CurrentRemoteLease && ! m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) { + if(m_CurrentRemoteLease && m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) { // bad lease, switch to next one if(m_RemoteLeaseSet) { auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&](const i2p::data::Lease& l) -> bool { From b4e7a9164554e9e8cb09ab3c244f89cf4afb3e07 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 12 Dec 2016 19:16:02 -0500 Subject: [PATCH 21/37] be less picky about next lease set --- Datagram.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Datagram.cpp b/Datagram.cpp index 9d0bcbcd..b2fc8aee 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -237,7 +237,7 @@ namespace datagram // bad lease, switch to next one if(m_RemoteLeaseSet) { auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&](const i2p::data::Lease& l) -> bool { - return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway || l.endDate <= m_CurrentRemoteLease->endDate; + return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway; }); auto sz = ls.size(); if (sz) { From 573e5eb5bd2a638399f64195d0b7c05bf41bf7e0 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 13 Dec 2016 09:10:39 -0500 Subject: [PATCH 22/37] fix typo --- WebSocks.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/WebSocks.cpp b/WebSocks.cpp index 1b438b01..a9a8751b 100644 --- a/WebSocks.cpp +++ b/WebSocks.cpp @@ -1,5 +1,6 @@ #include "WebSocks.h" #include "Log.h" +#include #ifdef WITH_EVENTS #include "ClientContext.h" @@ -382,14 +383,24 @@ namespace client class WebSocksImpl { public: - WebSocks(const std::string & addr, int port) {} - ~WebSocks(){} + WebSocksImpl(const std::string & addr, int port) + { + } + + ~WebSocksImpl() + { + } + void Start() { LogPrint(eLogInfo, "WebSockets not enabled on compile time"); } - void Stop() {} + void Stop() + { + + } + }; } } From ae3bb30d8a34453e98479915d3e5ea56f80f268c Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 17 Dec 2016 06:35:38 -0500 Subject: [PATCH 23/37] don't crash on os x when no lease set found for udp tunnel --- Datagram.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Datagram.cpp b/Datagram.cpp index 9d0bcbcd..e2bce1cf 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -293,6 +293,7 @@ namespace datagram void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr ls) { + if(!ls) return; // only update lease set if found and newer than previous lease set uint64_t oldExpire = 0; if(m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime(); From 08001ba373ce0feb7e0c5c66a85433bb00be0268 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 17 Dec 2016 06:37:34 -0500 Subject: [PATCH 24/37] remove pedantic log entry --- Datagram.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Datagram.cpp b/Datagram.cpp index e2bce1cf..12aff188 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -243,9 +243,6 @@ namespace datagram if (sz) { auto idx = rand() % sz; m_CurrentRemoteLease = ls[idx]; - } else { - // no more leases, bail - LogPrint(eLogWarning, "DatagramSession: no more valid remote leases to ", m_RemoteIdent.ToBase32()); } } else { // no remote lease set? From 67927bd8f405f9f11c123de8e5dcb93ab7b11951 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 18 Dec 2016 11:49:50 -0500 Subject: [PATCH 25/37] enable multiple acceptors in sam (initial) --- SAM.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++------------- SAM.h | 16 ++++++++--- 2 files changed, 76 insertions(+), 22 deletions(-) diff --git a/SAM.cpp b/SAM.cpp index 1fc1227a..ebb90aeb 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -54,11 +54,7 @@ namespace client case eSAMSocketTypeAcceptor: { if (m_Session) - { m_Session->DelSocket (shared_from_this ()); - if (m_Session->localDestination) - m_Session->localDestination->StopAcceptingStreams (); - } break; } default: @@ -289,6 +285,11 @@ namespace client dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); } + else + { + // start accepting streams because we're not a datagram session + m_Session->localDestination->AcceptStreams (std::bind (&SAMSession::AcceptI2P, m_Session, std::placeholders::_1)); + } if (m_Session->localDestination->IsReady ()) SendSessionCreateReplyOk (); @@ -401,20 +402,24 @@ namespace client m_Session = m_Owner.FindSession (id); if (m_Session) { - if (!m_Session->localDestination->IsAcceptingStreams ()) - { - m_SocketType = eSAMSocketTypeAcceptor; - m_Session->AddSocket (shared_from_this ()); - m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } - else - SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true); + m_SocketType = eSAMSocketTypeAcceptor; + m_Session->AddSocket (shared_from_this ()); } else SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); } + void SAMSocket::Accept(std::shared_ptr stream) + { + if(stream) { + m_SocketType = eSAMSocketTypeStream; + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + HandleI2PAccept(stream); + } else { + SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true); + Terminate(); + } + } size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data) { LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len); @@ -659,10 +664,6 @@ namespace client LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID); m_Stream = stream; context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ()); - auto session = m_Owner.FindSession (m_ID); - if (session) - session->localDestination->StopAcceptingStreams (); - m_SocketType = eSAMSocketTypeStream; if (!m_IsSilent) { // get remote peer address @@ -704,8 +705,10 @@ namespace client } SAMSession::SAMSession (std::shared_ptr dest): - localDestination (dest) + localDestination (dest), + m_BacklogPumper(dest->GetService()) { + PumpBacklog(); } SAMSession::~SAMSession () @@ -714,6 +717,42 @@ namespace client i2p::client::context.DeleteLocalDestination (localDestination); } + void SAMSession::AcceptI2P(std::shared_ptr stream) + { + if(!stream) return; // fail + std::unique_lock lock(m_SocketsMutex); + if(m_Backlog.size() > SAM_MAX_ACCEPT_BACKLOG) { + stream->Close(); + return; + } + m_Backlog.push_back(stream); + } + + void SAMSession::PumpBacklog() + { + // pump backlog every 100ms + boost::posix_time::milliseconds dlt(100); + m_BacklogPumper.expires_from_now(dlt); + m_BacklogPumper.async_wait(std::bind(&SAMSession::HandlePumpBacklog, this, std::placeholders::_1)); + } + + void SAMSession::HandlePumpBacklog(const boost::system::error_code & ec) + { + if(ec) return; + + std::unique_lock lock(m_SocketsMutex); + for(auto & stream : m_Backlog) { + for (auto & sock : m_Sockets) { + auto t = sock->GetSocketType(); + if(t == eSAMSocketTypeAcceptor) { + sock->Accept(stream); + break; + } + } + } + PumpBacklog(); + } + void SAMSession::CloseStreams () { { @@ -721,9 +760,13 @@ namespace client for (auto& sock : m_Sockets) { sock->CloseStream(); } + for(auto & stream : m_Backlog) { + stream->Close(); + } } // XXX: should this be done inside locked parts? m_Sockets.clear(); + m_Backlog.clear(); } SAMBridge::SAMBridge (const std::string& address, int port): @@ -834,8 +877,9 @@ namespace client auto session = std::make_shared(localDestination); std::unique_lock l(m_SessionsMutex); auto ret = m_Sessions.insert (std::make_pair(id, session)); - if (!ret.second) + if (!ret.second) { LogPrint (eLogWarning, "SAM: Session ", id, " already exists"); + } return ret.first->second; } return nullptr; diff --git a/SAM.h b/SAM.h index db08c5a0..26c6ea29 100644 --- a/SAM.h +++ b/SAM.h @@ -20,7 +20,8 @@ namespace client { const size_t SAM_SOCKET_BUFFER_SIZE = 8192; const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds - const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds + const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds + const int SAM_MAX_ACCEPT_BACKLOG = 50; const char SAM_HANDSHAKE[] = "HELLO VERSION"; const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n"; @@ -84,6 +85,8 @@ namespace client void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; }; SAMSocketType GetSocketType () const { return m_SocketType; }; + void Accept(std::shared_ptr stream); + private: void Terminate (); @@ -134,6 +137,8 @@ namespace client struct SAMSession { std::shared_ptr localDestination; + boost::asio::deadline_timer m_BacklogPumper; + std::list > m_Backlog; std::list > m_Sockets; std::mutex m_SocketsMutex; @@ -158,10 +163,15 @@ namespace client } return l; } - - SAMSession (std::shared_ptr dest); + + SAMSession (std::shared_ptr dest); ~SAMSession (); + void AcceptI2P(std::shared_ptr stream); + + void PumpBacklog(); + void HandlePumpBacklog(const boost::system::error_code & ec); + void CloseStreams (); }; From 042adb5e341c70fe13da385d22cd6e1cacaaf93c Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 18 Dec 2016 12:28:32 -0500 Subject: [PATCH 26/37] fix termination crash --- SAM.cpp | 56 ++++++++++++++++++++++++++++++++++---------------------- SAM.h | 2 ++ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/SAM.cpp b/SAM.cpp index ebb90aeb..5b21af51 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -404,6 +404,7 @@ namespace client { m_SocketType = eSAMSocketTypeAcceptor; m_Session->AddSocket (shared_from_this ()); + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); } else SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); @@ -413,7 +414,6 @@ namespace client { if(stream) { m_SocketType = eSAMSocketTypeStream; - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); HandleI2PAccept(stream); } else { SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true); @@ -713,7 +713,6 @@ namespace client SAMSession::~SAMSession () { - CloseStreams(); i2p::client::context.DeleteLocalDestination (localDestination); } @@ -736,17 +735,30 @@ namespace client m_BacklogPumper.async_wait(std::bind(&SAMSession::HandlePumpBacklog, this, std::placeholders::_1)); } + std::shared_ptr SAMSession::FindAcceptor() + { + for (auto & sock : m_Sockets) { + auto t = sock->GetSocketType(); + if(t == eSAMSocketTypeAcceptor) { + return sock; + } + } + return nullptr; + } + void SAMSession::HandlePumpBacklog(const boost::system::error_code & ec) { if(ec) return; - - std::unique_lock lock(m_SocketsMutex); - for(auto & stream : m_Backlog) { - for (auto & sock : m_Sockets) { - auto t = sock->GetSocketType(); - if(t == eSAMSocketTypeAcceptor) { - sock->Accept(stream); - break; + { + std::unique_lock lock(m_SocketsMutex); + auto itr = m_Backlog.begin(); + while(itr != m_Backlog.end()) { + auto sock = FindAcceptor(); + if (sock) { + sock->Accept(*itr); + itr = m_Backlog.erase(itr); + } else { + ++itr; } } } @@ -755,18 +767,18 @@ namespace client void SAMSession::CloseStreams () { - { - std::lock_guard lock(m_SocketsMutex); - for (auto& sock : m_Sockets) { - sock->CloseStream(); - } - for(auto & stream : m_Backlog) { - stream->Close(); - } - } - // XXX: should this be done inside locked parts? - m_Sockets.clear(); - m_Backlog.clear(); + localDestination->GetService().post([&] () { + std::lock_guard lock(m_SocketsMutex); + for (auto& sock : m_Sockets) { + sock->CloseStream(); + } + for(auto & stream : m_Backlog) { + stream->Close(); + } + // XXX: should this be done inside locked parts? + m_Sockets.clear(); + m_Backlog.clear(); + }); } SAMBridge::SAMBridge (const std::string& address, int port): diff --git a/SAM.h b/SAM.h index 26c6ea29..810a1bb0 100644 --- a/SAM.h +++ b/SAM.h @@ -169,6 +169,8 @@ namespace client void AcceptI2P(std::shared_ptr stream); + std::shared_ptr FindAcceptor(); + void PumpBacklog(); void HandlePumpBacklog(const boost::system::error_code & ec); From 965896b93239ca6d736cd52a7d218496b6236959 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 18 Dec 2016 12:56:34 -0500 Subject: [PATCH 27/37] fix sam crash on exit and datagram crash with no outbound tunnel --- Datagram.cpp | 2 +- SAM.cpp | 28 ++++++++++++---------------- SAM.h | 1 - 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/Datagram.cpp b/Datagram.cpp index f8437108..7066a153 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -310,7 +310,7 @@ namespace datagram std::vector send; auto routingPath = GetSharedRoutingPath(); // if we don't have a routing path we will drop all queued messages - if(routingPath) + if(routingPath && routingPath->outboundTunnel && routingPath->remoteLease) { for (const auto & msg : m_SendQueue) { diff --git a/SAM.cpp b/SAM.cpp index 5b21af51..5bf9a74b 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -710,11 +710,6 @@ namespace client { PumpBacklog(); } - - SAMSession::~SAMSession () - { - i2p::client::context.DeleteLocalDestination (localDestination); - } void SAMSession::AcceptI2P(std::shared_ptr stream) { @@ -767,18 +762,19 @@ namespace client void SAMSession::CloseStreams () { + m_BacklogPumper.cancel(); localDestination->GetService().post([&] () { - std::lock_guard lock(m_SocketsMutex); - for (auto& sock : m_Sockets) { - sock->CloseStream(); - } - for(auto & stream : m_Backlog) { - stream->Close(); - } - // XXX: should this be done inside locked parts? - m_Sockets.clear(); - m_Backlog.clear(); - }); + std::lock_guard lock(m_SocketsMutex); + for (auto& sock : m_Sockets) { + sock->CloseStream(); + } + for(auto & stream : m_Backlog) { + stream->Close(); + } + m_Sockets.clear(); + m_Backlog.clear(); + i2p::client::context.DeleteLocalDestination (localDestination); + }); } SAMBridge::SAMBridge (const std::string& address, int port): diff --git a/SAM.h b/SAM.h index 810a1bb0..7d7d6d3a 100644 --- a/SAM.h +++ b/SAM.h @@ -165,7 +165,6 @@ namespace client } SAMSession (std::shared_ptr dest); - ~SAMSession (); void AcceptI2P(std::shared_ptr stream); From 3dcc4e6bc183f403689eabf7042b6246fc40c325 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 23 Dec 2016 07:27:34 -0500 Subject: [PATCH 28/37] i2ptunnel fixes --- I2PTunnel.cpp | 19 ++++++++++--------- I2PTunnel.h | 8 +++++--- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 3527a328..756a22c2 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -177,7 +177,7 @@ namespace client { if (bytes_transferred > 0) Write (m_StreamBuffer, bytes_transferred); // postpone termination - else if (ecode == boost::asio::error::timed_out && m_Stream->IsOpen ()) + else if (ecode == boost::asio::error::timed_out && && m_Stream && m_Stream->IsOpen ()) StreamReceive (); else Terminate (); @@ -540,7 +540,6 @@ namespace client auto session = ObtainUDPSession(from, toPort, fromPort); session->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); session->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); - } void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) { @@ -554,6 +553,7 @@ namespace client ++itr; } } +<<<<<<< HEAD void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) { std::lock_guard lock(m_SessionsMutex); @@ -568,7 +568,7 @@ namespace client } } - std::shared_ptr I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort) + UDPSessionPtr I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort) { auto ih = from.GetIdentHash(); for (auto & s : m_Sessions ) @@ -652,7 +652,8 @@ namespace client { std::vector > sessions; std::lock_guard lock(m_SessionsMutex); - for (auto & s : m_Sessions ) + + for ( UDPSessionPtr s : m_Sessions ) { if (!s->m_Destination) continue; auto info = s->m_Destination->GetInfoForRemote(s->Identity); @@ -687,7 +688,7 @@ namespace client dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, - std::placeholders::_5)); + std::placeholders::_5)); } @@ -739,10 +740,9 @@ namespace client void I2PUDPClientTunnel::TryResolving() { LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest); - m_RemoteIdent = new i2p::data::IdentHash; - m_RemoteIdent->Fill(0); + i2p::data::IdentHash * h = new i2p::data::IdentHash; - while(!context.GetAddressBook().GetIdentHash(m_RemoteDest, *m_RemoteIdent) && !m_cancel_resolve) + while(!context.GetAddressBook().GetIdentHash(m_RemoteDest, *h) && !m_cancel_resolve) { LogPrint(eLogWarning, "UDP Tunnel: failed to lookup ", m_RemoteDest); std::this_thread::sleep_for(std::chrono::seconds(1)); @@ -753,6 +753,7 @@ namespace client return; } LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32()); + m_RemoteIdent = h; } void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) @@ -789,7 +790,7 @@ namespace client if(m_LocalSocket.is_open()) m_LocalSocket.close(); - + m_cancel_resolve = true; if(m_ResolveThread) diff --git a/I2PTunnel.h b/I2PTunnel.h index b49efe40..bde3d820 100644 --- a/I2PTunnel.h +++ b/I2PTunnel.h @@ -18,7 +18,7 @@ namespace i2p { namespace client { - const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192; + const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 65536; const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds // for HTTP tunnels @@ -182,6 +182,8 @@ namespace client /** how long has this converstation been idle in ms */ uint64_t idle; }; + + typedef std::shared_ptr UDPSessionPtr; /** server side udp tunnel, many i2p inbound to 1 ip outbound */ class I2PUDPServerTunnel @@ -202,14 +204,14 @@ namespace client private: void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - std::shared_ptr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); + UDPSessionPtr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); private: const std::string m_Name; boost::asio::ip::address m_LocalAddress; boost::asio::ip::udp::endpoint m_RemoteEndpoint; std::mutex m_SessionsMutex; - std::vector > m_Sessions; + std::vector m_Sessions; std::shared_ptr m_LocalDest; }; From 7be951b9626df6b96d5583e65c3f8ef6ec43f7be Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 23 Dec 2016 07:38:41 -0500 Subject: [PATCH 29/37] fix last commit, it was broken --- I2PTunnel.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 756a22c2..7ec4352d 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -177,7 +177,7 @@ namespace client { if (bytes_transferred > 0) Write (m_StreamBuffer, bytes_transferred); // postpone termination - else if (ecode == boost::asio::error::timed_out && && m_Stream && m_Stream->IsOpen ()) + else if (ecode == boost::asio::error::timed_out && m_Stream && m_Stream->IsOpen ()) StreamReceive (); else Terminate (); @@ -553,7 +553,6 @@ namespace client ++itr; } } -<<<<<<< HEAD void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) { std::lock_guard lock(m_SessionsMutex); From 252497280779117c432c05276597cbc97cc66790 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sat, 24 Dec 2016 12:04:39 -0500 Subject: [PATCH 30/37] don't use stack allocated buffers in SSU --- SSUData.cpp | 6 ++++-- SSUSession.cpp | 34 ++++++++++++++++++++++------------ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/SSUData.cpp b/SSUData.cpp index 48e3e3f1..cb20b3e8 100644 --- a/SSUData.cpp +++ b/SSUData.cpp @@ -371,7 +371,7 @@ namespace transport void SSUData::SendMsgAck (uint32_t msgID) { - uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16 + uint8_t * buf = new uint8_t[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16 uint8_t * payload = buf + sizeof (SSUHeader); *payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag payload++; @@ -384,6 +384,7 @@ namespace transport // encrypt message with session key m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); m_Session.Send (buf, 48); + delete [] buf; } void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum) @@ -393,7 +394,7 @@ namespace transport LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64"); return; } - uint8_t buf[64 + 18]; + uint8_t * buf = new uint8_t[64 + 18]; uint8_t * payload = buf + sizeof (SSUHeader); *payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag payload++; @@ -413,6 +414,7 @@ namespace transport // encrypt message with session key m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len); m_Session.Send (buf, len); + delete [] buf; } void SSUData::ScheduleResend() diff --git a/SSUSession.cpp b/SSUSession.cpp index 0e277432..cc181cc0 100644 --- a/SSUSession.cpp +++ b/SSUSession.cpp @@ -350,7 +350,7 @@ namespace transport void SSUSession::SendSessionRequest () { - uint8_t buf[320 + 18]; // 304 bytes for ipv4, 320 for ipv6 + uint8_t * buf = new uint8_t[320 + 18]; // 304 bytes for ipv4, 320 for ipv6 uint8_t * payload = buf + sizeof (SSUHeader); uint8_t flag = 0; // fill extended options, 3 bytes extended options don't change message size @@ -381,6 +381,7 @@ namespace transport RAND_bytes (iv, 16); // random iv FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, isV4 ? 304 : 320, m_IntroKey, iv, m_IntroKey, flag); m_Server.Send (buf, isV4 ? 304 : 320, m_RemoteEndpoint); + delete [] buf; } void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce) @@ -392,7 +393,7 @@ namespace transport return; } - uint8_t buf[96 + 18]; + uint8_t * buf = new uint8_t[96 + 18]; uint8_t * payload = buf + sizeof (SSUHeader); htobe32buf (payload, introducer.iTag); payload += 4; @@ -413,6 +414,7 @@ namespace transport else FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, introducer.iKey, iv, introducer.iKey); m_Server.Send (buf, 96, m_RemoteEndpoint); + delete [] buf; } void SSUSession::SendSessionCreated (const uint8_t * x, bool sendRelayTag) @@ -427,7 +429,7 @@ namespace transport SignedData s; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time s.Insert (x, 256); // x - uint8_t buf[384 + 18]; + uint8_t * buf = new uint8_t[384 + 18]; uint8_t * payload = buf + sizeof (SSUHeader); memcpy (payload, m_DHKeysPair->GetPublicKey (), 256); s.Insert (payload, 256); // y @@ -491,11 +493,12 @@ namespace transport // encrypt message with intro key FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, msgLen, m_IntroKey, iv, m_IntroKey); Send (buf, msgLen); + delete [] buf; } void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen) { - uint8_t buf[512 + 18]; + uint8_t * buf = new uint8_t[512 + 18]; uint8_t * payload = buf + sizeof (SSUHeader); *payload = 1; // 1 fragment payload++; // info @@ -534,6 +537,7 @@ namespace transport // encrypt message with session key FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CONFIRMED, buf, msgLen, m_SessionKey, iv, m_MacKey); Send (buf, msgLen); + delete [] buf; } void SSUSession::ProcessRelayRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from) @@ -561,14 +565,14 @@ namespace transport void SSUSession::SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from, const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to) { - uint8_t buf[80 + 18]; // 64 Alice's ipv4 and 80 Alice's ipv6 - uint8_t * payload = buf + sizeof (SSUHeader); // Charlie's address always v4 if (!to.address ().is_v4 ()) { LogPrint (eLogWarning, "SSU: Charlie's IP must be v4"); return; } + uint8_t * buf = new uint8_t[80 + 18]; // 64 Alice's ipv4 and 80 Alice's ipv6 + uint8_t * payload = buf + sizeof (SSUHeader); *payload = 4; payload++; // size htobe32buf (payload, to.address ().to_v4 ().to_ulong ()); // Charlie's IP @@ -610,6 +614,7 @@ namespace transport m_Server.Send (buf, isV4 ? 64 : 80, from); } LogPrint (eLogDebug, "SSU: relay response sent"); + delete [] buf; } void SSUSession::SendRelayIntro (std::shared_ptr session, const boost::asio::ip::udp::endpoint& from) @@ -621,7 +626,7 @@ namespace transport LogPrint (eLogWarning, "SSU: Alice's IP must be v4"); return; } - uint8_t buf[48 + 18]; + uint8_t * buf = new uint8_t[48 + 18]; uint8_t * payload = buf + sizeof (SSUHeader); *payload = 4; payload++; // size @@ -635,6 +640,7 @@ namespace transport FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, 48, session->m_SessionKey, iv, session->m_MacKey); m_Server.Send (buf, 48, session->m_RemoteEndpoint); LogPrint (eLogDebug, "SSU: relay intro sent"); + delete [] buf; } void SSUSession::ProcessRelayResponse (const uint8_t * buf, size_t len) @@ -1038,7 +1044,7 @@ namespace transport // toAddress is true for Alice<->Chalie communications only // sendAddress is false if message comes from Alice { - uint8_t buf[80 + 18]; + uint8_t * buf = new uint8_t[80 + 18]; uint8_t iv[16]; uint8_t * payload = buf + sizeof (SSUHeader); htobe32buf (payload, nonce); @@ -1094,7 +1100,8 @@ namespace transport // encrypt message with session key FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80); Send (buf, 80); - } + } + delete [] buf; } void SSUSession::SendPeerTest () @@ -1119,7 +1126,7 @@ namespace transport { if (m_State == eSessionStateEstablished) { - uint8_t buf[48 + 18]; + uint8_t * buf = new uint8_t[48 + 18]; uint8_t * payload = buf + sizeof (SSUHeader); *payload = 0; // flags payload++; @@ -1129,6 +1136,7 @@ namespace transport Send (buf, 48); LogPrint (eLogDebug, "SSU: keep-alive sent"); m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + delete [] buf; } } @@ -1136,7 +1144,7 @@ namespace transport { if (m_IsSessionKey) { - uint8_t buf[48 + 18]; + uint8_t * buf = new uint8_t[48 + 18]; // encrypt message with session key FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48); try @@ -1148,12 +1156,13 @@ namespace transport LogPrint (eLogWarning, "SSU: exception while sending session destoroyed: ", ex.what ()); } LogPrint (eLogDebug, "SSU: session destroyed sent"); + delete [] buf; } } void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len) { - uint8_t buf[SSU_MTU_V4 + 18]; + uint8_t * buf = new uint8_t[SSU_MTU_V4 + 18]; size_t msgSize = len + sizeof (SSUHeader); size_t paddingSize = msgSize & 0x0F; // %16 if (paddingSize > 0) msgSize += (16 - paddingSize); @@ -1166,6 +1175,7 @@ namespace transport // encrypt message with session key FillHeaderAndEncrypt (type, buf, msgSize); Send (buf, msgSize); + delete [] buf; } void SSUSession::Send (const uint8_t * buf, size_t size) From 1b0fc180c415c85ee15525af7709edd1fe3341cd Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sat, 24 Dec 2016 16:05:44 -0500 Subject: [PATCH 31/37] Fix Tunnel Gateway Leak --- TunnelGateway.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/TunnelGateway.cpp b/TunnelGateway.cpp index ad423fc0..9f13d84c 100644 --- a/TunnelGateway.cpp +++ b/TunnelGateway.cpp @@ -20,6 +20,7 @@ namespace tunnel TunnelGatewayBuffer::~TunnelGatewayBuffer () { + ClearTunnelDataMsgs (); } void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block) @@ -48,7 +49,7 @@ namespace tunnel di[0] = block.deliveryType << 5; // set delivery type // create fragments - std::shared_ptr msg = block.data; + const std::shared_ptr & msg = block.data; size_t fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length if (fullMsgLen <= m_RemainingSize) { @@ -115,9 +116,13 @@ namespace tunnel m_CurrentTunnelDataMsg->len += s+7; if (isLastFragment) { - m_RemainingSize -= s+7; - if (!m_RemainingSize) - CompleteCurrentTunnelDataMessage (); + if(m_RemainingSize < (s+7)) { + LogPrint (eLogError, "TunnelGateway: remaining size overflow: ", m_RemainingSize, " < ", s+7); + } else { + m_RemainingSize -= s+7; + if (m_RemainingSize == 0) + CompleteCurrentTunnelDataMessage (); + } } else CompleteCurrentTunnelDataMessage (); @@ -138,10 +143,12 @@ namespace tunnel void TunnelGatewayBuffer::ClearTunnelDataMsgs () { m_TunnelDataMsgs.clear (); + m_CurrentTunnelDataMsg = nullptr; } void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage () { + m_CurrentTunnelDataMsg = nullptr; m_CurrentTunnelDataMsg = NewI2NPShortMessage (); m_CurrentTunnelDataMsg->Align (12); // we reserve space for padding @@ -196,7 +203,7 @@ namespace tunnel void TunnelGateway::SendBuffer () { m_Buffer.CompleteCurrentTunnelDataMessage (); - auto tunnelMsgs = m_Buffer.GetTunnelDataMsgs (); + const auto & tunnelMsgs = m_Buffer.GetTunnelDataMsgs (); for (auto& tunnelMsg : tunnelMsgs) { m_Tunnel->EncryptTunnelMsg (tunnelMsg, tunnelMsg); From 4688e6d534bceb3d63412a74f5ee80d39a4d0966 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sat, 24 Dec 2016 16:31:28 -0500 Subject: [PATCH 32/37] fix segfault --- I2PTunnel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 7ec4352d..fe0d5a71 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -751,8 +751,8 @@ namespace client LogPrint(eLogError, "UDP Tunnel: lookup of ", m_RemoteDest, " was cancelled"); return; } - LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32()); m_RemoteIdent = h; + LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32()); } void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) From 858b497199ee7ce8e640ee188ada0027ebfb48a8 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 25 Dec 2016 08:18:23 -0500 Subject: [PATCH 33/37] prevent overflow --- I2PTunnel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index fe0d5a71..57006621 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -212,7 +212,9 @@ namespace client // send destination first like received from I2P std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 (); dest += "\n"; - memcpy (m_StreamBuffer, dest.c_str (), dest.size ()); + if(sizeof(m_StreamBuffer) >= dest.size()) { + memcpy (m_StreamBuffer, dest.c_str (), dest.size ()); + } HandleStreamReceive (boost::system::error_code (), dest.size ()); } Receive (); From 76d9f1ea3767dea3b650978e09b0dd599b206424 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 25 Dec 2016 08:56:47 -0500 Subject: [PATCH 34/37] * make loopback address mapping configurable * add loopback address mapping to udp server tunnel --- ClientContext.cpp | 11 ++++++++-- ClientContext.h | 3 ++- I2PTunnel.cpp | 56 ++++++++++++++++++++++++++++++++--------------- I2PTunnel.h | 9 ++++++-- 4 files changed, 56 insertions(+), 23 deletions(-) diff --git a/ClientContext.cpp b/ClientContext.cpp index 31f5e640..939a9e43 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -493,7 +493,8 @@ namespace client i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256); uint32_t maxConns = section.second.get(i2p::stream::I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN, i2p::stream::DEFAULT_MAX_CONNS_PER_MIN); std::string address = section.second.get (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1"); - + bool mapToLoopback = section.second.get(I2P_SERVER_TUNNEL_MAPTOLOOPBACK, true); + // I2CP std::map options; ReadI2CPOptions (section, options); @@ -512,6 +513,10 @@ namespace client auto localAddress = boost::asio::ip::address::from_string(address); boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port); I2PUDPServerTunnel * serverTunnel = new I2PUDPServerTunnel(name, localDestination, localAddress, endpoint, port); + if(!mapToLoopback) { + LogPrint(eLogInfo, "Clients: disabling loopback address mapping"); + } + serverTunnel->SetMapToLoopback(mapToLoopback); std::lock_guard lock(m_ForwardsMutex); if(m_ServerForwards.insert( std::make_pair( @@ -538,7 +543,9 @@ namespace client LogPrint(eLogInfo, "Clients: Set Max Conns To ", maxConns); serverTunnel->SetMaxConnsPerMinute(maxConns); - + if(!mapToLoopback) + LogPrint(eLogInfo, "Clients: disabling loopback address mapping"); + serverTunnel->SetMapToLoopback(mapToLoopback); if (accessList.length () > 0) { diff --git a/ClientContext.h b/ClientContext.h index db74a19e..e013671f 100644 --- a/ClientContext.h +++ b/ClientContext.h @@ -42,7 +42,8 @@ namespace client const char I2P_SERVER_TUNNEL_GZIP[] = "gzip"; const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword"; const char I2P_SERVER_TUNNEL_ADDRESS[] = "address"; - + const char I2P_SERVER_TUNNEL_MAPTOLOOPBACK[] = "maploopback"; + class ClientContext { public: diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 57006621..5a8d6160 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -58,25 +58,39 @@ namespace client StreamReceive (); Receive (); } - - void I2PTunnelConnection::Connect () + + static boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr) + { + boost::asio::ip::address_v4::bytes_type bytes; + const uint8_t * ident = addr; + bytes[0] = 127; + memcpy (bytes.data ()+1, ident, 3); + boost::asio::ip::address ourIP = boost::asio::ip::address_v4 (bytes); + return ourIP; + } + + static void MapToLoopback(const std::shared_ptr & sock, const i2p::data::IdentHash & addr) + { + + // bind to 127.x.x.x address + // where x.x.x are first three bytes from ident + auto ourIP = GetLoopbackAddressFor(addr); + sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0)); + + } + + void I2PTunnelConnection::Connect (bool mapToLoopback) { I2PTunnelSetSocketOptions(m_Socket); if (m_Socket) { -#ifdef __linux__ - // bind to 127.x.x.x address - // where x.x.x are first three bytes from ident +#ifdef __linux__ if (m_RemoteEndpoint.address ().is_v4 () && - m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127) + m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127 && mapToLoopback) { m_Socket->open (boost::asio::ip::tcp::v4 ()); - boost::asio::ip::address_v4::bytes_type bytes; - const uint8_t * ident = m_Stream->GetRemoteIdentity ()->GetIdentHash (); - bytes[0] = 127; - memcpy (bytes.data ()+1, ident, 3); - boost::asio::ip::address ourIP = boost::asio::ip::address_v4 (bytes); - m_Socket->bind (boost::asio::ip::tcp::endpoint (ourIP, 0)); + auto ident = m_Stream->GetRemoteIdentity()->GetIdentHash(); + MapToLoopback(m_Socket, ident); } #endif m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect, @@ -417,7 +431,7 @@ namespace client I2PServerTunnel::I2PServerTunnel (const std::string& name, const std::string& address, int port, std::shared_ptr localDestination, int inport, bool gzip): - I2PService (localDestination), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false) + I2PService (localDestination), m_MapToLoopback(true), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false) { m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port, gzip); } @@ -502,7 +516,7 @@ namespace client { auto conn = std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint ()); AddHandler (conn); - conn->Connect (); + conn->Connect (m_MapToLoopback); } I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& name, const std::string& address, @@ -581,8 +595,15 @@ namespace client return s; } } - /** create new udp session */ - boost::asio::ip::udp::endpoint ep(m_LocalAddress, 0); + boost::asio::ip::address addr; + /** create new udp session */ + if(m_LocalAddress.is_loopback() && m_MapToLoopback) { + auto ident = from.GetIdentHash(); + addr = GetLoopbackAddressFor(ident); + } else { + addr = m_LocalAddress; + } + boost::asio::ip::udp::endpoint ep(addr, 0); m_Sessions.push_back(std::make_shared(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort)); auto & back = m_Sessions.back(); return back; @@ -627,6 +648,7 @@ namespace client I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr localDestination, boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port) : + m_MapToLoopback(true), m_Name(name), m_LocalAddress(localAddress), m_RemoteEndpoint(forwardTo) @@ -768,8 +790,6 @@ namespace client // found convo if (len > 0) { LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32()); - uint8_t sendbuf[len]; - memcpy(sendbuf, buf, len); m_LocalSocket.send_to(boost::asio::buffer(buf, len), itr->second.first); // mark convo as active itr->second.second = i2p::util::GetMillisecondsSinceEpoch(); diff --git a/I2PTunnel.h b/I2PTunnel.h index bde3d820..2b895508 100644 --- a/I2PTunnel.h +++ b/I2PTunnel.h @@ -38,7 +38,7 @@ namespace client const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P ~I2PTunnelConnection (); void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0); - void Connect (); + void Connect (bool mapToLoopback = true); protected: @@ -201,12 +201,15 @@ namespace client std::vector > GetSessions(); std::shared_ptr GetLocalDestination () const { return m_LocalDest; } + void SetMapToLoopback(bool mapToLoopback = true) { m_MapToLoopback = mapToLoopback; } + private: void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); UDPSessionPtr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); private: + bool m_MapToLoopback; const std::string m_Name; boost::asio::ip::address m_LocalAddress; boost::asio::ip::udp::endpoint m_RemoteEndpoint; @@ -264,6 +267,8 @@ namespace client void SetAccessList (const std::set& accessList); + void SetMapToLoopback(bool mapToLoopback) { m_MapToLoopback = mapToLoopback; } + const std::string& GetAddress() const { return m_Address; } int GetPort () const { return m_Port; }; uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); }; @@ -283,7 +288,7 @@ namespace client virtual void CreateI2PConnection (std::shared_ptr stream); private: - + bool m_MapToLoopback; std::string m_Name, m_Address; int m_Port; boost::asio::ip::tcp::endpoint m_Endpoint; From c5d3c0c6f86fbd7c339168a3fe11d060506efec5 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sat, 7 Jan 2017 08:32:50 -0500 Subject: [PATCH 35/37] * add websocks * enable socks, websocks and httpproxy as client tunnels * remove old websocks config --- ClientContext.cpp | 46 ++- ClientContext.h | 5 +- Config.cpp | 7 - Daemon.cpp | 11 - I2PService.h | 5 +- WebSocks.cpp | 679 ++++++++++++++++++++++------------------- WebSocks.h | 31 +- android/jni/Android.mk | 3 +- 8 files changed, 427 insertions(+), 360 deletions(-) diff --git a/ClientContext.cpp b/ClientContext.cpp index 939a9e43..11ed7c98 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -8,6 +8,8 @@ #include "Identity.h" #include "util.h" #include "ClientContext.h" +#include "SOCKS.h" +#include "WebSocks.h" namespace i2p { @@ -424,7 +426,11 @@ namespace client try { std::string type = section.second.get (I2P_TUNNELS_SECTION_TYPE); - if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) + if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT + || type == I2P_TUNNELS_SECTION_TYPE_SOCKS + || type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS + || type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY + || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { // mandatory params std::string dest = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION); @@ -466,19 +472,45 @@ namespace client LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists"); } else { - // tcp client - auto clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort); - if (m_ClientTunnels.insert (std::make_pair (clientTunnel->GetAcceptor ().local_endpoint (), - std::unique_ptr(clientTunnel))).second) + boost::asio::ip::tcp::endpoint clientEndpoint; + I2PService * clientTunnel = nullptr; + if (type == I2P_TUNNELS_SECTION_TYPE_SOCKS) + { + // socks proxy + clientTunnel = new i2p::proxy::SOCKSProxy(address, port, "", destinationPort, localDestination); + clientEndpoint = ((i2p::proxy::SOCKSProxy*)clientTunnel)->GetAcceptor().local_endpoint(); + } + else if (type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY) + { + // http proxy + clientTunnel = new i2p::proxy::HTTPProxy(address, port, localDestination); + clientEndpoint = ((i2p::proxy::HTTPProxy*)clientTunnel)->GetAcceptor().local_endpoint(); + } + else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS) + { + // websocks proxy + clientTunnel = new WebSocks(address, port, localDestination);; + clientEndpoint = ((WebSocks*)clientTunnel)->GetLocalEndpoint(); + } + else + { + // tcp client + clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort); + clientEndpoint = ((I2PClientTunnel*)clientTunnel)->GetAcceptor().local_endpoint(); + } + if (m_ClientTunnels.insert (std::make_pair (clientEndpoint, std::unique_ptr(clientTunnel))).second) { clientTunnel->Start (); numClientTunnels++; } else - LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientTunnel->GetAcceptor ().local_endpoint (), " already exists"); + LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientEndpoint, "already exists"); } } - else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_IRC || type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) + else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER + || type == I2P_TUNNELS_SECTION_TYPE_HTTP + || type == I2P_TUNNELS_SECTION_TYPE_IRC + || type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) { // mandatory params std::string host = section.second.get (I2P_SERVER_TUNNEL_HOST); diff --git a/ClientContext.h b/ClientContext.h index 1961f411..fad042ed 100644 --- a/ClientContext.h +++ b/ClientContext.h @@ -26,6 +26,9 @@ namespace client const char I2P_TUNNELS_SECTION_TYPE_IRC[] = "irc"; const char I2P_TUNNELS_SECTION_TYPE_UDPCLIENT[] = "udpclient"; const char I2P_TUNNELS_SECTION_TYPE_UDPSERVER[] = "udpserver"; + const char I2P_TUNNELS_SECTION_TYPE_SOCKS[] = "socks"; + const char I2P_TUNNELS_SECTION_TYPE_WEBSOCKS[] = "websocks"; + const char I2P_TUNNELS_SECTION_TYPE_HTTPPROXY[] = "httpproxy"; const char I2P_CLIENT_TUNNEL_PORT[] = "port"; const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address"; const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination"; @@ -93,7 +96,7 @@ namespace client i2p::proxy::HTTPProxy * m_HttpProxy; i2p::proxy::SOCKSProxy * m_SocksProxy; - std::map > m_ClientTunnels; // local endpoint->tunnel + std::map > m_ClientTunnels; // local endpoint->tunnel std::map, std::unique_ptr > m_ServerTunnels; // ->tunnel std::mutex m_ForwardsMutex; diff --git a/Config.cpp b/Config.cpp index 728b7996..64333d74 100644 --- a/Config.cpp +++ b/Config.cpp @@ -203,12 +203,6 @@ namespace config { ("websockets.address", value()->default_value("127.0.0.1"), "address to bind websocket server on") ("websockets.port", value()->default_value(7666), "port to bind websocket server on"); - options_description websocks("WebSOCKS options"); - websocks.add_options() - ("websocks.enabled", value()->default_value(false), "enable WebSOCKS server") - ("websocks.address", value()->default_value("127.0.0.1"), "address to bind WebSOCKS server on") - ("websocks.port", value()->default_value(7666), "port to bind WebSOCKS server on"); - m_OptionsDesc .add(general) .add(limits) @@ -225,7 +219,6 @@ namespace config { .add(addressbook) .add(trust) .add(websocket) - .add(websocks) ; } diff --git a/Daemon.cpp b/Daemon.cpp index edd3dcf9..c7aaa279 100644 --- a/Daemon.cpp +++ b/Daemon.cpp @@ -27,7 +27,6 @@ #include "Event.h" #include "Websocket.h" -#include "WebSocks.h" namespace i2p { @@ -44,7 +43,6 @@ namespace i2p std::unique_ptr UPnP; #ifdef WITH_EVENTS std::unique_ptr m_WebsocketServer; - std::unique_ptr m_WebSocksServer; #endif }; @@ -309,15 +307,6 @@ namespace i2p d.m_WebsocketServer->Start(); i2p::event::core.SetListener(d.m_WebsocketServer->ToListener()); } - bool websocks; i2p::config::GetOption("websocks.enabled", websocks); - if (websocks) { - std::string websocksAddr; i2p::config::GetOption("websocks.address", websocksAddr); - uint16_t websocksPort; i2p::config::GetOption("websocks.port", websocksPort); - LogPrint(eLogInfo, "Daemon: starting up WebSOCKS server at ", websocksAddr, ":", websocksPort); - d.m_WebSocksServer = std::unique_ptr(new i2p::client::WebSocks(websocksAddr, websocksPort)); - d.m_WebSocksServer->Start(); - } - #endif return true; } diff --git a/I2PService.h b/I2PService.h index 795b075e..096443db 100644 --- a/I2PService.h +++ b/I2PService.h @@ -121,10 +121,11 @@ namespace client void Stop (); const boost::asio::ip::tcp::acceptor& GetAcceptor () const { return m_Acceptor; }; - + + virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; } + protected: virtual std::shared_ptr CreateHandler(std::shared_ptr socket) = 0; - virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; } private: void Accept(); void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); diff --git a/WebSocks.cpp b/WebSocks.cpp index a9a8751b..b6faa711 100644 --- a/WebSocks.cpp +++ b/WebSocks.cpp @@ -22,254 +22,273 @@ namespace i2p { namespace client { - typedef websocketpp::server WebSocksServerImpl; + typedef websocketpp::server WebSocksServerImpl; - typedef std::function)> StreamConnectFunc; + typedef std::function)> StreamConnectFunc; - struct IWebSocksConn - { - virtual void Close() = 0; - virtual void GotMessage(const websocketpp::connection_hdl & conn, WebSocksServerImpl::message_ptr msg) = 0; - }; + struct IWebSocksConn : public I2PServiceHandler + { + IWebSocksConn(I2PService * parent) : I2PServiceHandler(parent) {} + virtual void Close() = 0; + virtual void GotMessage(const websocketpp::connection_hdl & conn, WebSocksServerImpl::message_ptr msg) = 0; + }; - typedef std::shared_ptr WebSocksConn_ptr; + typedef std::shared_ptr WebSocksConn_ptr; - WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent); + WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent); class WebSocksImpl { - typedef std::mutex mutex_t; - typedef std::unique_lock lock_t; + typedef std::mutex mutex_t; + typedef std::unique_lock lock_t; - typedef std::shared_ptr Destination_t; - public: + typedef std::shared_ptr Destination_t; + public: - typedef WebSocksServerImpl ServerImpl; - typedef ServerImpl::message_ptr MessagePtr; + typedef WebSocksServerImpl ServerImpl; + typedef ServerImpl::message_ptr MessagePtr; WebSocksImpl(const std::string & addr, int port) : - m_Run(false), - m_Addr(addr), - m_Port(port), - m_Thread(nullptr) + Parent(nullptr), + m_Run(false), + m_Addr(addr), + m_Port(port), + m_Thread(nullptr) + { + m_Server.init_asio(); + m_Server.set_open_handler(std::bind(&WebSocksImpl::ConnOpened, this, std::placeholders::_1)); + } + + void InitializeDestination(WebSocks * parent) + { + Parent = parent; + m_Dest = Parent->GetLocalDestination(); + } + + ServerImpl::connection_ptr GetConn(const websocketpp::connection_hdl & conn) + { + return m_Server.get_con_from_hdl(conn); + } + + void CloseConn(const websocketpp::connection_hdl & conn) + { + auto c = GetConn(conn); + if(c) c->close(websocketpp::close::status::normal, "closed"); + } + + void CreateStreamTo(const std::string & addr, int port, StreamConnectFunc complete) + { + auto & addressbook = i2p::client::context.GetAddressBook(); + i2p::data::IdentHash ident; + if(addressbook.GetIdentHash(addr, ident)) { + // address found + m_Dest->CreateStream(complete, ident, port); + } else { + // not found + complete(nullptr); + } + } + + void ConnOpened(websocketpp::connection_hdl conn) + { + auto ptr = CreateWebSocksConn(conn, this); + Parent->AddHandler(ptr); + m_Conns.push_back(ptr); + } + + void Start() + { + if(m_Run) return; // already started + m_Server.listen(boost::asio::ip::address::from_string(m_Addr), m_Port); + m_Server.start_accept(); + m_Run = true; + m_Thread = new std::thread([&] (){ + while(m_Run) { + try { + m_Server.run(); + } catch( std::exception & ex) { + LogPrint(eLogError, "Websocks runtime exception: ", ex.what()); + } + } + }); + m_Dest->Start(); + } + + void Stop() { - m_Server.init_asio(); - m_Server.set_open_handler(std::bind(&WebSocksImpl::ConnOpened, this, std::placeholders::_1)); - i2p::data::PrivateKeys k = i2p::data::PrivateKeys::CreateRandomKeys(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); - m_Dest = std::make_shared(k, false); + for(const auto & conn : m_Conns) + conn->Close(); + + m_Dest->Stop(); + m_Run = false; + m_Server.stop(); + if(m_Thread) { + m_Thread->join(); + delete m_Thread; + } + m_Thread = nullptr; } - ServerImpl::connection_ptr GetConn(const websocketpp::connection_hdl & conn) - { - return m_Server.get_con_from_hdl(conn); - } - - void CloseConn(const websocketpp::connection_hdl & conn) - { - auto c = GetConn(conn); - if(c) c->close(websocketpp::close::status::normal, "closed"); - } - - void CreateStreamTo(const std::string & addr, int port, StreamConnectFunc complete) - { - auto & addressbook = i2p::client::context.GetAddressBook(); - i2p::data::IdentHash ident; - if(addressbook.GetIdentHash(addr, ident)) { - // address found - m_Dest->CreateStream(complete, ident, port); - } else { - // not found - complete(nullptr); - } - } - - void ConnOpened(websocketpp::connection_hdl conn) - { - m_Conns.push_back(CreateWebSocksConn(conn, this)); - } - - void Start() - { - if(m_Run) return; // already started - m_Server.listen(boost::asio::ip::address::from_string(m_Addr), m_Port); - m_Server.start_accept(); - m_Run = true; - m_Thread = new std::thread([&] (){ - while(m_Run) { - try { - m_Server.run(); - } catch( std::exception & ex) { - LogPrint(eLogError, "Websocks runtime exception: ", ex.what()); - } - } - }); - m_Dest->Start(); - } - - void Stop() - { - for(const auto & conn : m_Conns) - conn->Close(); - - m_Dest->Stop(); - m_Run = false; - m_Server.stop(); - if(m_Thread) { - m_Thread->join(); - delete m_Thread; - } - m_Thread = nullptr; - } - - private: - std::vector m_Conns; - bool m_Run; - ServerImpl m_Server; - std::string m_Addr; - int m_Port; - std::thread * m_Thread; - Destination_t m_Dest; + boost::asio::ip::tcp::endpoint GetLocalEndpoint() + { + return boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(m_Addr), m_Port); + } + + WebSocks * Parent; + + private: + std::vector m_Conns; + bool m_Run; + ServerImpl m_Server; + std::string m_Addr; + int m_Port; + std::thread * m_Thread; + Destination_t m_Dest; }; - struct WebSocksConn : public IWebSocksConn - { - enum ConnState - { - eWSCInitial, - eWSCTryConnect, - eWSCFailConnect, - eWSCOkayConnect, - eWSCClose, - eWSCEnd - }; - - typedef WebSocksServerImpl ServerImpl; - typedef ServerImpl::message_ptr Message_t; - typedef websocketpp::connection_hdl ServerConn; - typedef std::shared_ptr Destination_t; - typedef std::shared_ptr StreamDest_t; - typedef std::shared_ptr Stream_t; - - ServerConn m_Conn; - Stream_t m_Stream; - ConnState m_State; - WebSocksImpl * m_Parent; - std::string m_RemoteAddr; - int m_RemotePort; - uint8_t m_RecvBuf[2048]; - - WebSocksConn(const ServerConn & conn, WebSocksImpl * parent) : - m_Conn(conn), - m_Stream(nullptr), - m_State(eWSCInitial), - m_Parent(parent) - { - - } - - ~WebSocksConn() - { - Close(); - } - - void EnterState(ConnState state) - { - LogPrint(eLogDebug, "websocks: state ", m_State, " -> ", state); - switch(m_State) - { - case eWSCInitial: - if (state == eWSCClose) { - m_State = eWSCClose; - // connection was opened but never used - LogPrint(eLogInfo, "websocks: connection closed but never used"); - Close(); - return; - } else if (state == eWSCTryConnect) { - // we will try to connect - m_State = eWSCTryConnect; - m_Parent->CreateStreamTo(m_RemoteAddr, m_RemotePort, std::bind(&WebSocksConn::ConnectResult, this, std::placeholders::_1)); - } else { - LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); - } - return; - case eWSCTryConnect: - if(state == eWSCOkayConnect) { - // we connected okay - LogPrint(eLogDebug, "websocks: connected to ", m_RemoteAddr, ":", m_RemotePort); - SendResponse(""); - m_State = eWSCOkayConnect; - } else if(state == eWSCFailConnect) { - // we did not connect okay - LogPrint(eLogDebug, "websocks: failed to connect to ", m_RemoteAddr, ":", m_RemotePort); - SendResponse("failed to connect"); - m_State = eWSCFailConnect; - EnterState(eWSCInitial); - } else if(state == eWSCClose) { - // premature close - LogPrint(eLogWarning, "websocks: websocket connection closed prematurely"); - m_State = eWSCClose; - } else { - LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); - } - return; - case eWSCFailConnect: - if (state == eWSCInitial) { - // reset to initial state so we can try connecting again - m_RemoteAddr = ""; - m_RemotePort = 0; - LogPrint(eLogDebug, "websocks: reset websocket conn to initial state"); - m_State = eWSCInitial; - } else if (state == eWSCClose) { - // we are going to close the connection - m_State = eWSCClose; - Close(); - } else { - LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); - } - return; - case eWSCOkayConnect: - if(state == eWSCClose) { - // graceful close - m_State = eWSCClose; - Close(); - } else { - LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); - } - case eWSCClose: - if(state == eWSCEnd) { - LogPrint(eLogDebug, "websocks: socket ended"); - } else { - LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); - } - return; - default: - LogPrint(eLogError, "websocks: bad state ", m_State); - } - } - - void StartForwarding() - { - LogPrint(eLogDebug, "websocks: begin forwarding data"); + struct WebSocksConn : public IWebSocksConn , public std::enable_shared_from_this + { + enum ConnState + { + eWSCInitial, + eWSCTryConnect, + eWSCFailConnect, + eWSCOkayConnect, + eWSCClose, + eWSCEnd + }; + + typedef WebSocksServerImpl ServerImpl; + typedef ServerImpl::message_ptr Message_t; + typedef websocketpp::connection_hdl ServerConn; + typedef std::shared_ptr Destination_t; + typedef std::shared_ptr StreamDest_t; + typedef std::shared_ptr Stream_t; + + ServerConn m_Conn; + Stream_t m_Stream; + ConnState m_State; + WebSocksImpl * m_Parent; + std::string m_RemoteAddr; + int m_RemotePort; + uint8_t m_RecvBuf[2048]; + + WebSocksConn(const ServerConn & conn, WebSocksImpl * parent) : + IWebSocksConn(parent->Parent), + m_Conn(conn), + m_Stream(nullptr), + m_State(eWSCInitial), + m_Parent(parent) + { + + } + + ~WebSocksConn() + { + Close(); + } + + void EnterState(ConnState state) + { + LogPrint(eLogDebug, "websocks: state ", m_State, " -> ", state); + switch(m_State) + { + case eWSCInitial: + if (state == eWSCClose) { + m_State = eWSCClose; + // connection was opened but never used + LogPrint(eLogInfo, "websocks: connection closed but never used"); + Close(); + return; + } else if (state == eWSCTryConnect) { + // we will try to connect + m_State = eWSCTryConnect; + m_Parent->CreateStreamTo(m_RemoteAddr, m_RemotePort, std::bind(&WebSocksConn::ConnectResult, this, std::placeholders::_1)); + } else { + LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); + } + return; + case eWSCTryConnect: + if(state == eWSCOkayConnect) { + // we connected okay + LogPrint(eLogDebug, "websocks: connected to ", m_RemoteAddr, ":", m_RemotePort); + SendResponse(""); + m_State = eWSCOkayConnect; + } else if(state == eWSCFailConnect) { + // we did not connect okay + LogPrint(eLogDebug, "websocks: failed to connect to ", m_RemoteAddr, ":", m_RemotePort); + SendResponse("failed to connect"); + m_State = eWSCFailConnect; + EnterState(eWSCInitial); + } else if(state == eWSCClose) { + // premature close + LogPrint(eLogWarning, "websocks: websocket connection closed prematurely"); + m_State = eWSCClose; + } else { + LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); + } + return; + case eWSCFailConnect: + if (state == eWSCInitial) { + // reset to initial state so we can try connecting again + m_RemoteAddr = ""; + m_RemotePort = 0; + LogPrint(eLogDebug, "websocks: reset websocket conn to initial state"); + m_State = eWSCInitial; + } else if (state == eWSCClose) { + // we are going to close the connection + m_State = eWSCClose; + Close(); + } else { + LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); + } + return; + case eWSCOkayConnect: + if(state == eWSCClose) { + // graceful close + m_State = eWSCClose; + Close(); + } else { + LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); + } + case eWSCClose: + if(state == eWSCEnd) { + LogPrint(eLogDebug, "websocks: socket ended"); + Kill(); + auto me = shared_from_this(); + Done(me); + } else { + LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); + } + return; + default: + LogPrint(eLogError, "websocks: bad state ", m_State); + } + } + + void StartForwarding() + { + LogPrint(eLogDebug, "websocks: begin forwarding data"); uint8_t b[1]; m_Stream->Send(b, 0); - AsyncRecv(); - } - - void HandleAsyncRecv(const boost::system::error_code &ec, std::size_t n) - { - if(ec) { - // error - LogPrint(eLogWarning, "websocks: connection error ", ec.message()); - EnterState(eWSCClose); - } else { - // forward data - LogPrint(eLogDebug, "websocks recv ", n); - + AsyncRecv(); + } + + void HandleAsyncRecv(const boost::system::error_code &ec, std::size_t n) + { + if(ec) { + // error + LogPrint(eLogWarning, "websocks: connection error ", ec.message()); + EnterState(eWSCClose); + } else { + // forward data + LogPrint(eLogDebug, "websocks recv ", n); + std::string str((char*)m_RecvBuf, n); auto conn = m_Parent->GetConn(m_Conn); - if(!conn) { + if(!conn) { LogPrint(eLogWarning, "websocks: connection is gone"); EnterState(eWSCClose); return; @@ -277,98 +296,98 @@ namespace client conn->send(str); AsyncRecv(); - } - } - - void AsyncRecv() - { - m_Stream->AsyncReceive( - boost::asio::buffer(m_RecvBuf, sizeof(m_RecvBuf)), - std::bind(&WebSocksConn::HandleAsyncRecv, this, std::placeholders::_1, std::placeholders::_2), 60); - } - - /** @brief send error message or empty string for success */ - void SendResponse(const std::string & errormsg) - { - boost::property_tree::ptree resp; - if(errormsg.size()) { - resp.put("error", errormsg); - resp.put("success", 0); - } else { - resp.put("success", 1); - } - std::ostringstream ss; - write_json(ss, resp); - auto conn = m_Parent->GetConn(m_Conn); - if(conn) conn->send(ss.str()); - } - - void ConnectResult(Stream_t stream) - { - m_Stream = stream; - if(m_State == eWSCClose) { - // premature close of websocket - Close(); - return; - } - if(m_Stream) { - // connect good - EnterState(eWSCOkayConnect); + } + } + + void AsyncRecv() + { + m_Stream->AsyncReceive( + boost::asio::buffer(m_RecvBuf, sizeof(m_RecvBuf)), + std::bind(&WebSocksConn::HandleAsyncRecv, this, std::placeholders::_1, std::placeholders::_2), 60); + } + + /** @brief send error message or empty string for success */ + void SendResponse(const std::string & errormsg) + { + boost::property_tree::ptree resp; + if(errormsg.size()) { + resp.put("error", errormsg); + resp.put("success", 0); + } else { + resp.put("success", 1); + } + std::ostringstream ss; + write_json(ss, resp); + auto conn = m_Parent->GetConn(m_Conn); + if(conn) conn->send(ss.str()); + } + + void ConnectResult(Stream_t stream) + { + m_Stream = stream; + if(m_State == eWSCClose) { + // premature close of websocket + Close(); + return; + } + if(m_Stream) { + // connect good + EnterState(eWSCOkayConnect); StartForwarding(); - } else { - // connect failed - EnterState(eWSCFailConnect); - } - } - - virtual void GotMessage(const websocketpp::connection_hdl & conn, WebSocksServerImpl::message_ptr msg) - { - (void) conn; - std::string payload = msg->get_payload(); - if(m_State == eWSCOkayConnect) - { - // forward to server - LogPrint(eLogDebug, "websocks: forward ", payload.size()); - m_Stream->Send((uint8_t*)payload.c_str(), payload.size()); - } else if (m_State == eWSCInitial) { - // recv connect request - auto itr = payload.find(":"); - if(itr == std::string::npos) { - // no port - m_RemotePort = 0; - m_RemoteAddr = payload; - } else { - // includes port - m_RemotePort = std::stoi(payload.substr(itr+1)); - m_RemoteAddr = payload.substr(0, itr); - } - EnterState(eWSCTryConnect); - } else { - // wtf? - LogPrint(eLogWarning, "websocks: got message in invalid state ", m_State); - } - } - - virtual void Close() - { - if(m_State == eWSCClose) { - LogPrint(eLogDebug, "websocks: closing connection"); - if(m_Stream) m_Stream->Close(); - m_Parent->CloseConn(m_Conn); - EnterState(eWSCEnd); - } else { - EnterState(eWSCClose); - } - } - }; - - WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent) - { - auto ptr = std::make_shared(conn, parent); - auto c = parent->GetConn(conn); - c->set_message_handler(std::bind(&WebSocksConn::GotMessage, ptr.get(), std::placeholders::_1, std::placeholders::_2)); - return ptr; - } + } else { + // connect failed + EnterState(eWSCFailConnect); + } + } + + virtual void GotMessage(const websocketpp::connection_hdl & conn, WebSocksServerImpl::message_ptr msg) + { + (void) conn; + std::string payload = msg->get_payload(); + if(m_State == eWSCOkayConnect) + { + // forward to server + LogPrint(eLogDebug, "websocks: forward ", payload.size()); + m_Stream->Send((uint8_t*)payload.c_str(), payload.size()); + } else if (m_State == eWSCInitial) { + // recv connect request + auto itr = payload.find(":"); + if(itr == std::string::npos) { + // no port + m_RemotePort = 0; + m_RemoteAddr = payload; + } else { + // includes port + m_RemotePort = std::stoi(payload.substr(itr+1)); + m_RemoteAddr = payload.substr(0, itr); + } + EnterState(eWSCTryConnect); + } else { + // wtf? + LogPrint(eLogWarning, "websocks: got message in invalid state ", m_State); + } + } + + virtual void Close() + { + if(m_State == eWSCClose) { + LogPrint(eLogDebug, "websocks: closing connection"); + if(m_Stream) m_Stream->Close(); + m_Parent->CloseConn(m_Conn); + EnterState(eWSCEnd); + } else { + EnterState(eWSCClose); + } + } + }; + + WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent) + { + auto ptr = std::make_shared(conn, parent); + auto c = parent->GetConn(conn); + c->set_message_handler(std::bind(&WebSocksConn::GotMessage, ptr.get(), std::placeholders::_1, std::placeholders::_2)); + return ptr; + } } } @@ -383,7 +402,7 @@ namespace client class WebSocksImpl { public: - WebSocksImpl(const std::string & addr, int port) + WebSocksImpl(const std::string & addr, int port) : m_Addr(addr), m_Port(port) { } @@ -398,9 +417,20 @@ namespace client void Stop() { + } + void InitializeDestination(WebSocks * parent) + { } + boost::asio::ip::tcp::endpoint GetLocalEndpoint() + { + return boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(m_Addr), m_Port); + } + + std::string m_Addr; + int m_Port; + }; } } @@ -410,17 +440,28 @@ namespace i2p { namespace client { - WebSocks::WebSocks(const std::string & addr, int port) : m_Impl(new WebSocksImpl(addr, port)) {} + WebSocks::WebSocks(const std::string & addr, int port, std::shared_ptr localDestination) : m_Impl(new WebSocksImpl(addr, port)) + { + m_Impl->InitializeDestination(this); + } WebSocks::~WebSocks() { delete m_Impl; } void WebSocks::Start() { m_Impl->Start(); + GetLocalDestination()->Start(); + } + + boost::asio::ip::tcp::endpoint WebSocks::GetLocalEndpoint() const + { + return m_Impl->GetLocalEndpoint(); } void WebSocks::Stop() { m_Impl->Stop(); + GetLocalDestination()->Stop(); } } } + diff --git a/WebSocks.h b/WebSocks.h index 93943abd..2314659f 100644 --- a/WebSocks.h +++ b/WebSocks.h @@ -1,27 +1,34 @@ #ifndef WEBSOCKS_H_ #define WEBSOCKS_H_ #include +#include +#include "I2PService.h" +#include "Destination.h" namespace i2p { namespace client { - class WebSocksImpl; + class WebSocksImpl; - /** @brief websocket socks proxy server */ - class WebSocks - { - public: - WebSocks(const std::string & addr, int port); - ~WebSocks(); + /** @brief websocket socks proxy server */ + class WebSocks : public i2p::client::I2PService + { + public: + WebSocks(const std::string & addr, int port, std::shared_ptr localDestination); + ~WebSocks(); - void Start(); - void Stop(); + void Start(); + void Stop(); - private: - WebSocksImpl * m_Impl; - }; + boost::asio::ip::tcp::endpoint GetLocalEndpoint() const; + + const char * GetName() { return "WebSOCKS Proxy"; } + + private: + WebSocksImpl * m_Impl; + }; } } #endif diff --git a/android/jni/Android.mk b/android/jni/Android.mk index c44594f0..5eaa983e 100755 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -59,7 +59,8 @@ LOCAL_SRC_FILES := DaemonAndroid.cpp i2pd_android.cpp \ ../../TunnelPool.cpp \ ../../Timestamp.cpp \ ../../Event.cpp \ - ../../BloomFilter.cpp \ + ../../WebSocks.cpp \ +../../BloomFilter.cpp \ ../../util.cpp \ ../../i2pd.cpp ../../UPnP.cpp From 7b16aa60501398a4b038a73a1156028d84d707b0 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sat, 7 Jan 2017 08:40:02 -0500 Subject: [PATCH 36/37] revert --- Identity.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Identity.h b/Identity.h index 7a9d049a..49dada48 100644 --- a/Identity.h +++ b/Identity.h @@ -133,7 +133,7 @@ namespace data size_t FromBase64(const std::string& s); std::string ToBase64 () const; - static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); + static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1); private: From a5d6820453e190c9e289d0c77ebb00353c2cce64 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sat, 7 Jan 2017 13:55:17 -0500 Subject: [PATCH 37/37] fix --- ClientContext.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ClientContext.cpp b/ClientContext.cpp index 11ed7c98..af108dd7 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -433,7 +433,9 @@ namespace client || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { // mandatory params - std::string dest = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION); + std::string dest; + if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) + dest = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION); int port = section.second.get (I2P_CLIENT_TUNNEL_PORT); // optional params std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "");