From e384ec32b83f9ef4ac1c1947840259e810005f76 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 15 Nov 2016 15:40:09 -0500 Subject: [PATCH 01/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] try fixing datagram --- AddressBook.cpp | 2 +- Datagram.cpp | 373 +++++++++++++++++------------------------------- Datagram.h | 72 ++++------ 3 files changed, 162 insertions(+), 285 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); + return DatagramSession::Info(nullptr, nullptr, m_LastUse); } + + void DatagramSession::Ack() + { + m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); + auto path = GetSharedRoutingPath(); + if(path) + path->updateTime = i2p::util::GetSecondsSinceEpoch (); + } + + std::shared_ptr DatagramSession::GetSharedRoutingPath () + { + if(!m_RoutingSession) { + if(!m_RemoteLeaseSet) { + m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); + } + if(!m_RemoteLeaseSet) { + // no remote lease set + return nullptr; + } + m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); + } + 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; + } + 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 { + // 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; + } + } + path->outboundTunnel = m_CurrentOutboundTunnel; + path->remoteLease = m_CurrentRemoteLease; + m_RoutingSession->SetSharedRoutingPath(path); + } + return path; + } + + void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr ls) + { + // 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; + } + 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); - + m_SendQueue.push_back(msg); + // flush queue right away if full + if(m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) FlushSendQueue(); } - void DatagramSession::UpdateRoutingPath(const std::shared_ptr & path) + void DatagramSession::FlushSendQueue () { - 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 (); - } - bool DatagramSession::ShouldUpdateRoutingPath() const - { - 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; - } - - - bool DatagramSession::ShouldSwitchLease() const - { - std::shared_ptr routingPath = nullptr; - std::shared_ptr currentLease = nullptr; - if(m_RoutingSession) - routingPath = m_RoutingSession->GetSharedRoutingPath (); + std::vector send; + auto routingPath = GetSharedRoutingPath(); + // if we don't have a routing path we will drop all queued messages 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 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()) + for (const auto & msg : m_SendQueue) { - // ya so let's stick with it - outboundTunnel = routingPath->outboundTunnel; + auto m = m_RoutingSession->WrapSingleMessage(msg); + send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, m}); } - else - outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(routingPath->outboundTunnel); // no so we'll switch outbound tunnels + routingPath->outboundTunnel->SendTunnelDataMsg(send); } - // 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(); - } - 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; - } - } - 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 // we don't have a new routing path to give - routingPath = nullptr; - } - return routingPath; + m_SendQueue.clear(); + ScheduleFlushSendQueue(); } - void DatagramSession::ResetRoutingPath() + void DatagramSession::ScheduleFlushSendQueue() { - 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); - } - } - - std::shared_ptr DatagramSession::GetNextLease() - { - auto now = i2p::util::GetMillisecondsSinceEpoch (); - std::shared_ptr next = nullptr; - if(m_RemoteLeaseSet) - { - 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()) - { - // pick random valid next lease - uint32_t idx = rand() % leases.size(); - next = leases[idx]; - } - else - LogPrint(eLogWarning, "DatagramDestination: no leases to use"); - } - 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)); - } - - void DatagramSession::HandleGotLeaseSet(std::shared_ptr remoteIdent, std::shared_ptr msg) - { - 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); - /** 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(); + std::shared_ptr GetSharedRoutingPath(); + + void HandleLeaseSetUpdated(std::shared_ptr ls); - /** 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] * 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 5ad25376bb07cc54a672340707e8d53e6a135a6f Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 5 Jan 2017 16:03:53 -0500 Subject: [PATCH 35/60] send all outgoing messages in one buffer --- NTCPSession.cpp | 36 +++++++++++++++++++----------------- NTCPSession.h | 5 ++--- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/NTCPSession.cpp b/NTCPSession.cpp index 65da5def..88c926fd 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -635,14 +635,7 @@ namespace transport return true; } - void NTCPSession::Send (std::shared_ptr msg) - { - m_IsSending = true; - boost::asio::async_write (m_Socket, CreateMsgBuffer (msg), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector >{ msg })); - } - - boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (std::shared_ptr msg) + size_t NTCPSession::CreateMsgBuffer (std::shared_ptr msg, uint8_t * buf) { uint8_t * sendBuffer; int len; @@ -674,24 +667,29 @@ namespace transport htobe32buf (sendBuffer + len + 2 + padding, adler32 (adler32 (0, Z_NULL, 0), sendBuffer, len + 2+ padding)); int l = len + padding + 6; - m_Encryption.Encrypt(sendBuffer, l, sendBuffer); - return boost::asio::buffer ((const uint8_t *)sendBuffer, l); + m_Encryption.Encrypt(sendBuffer, l, buf); + return l; } void NTCPSession::Send (const std::vector >& msgs) { + if (!msgs.size ()) return; m_IsSending = true; - std::vector bufs; + size_t len = 0; for (const auto& it: msgs) - bufs.push_back (CreateMsgBuffer (it)); - boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (), - std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs)); + len += it->GetLength () + 22; // 6 + 16 + uint8_t * buf = new uint8_t[len]; + len = 0; + for (const auto& it: msgs) + len += CreateMsgBuffer (it, buf + len); + boost::asio::async_write (m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, buf)); } - void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs) + void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint8_t * buf) { - (void) msgs; + delete[] buf; m_IsSending = false; if (ecode) { @@ -716,7 +714,11 @@ namespace transport void NTCPSession::SendTimeSyncMessage () { - Send (nullptr); + uint8_t * buf = new uint8_t[16]; + m_IsSending = true; + auto len = CreateMsgBuffer (nullptr, buf); // nullptr means timestamp + boost::asio::async_write (m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, buf)); } diff --git a/NTCPSession.h b/NTCPSession.h index a5d6f99f..bd3f3369 100644 --- a/NTCPSession.h +++ b/NTCPSession.h @@ -91,10 +91,9 @@ namespace transport void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); bool DecryptNextBlock (const uint8_t * encrypted); - void Send (std::shared_ptr msg); - boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr msg); + size_t CreateMsgBuffer (std::shared_ptr msg, uint8_t * buf); void Send (const std::vector >& msgs); - void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs); + void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint8_t * buf); private: From 0b28812f7ed357f64a436ac41c5bc49da73887d8 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 5 Jan 2017 17:37:39 -0500 Subject: [PATCH 36/60] rollback --- NTCPSession.cpp | 36 +++++++++++++++++------------------- NTCPSession.h | 5 +++-- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/NTCPSession.cpp b/NTCPSession.cpp index 88c926fd..65da5def 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -635,7 +635,14 @@ namespace transport return true; } - size_t NTCPSession::CreateMsgBuffer (std::shared_ptr msg, uint8_t * buf) + void NTCPSession::Send (std::shared_ptr msg) + { + m_IsSending = true; + boost::asio::async_write (m_Socket, CreateMsgBuffer (msg), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector >{ msg })); + } + + boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (std::shared_ptr msg) { uint8_t * sendBuffer; int len; @@ -667,29 +674,24 @@ namespace transport htobe32buf (sendBuffer + len + 2 + padding, adler32 (adler32 (0, Z_NULL, 0), sendBuffer, len + 2+ padding)); int l = len + padding + 6; - m_Encryption.Encrypt(sendBuffer, l, buf); - return l; + m_Encryption.Encrypt(sendBuffer, l, sendBuffer); + return boost::asio::buffer ((const uint8_t *)sendBuffer, l); } void NTCPSession::Send (const std::vector >& msgs) { - if (!msgs.size ()) return; m_IsSending = true; - size_t len = 0; + std::vector bufs; for (const auto& it: msgs) - len += it->GetLength () + 22; // 6 + 16 - uint8_t * buf = new uint8_t[len]; - len = 0; - for (const auto& it: msgs) - len += CreateMsgBuffer (it, buf + len); - boost::asio::async_write (m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, buf)); + bufs.push_back (CreateMsgBuffer (it)); + boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (), + std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs)); } - void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint8_t * buf) + void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs) { - delete[] buf; + (void) msgs; m_IsSending = false; if (ecode) { @@ -714,11 +716,7 @@ namespace transport void NTCPSession::SendTimeSyncMessage () { - uint8_t * buf = new uint8_t[16]; - m_IsSending = true; - auto len = CreateMsgBuffer (nullptr, buf); // nullptr means timestamp - boost::asio::async_write (m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, buf)); + Send (nullptr); } diff --git a/NTCPSession.h b/NTCPSession.h index bd3f3369..a5d6f99f 100644 --- a/NTCPSession.h +++ b/NTCPSession.h @@ -91,9 +91,10 @@ namespace transport void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); bool DecryptNextBlock (const uint8_t * encrypted); - size_t CreateMsgBuffer (std::shared_ptr msg, uint8_t * buf); + void Send (std::shared_ptr msg); + boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr msg); void Send (const std::vector >& msgs); - void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint8_t * buf); + void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs); private: From 417eb56a9be3b47d44a3c15ec14a6abbc0f2baef Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 6 Jan 2017 09:59:22 -0500 Subject: [PATCH 37/60] rollback to 2.6.0 --- I2PControl.cpp | 197 +++++++++++++++++++++++-------------------------- I2PControl.h | 4 +- 2 files changed, 95 insertions(+), 106 deletions(-) diff --git a/I2PControl.cpp b/I2PControl.cpp index 1e8546ac..6d6a5ee3 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include @@ -14,7 +16,6 @@ #include "Crypto.h" #include "FS.h" #include "Log.h" -#include "HTTP.h" #include "Config.h" #include "NetDb.h" #include "RouterContext.h" @@ -189,66 +190,71 @@ namespace client if (ecode) { LogPrint (eLogError, "I2PControl: read error: ", ecode.message ()); return; - } - /* try to parse received data */ - std::stringstream json; - std::string response; - bool isHTTP = false; - if (memcmp (buf->data (), "POST", 4) == 0) { - long int remains = 0; - isHTTP = true; - i2p::http::HTTPReq req; - std::size_t len = req.parse(buf->data(), bytes_transferred); - if (len <= 0) { - LogPrint(eLogError, "I2PControl: incomplete/malformed POST request"); - return; - } - /* append to json chunk of data from 1st request */ - json.write(buf->data() + len, bytes_transferred - len); - remains = req.content_length(); - /* if request has Content-Length header, fetch rest of data and store to json buffer */ - while (remains > 0) { - len = ((long int) buf->size() < remains) ? buf->size() : remains; - bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), len)); - json.write(buf->data(), bytes_transferred); - remains -= bytes_transferred; - } } else { - json.write(buf->data(), bytes_transferred); - } - //LogPrint(eLogDebug, "I2PControl: json from request: ", json.str()); + try + { + bool isHtml = !memcmp (buf->data (), "POST", 4); + std::stringstream ss; + ss.write (buf->data (), bytes_transferred); + if (isHtml) + { + std::string header; + size_t contentLength = 0; + while (!ss.eof () && header != "\r") + { + std::getline(ss, header); + auto colon = header.find (':'); + if (colon != std::string::npos && header.substr (0, colon) == "Content-Length") + contentLength = std::stoi (header.substr (colon + 1)); + } + if (ss.eof ()) + { + LogPrint (eLogError, "I2PControl: malformed request, HTTP header expected"); + return; // TODO: + } + std::streamoff rem = contentLength + ss.tellg () - bytes_transferred; // more bytes to read + if (rem > 0) + { + bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), rem)); + ss.write (buf->data (), bytes_transferred); + } + } + std::ostringstream response; #if GCC47_BOOST149 - LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7"); - BuildErrorResponse(response, 32603, "JSON requests is not supported with this version of boost"); + LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7"); + response << "{\"id\":null,\"error\":"; + response << "{\"code\":-32603,\"message\":\"JSON requests is not supported with this version of boost\"},"; + response << "\"jsonrpc\":\"2.0\"}"; #else - /* now try to parse json itself */ - std::string j_str = json.str(); - std::stringstream _json(j_str); - try { - boost::property_tree::ptree pt; - boost::property_tree::read_json (_json, pt); + boost::property_tree::ptree pt; + boost::property_tree::read_json (ss, pt); - std::string id = pt.get("id"); - std::string method = pt.get("method"); - auto it = m_MethodHandlers.find (method); - if (it != m_MethodHandlers.end ()) { - std::ostringstream ss; - ss << "{\"id\":" << id << ",\"result\":{"; - (this->*(it->second))(pt.get_child ("params"), ss); - ss << "},\"jsonrpc\":\"2.0\"}"; - response = ss.str(); - } else { - LogPrint (eLogWarning, "I2PControl: unknown method ", method); - BuildErrorResponse(response, 32601, "Method not found"); - } - } catch (std::exception& ex) { - LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ()); - BuildErrorResponse(response, 32603, ex.what()); - } catch (...) { - LogPrint (eLogError, "I2PControl: handle request unknown exception"); - } + std::string id = pt.get("id"); + std::string method = pt.get("method"); + auto it = m_MethodHandlers.find (method); + if (it != m_MethodHandlers.end ()) + { + response << "{\"id\":" << id << ",\"result\":{"; + (this->*(it->second))(pt.get_child ("params"), response); + response << "},\"jsonrpc\":\"2.0\"}"; + } else { + LogPrint (eLogWarning, "I2PControl: unknown method ", method); + response << "{\"id\":null,\"error\":"; + response << "{\"code\":-32601,\"message\":\"Method not found\"},"; + response << "\"jsonrpc\":\"2.0\"}"; + } #endif - SendResponse (socket, buf, response, isHTTP); + SendResponse (socket, buf, response, isHtml); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ()); + } + catch (...) + { + LogPrint (eLogError, "I2PControl: handle request unknown exception"); + } + } } void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const @@ -270,28 +276,27 @@ namespace client ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value; } - void I2PControlService::BuildErrorResponse (std::string & content, int code, const char *message) { - std::stringstream ss; - ss << "{\"id\":null,\"error\":"; - ss << "{\"code\":" << -code << ",\"message\":\"" << message << "\"},"; - ss << "\"jsonrpc\":\"2.0\"}"; - content = ss.str(); - } - void I2PControlService::SendResponse (std::shared_ptr socket, - std::shared_ptr buf, std::string& content, bool isHTTP) + std::shared_ptr buf, std::ostringstream& response, bool isHtml) { - if (isHTTP) { - i2p::http::HTTPRes res; - res.code = 200; - res.add_header("Content-Type", "application/json"); - res.add_header("Connection", "close"); - res.body = content; - std::string tmp = res.to_string(); - content = tmp; + size_t len = response.str ().length (), offset = 0; + if (isHtml) + { + std::ostringstream header; + header << "HTTP/1.1 200 OK\r\n"; + header << "Connection: close\r\n"; + header << "Content-Length: " << boost::lexical_cast(len) << "\r\n"; + header << "Content-Type: application/json\r\n"; + header << "Date: "; + auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT"); + header.imbue(std::locale (header.getloc(), facet)); + header << boost::posix_time::second_clock::local_time() << "\r\n"; + header << "\r\n"; + offset = header.str ().size (); + memcpy (buf->data (), header.str ().c_str (), offset); } - std::copy(content.begin(), content.end(), buf->begin()); - boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), content.length()), + memcpy (buf->data () + offset, response.str ().c_str (), len); + boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len), boost::asio::transfer_all (), std::bind(&I2PControlService::HandleResponseSent, this, std::placeholders::_1, std::placeholders::_2, socket, buf)); @@ -318,7 +323,7 @@ namespace client } InsertParam (results, "API", api); results << ","; - std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ()); + std::string token = boost::lexical_cast(i2p::util::GetSecondsSinceEpoch ()); m_Tokens.insert (token); InsertParam (results, "Token", token); } @@ -344,10 +349,9 @@ namespace client (this->*(it1->second))(it.second.data ()); InsertParam (results, it.first, ""); } - else { + else LogPrint (eLogError, "I2PControl: I2PControl unknown request: ", it.first); - } - } + } } void I2PControlService::PasswordHandler (const std::string& value) @@ -361,20 +365,17 @@ namespace client void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { - for (auto it = params.begin (); it != params.end (); ++it) + for (auto it = params.begin (); it != params.end (); it++) { LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first); - if (it != params.begin ()) results << ","; auto it1 = m_RouterInfoHandlers.find (it->first); if (it1 != m_RouterInfoHandlers.end ()) { + if (it != params.begin ()) results << ","; (this->*(it1->second))(results); } else - { - InsertParam(results, it->first, ""); LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first); - } } } @@ -440,20 +441,15 @@ namespace client void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { - for (auto it = params.begin (); it != params.end (); ++it) + for (auto it = params.begin (); it != params.end (); it++) { - if (it != params.begin ()) results << ","; + if (it != params.begin ()) results << ","; LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first); auto it1 = m_RouterManagerHandlers.find (it->first); - if (it1 != m_RouterManagerHandlers.end ()) - { - (this->*(it1->second))(results); - } - else - { - InsertParam(results, it->first, ""); + if (it1 != m_RouterManagerHandlers.end ()) { + (this->*(it1->second))(results); + } else LogPrint (eLogError, "I2PControl: RouterManager unknown request: ", it->first); - } } } @@ -494,20 +490,15 @@ namespace client // network setting void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { - for (auto it = params.begin (); it != params.end (); ++it) + for (auto it = params.begin (); it != params.end (); it++) { if (it != params.begin ()) results << ","; LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first); auto it1 = m_NetworkSettingHandlers.find (it->first); - if (it1 != m_NetworkSettingHandlers.end ()) - { - (this->*(it1->second))(it->second.data (), results); - } - else - { - InsertParam(results, it->first, ""); + if (it1 != m_NetworkSettingHandlers.end ()) { + (this->*(it1->second))(it->second.data (), results); + } else LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first); - } } } diff --git a/I2PControl.h b/I2PControl.h index bd5b9bad..047c2fe2 100644 --- a/I2PControl.h +++ b/I2PControl.h @@ -45,9 +45,8 @@ namespace client void ReadRequest (std::shared_ptr socket); void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred, std::shared_ptr socket, std::shared_ptr buf); - void BuildErrorResponse (std::string & content, int code, const char *message); void SendResponse (std::shared_ptr socket, - std::shared_ptr buf, std::string& response, bool isHtml); + std::shared_ptr buf, std::ostringstream& response, bool isHtml); void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::shared_ptr socket, std::shared_ptr buf); @@ -120,4 +119,3 @@ namespace client } #endif - From fde1c08945a173518a9274005a3a3e4edb33cdb1 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 6 Jan 2017 14:02:54 -0500 Subject: [PATCH 38/60] change country code to A1 --- I2PControl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/I2PControl.cpp b/I2PControl.cpp index 6d6a5ee3..523f10e1 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -536,7 +536,7 @@ namespace client X509_gmtime_adj (X509_get_notAfter (x509), I2P_CONTROL_CERTIFICATE_VALIDITY*24*60*60); // expiration X509_set_pubkey (x509, pkey); // public key X509_NAME * name = X509_get_subject_name (x509); - X509_NAME_add_entry_by_txt (name, "C", MBSTRING_ASC, (unsigned char *)"RU", -1, -1, 0); // country (Russia by default) + X509_NAME_add_entry_by_txt (name, "C", MBSTRING_ASC, (unsigned char *)"A1", -1, -1, 0); // country (Anonymous proxy) X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name X509_set_issuer_name (x509, name); // set issuer to ourselves From b6097160f1a6f4a3120386d5a55f081fb0277bb9 Mon Sep 17 00:00:00 2001 From: Mikal Villa Date: Fri, 6 Jan 2017 21:47:55 +0100 Subject: [PATCH 39/60] Adding default port to config docs --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 3212aea9..2074dceb 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -36,7 +36,7 @@ Windows-specific options: All options below still possible in cmdline, but better write it in config file: * --http.address= - The address to listen on (HTTP server) -* --http.port= - The port to listen on (HTTP server) +* --http.port= - The port to listen on (HTTP server) 7070 by default * --http.auth - Enable basic HTTP auth for webconsole * --http.user= - Username for basic auth (default: i2pd) * --http.pass= - Password for basic auth (default: random, see logs) From a4cfdcb5c413fd0c8f16e24265562b15e35bcdb5 Mon Sep 17 00:00:00 2001 From: Mikal Villa Date: Sat, 7 Jan 2017 02:17:02 +0100 Subject: [PATCH 40/60] Improved and minimalized docker image --- build/docker/Dockerfile | 54 +++++++++++++++++++ build/docker/entrypoint.sh | 24 +++++++++ .../{ => docker/old-ubuntu-based}/Dockerfile | 0 3 files changed, 78 insertions(+) create mode 100644 build/docker/Dockerfile create mode 100644 build/docker/entrypoint.sh rename build/{ => docker/old-ubuntu-based}/Dockerfile (100%) diff --git a/build/docker/Dockerfile b/build/docker/Dockerfile new file mode 100644 index 00000000..229d0d53 --- /dev/null +++ b/build/docker/Dockerfile @@ -0,0 +1,54 @@ +FROM alpine:latest + +MAINTAINER Mikal Villa + +ENV GIT_BRANCH="master" +ENV I2PD_PREFIX="/opt/i2pd-${GIT_BRANCH}" +ENV PATH=${I2PD_PREFIX}/bin:$PATH + +ENV GOSU_VERSION=1.7 +ENV GOSU_SHASUM="34049cfc713e8b74b90d6de49690fa601dc040021980812b2f1f691534be8a50 /usr/local/bin/gosu" + +RUN mkdir /user && adduser -S -h /user i2pd && chown -R i2pd:nobody /user + + +# +# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the +# image under 20mb we need to remove all the build dependencies in the same "RUN" / layer. +# + +# 1. install deps, clone and build. +# 2. strip binaries. +# 3. Purge all dependencies and other unrelated packages, including build directory. +RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool boost-dev build-base openssl-dev openssl git \ + && mkdir -p /tmp/build \ + && cd /tmp/build && git clone -b ${GIT_BRANCH} https://github.com/PurpleI2P/i2pd.git \ + && cd i2pd \ + && make -j4 \ + && mkdir -p ${I2PD_PREFIX}/bin \ + && mv i2pd ${I2PD_PREFIX}/bin/ \ + && cd ${I2PD_PREFIX}/bin \ + && strip i2pd \ + && rm -fr /tmp/build && apk --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \ + boost-python3 python3 gdbm boost-unit_test_framework boost-python linux-headers boost-prg_exec_monitor \ + boost-serialization boost-signals boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \ + libtool g++ gcc pkgconfig + +# 2. Adding required libraries to run i2pd to ensure it will run. +RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl musl-utils libstdc++ + +# Gosu is a replacement for su/sudo in docker and not a backdoor :) See https://github.com/tianon/gosu +RUN wget -O /usr/local/bin/gosu https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64 \ + && echo "${GOSU_SHASUM}" | sha256sum -c && chmod +x /usr/local/bin/gosu + +COPY entrypoint.sh /entrypoint.sh + +RUN chmod a+x /entrypoint.sh +RUN echo "export PATH=${PATH}" >> /etc/profile + +VOLUME [ "/var/lib/i2pd" ] + +EXPOSE 7070 4444 4447 7656 2827 7654 7650 + +ENTRYPOINT [ "/entrypoint.sh" ] + diff --git a/build/docker/entrypoint.sh b/build/docker/entrypoint.sh new file mode 100644 index 00000000..24a415aa --- /dev/null +++ b/build/docker/entrypoint.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +ARGS="" +if [ "${ENABLE_IPV6}" != "" ]; then + ARGS="${ARGS} –ipv6" +fi + +if [ "${LOGLEVEL}" != "" ]; then + ARGS="${ARGS} –loglevel=${LOGLEVEL}" +fi + +if [ "${ENABLE_AUTH}" != "" ]; then + ARGS="${ARGS} –http.auth" +fi + + +# To make ports exposeable +DEFAULT_ARGS=" –http.address=0.0.0.0 –httpproxy.address=0.0.0.0 -socksproxy.address=0.0.0.0 –sam.address=0.0.0.0 –bob.address=0.0.0.0 –i2cp.address=0.0.0.0 –i2pcontrol.port=0.0.0.0 –upnp.enabled=false -service " + +mkdir -p /var/lib/i2pd && chown -R i2pd:nobody /var/lib/i2pd && chmod u+rw /var/lib/i2pd + +gosu i2pd i2pd $DEFAULT_ARGS $ARGS + + diff --git a/build/Dockerfile b/build/docker/old-ubuntu-based/Dockerfile similarity index 100% rename from build/Dockerfile rename to build/docker/old-ubuntu-based/Dockerfile From 5b2bc23d03b2975036451d512ee58ec1e8f1ec45 Mon Sep 17 00:00:00 2001 From: Mikal Villa Date: Sat, 7 Jan 2017 02:30:17 +0100 Subject: [PATCH 41/60] Adding readme --- build/docker/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 build/docker/README.md diff --git a/build/docker/README.md b/build/docker/README.md new file mode 100644 index 00000000..df2cdc01 --- /dev/null +++ b/build/docker/README.md @@ -0,0 +1,34 @@ +Howto build & run +================== + +**Build** + +Assuming you're in the root directory of the anoncoin source code. + +$ `cd build/docker` +$ `docker -t meeh/i2pd:latest .` + +**Run** + +To run either the local build, or if not found - fetched prebuild from hub.docker.io, run the following command. + +$ `docker run --name anonnode -v /path/to/i2pd/datadir/on/host:/var/lib/i2pd -p 7070:7070 -p 4444:4444 -p 4447:4447 -p 7656:7656 -p 2827:2827 -p 7654:7654 -p 7650:7650 -d meeh/i2pd` + +All the ports ( -p HOSTPORT:DOCKERPORT ) is optional. However the command above enable all features (Webconsole, HTTP Proxy, BOB, SAM, i2cp, etc) + +The volume ( -v HOSTDIR:DOCKERDIR ) is also optional, but if you don't use it, your config, routerid and private keys will die along with the container. + +**Options** + +Options are set via docker environment variables. This can be set at run with -e parameters. + +* **ENABLE_IPV6** - Enable IPv6 support. Any value can be used - it triggers as long as it's not empty. +* **LOGLEVEL** - Set the loglevel. +* **ENABLE_AUTH** - Enable auth for the webconsole. Username and password needs to be set manually in i2pd.conf cause security reasons. + +**Logging** + +Logging happens to STDOUT as the best practise with docker containers, since infrastructure systems like kubernetes with ELK integration can automaticly forward the log to say, kibana or greylog without manual setup. :) + + + From 3755002381e4fc29365d3ddc75ac4a392c9f101d Mon Sep 17 00:00:00 2001 From: Mikal Villa Date: Sat, 7 Jan 2017 02:56:09 +0100 Subject: [PATCH 42/60] Moving dockerfile to trigger autobuild of docker images. --- build/docker/Dockerfile => Dockerfile | 0 build/docker/entrypoint.sh => entrypoint.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename build/docker/Dockerfile => Dockerfile (100%) rename build/docker/entrypoint.sh => entrypoint.sh (100%) diff --git a/build/docker/Dockerfile b/Dockerfile similarity index 100% rename from build/docker/Dockerfile rename to Dockerfile diff --git a/build/docker/entrypoint.sh b/entrypoint.sh similarity index 100% rename from build/docker/entrypoint.sh rename to entrypoint.sh From c5d3c0c6f86fbd7c339168a3fe11d060506efec5 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sat, 7 Jan 2017 08:32:50 -0500 Subject: [PATCH 43/60] * 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 | 641 ++++++++++++++++++++++------------------- WebSocks.h | 31 +- android/jni/Android.mk | 3 +- 8 files changed, 408 insertions(+), 341 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)); - i2p::data::PrivateKeys k = i2p::data::PrivateKeys::CreateRandomKeys(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); - m_Dest = std::make_shared(k, false); + m_Server.init_asio(); + m_Server.set_open_handler(std::bind(&WebSocksImpl::ConnOpened, this, std::placeholders::_1)); } - ServerImpl::connection_ptr GetConn(const websocketpp::connection_hdl & conn) - { - return m_Server.get_con_from_hdl(conn); - } + void InitializeDestination(WebSocks * parent) + { + Parent = parent; + m_Dest = Parent->GetLocalDestination(); + } - void CloseConn(const websocketpp::connection_hdl & conn) - { - auto c = GetConn(conn); - if(c) c->close(websocketpp::close::status::normal, "closed"); - } + ServerImpl::connection_ptr GetConn(const websocketpp::connection_hdl & conn) + { + return m_Server.get_con_from_hdl(conn); + } - 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 CloseConn(const websocketpp::connection_hdl & conn) + { + auto c = GetConn(conn); + if(c) c->close(websocketpp::close::status::normal, "closed"); + } - void ConnOpened(websocketpp::connection_hdl conn) - { - m_Conns.push_back(CreateWebSocksConn(conn, this)); - } + 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 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 ConnOpened(websocketpp::connection_hdl conn) + { + auto ptr = CreateWebSocksConn(conn, this); + Parent->AddHandler(ptr); + m_Conns.push_back(ptr); + } - void Stop() - { - for(const auto & conn : m_Conns) - conn->Close(); + 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(); + } - m_Dest->Stop(); - m_Run = false; - m_Server.stop(); - if(m_Thread) { - m_Thread->join(); - delete m_Thread; - } - m_Thread = nullptr; - } + void Stop() + { + for(const auto & conn : m_Conns) + conn->Close(); - 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; + m_Dest->Stop(); + m_Run = false; + m_Server.stop(); + if(m_Thread) { + m_Thread->join(); + delete m_Thread; + } + m_Thread = nullptr; + } + + 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 - }; + 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; + 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]; + 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(const ServerConn & conn, WebSocksImpl * parent) : + IWebSocksConn(parent->Parent), + m_Conn(conn), + m_Stream(nullptr), + m_State(eWSCInitial), + m_Parent(parent) + { - } + } - ~WebSocksConn() - { - Close(); - } + ~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 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"); + void StartForwarding() + { + LogPrint(eLogDebug, "websocks: begin forwarding data"); uint8_t b[1]; m_Stream->Send(b, 0); - AsyncRecv(); - } + 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); - + 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); - } + 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()); - } + /** @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 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); - } - } + } 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); - } - } - }; + 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; - } + 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 44/60] 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 45/60] 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, ""); From d83fc3181be099fc0fe91f4c5532546c28cc58c1 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 7 Jan 2017 21:20:09 -0500 Subject: [PATCH 46/60] EdDSA keys compatible with Java --- Identity.cpp | 2 +- Signature.cpp | 14 ++++++++++++-- Signature.h | 3 ++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Identity.cpp b/Identity.cpp index 19c1a240..4e9bee63 100644 --- a/Identity.cpp +++ b/Identity.cpp @@ -509,7 +509,7 @@ namespace data m_Signer.reset (new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey)); break; case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey)); + m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, m_Public->GetStandardIdentity ().certificate - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH)); break; default: LogPrint (eLogError, "Identity: Signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported"); diff --git a/Signature.cpp b/Signature.cpp index 11fc8600..c38b1333 100644 --- a/Signature.cpp +++ b/Signature.cpp @@ -467,17 +467,27 @@ namespace crypto return GetEd25519 ()->Verify (m_PublicKey, digest, signature); } - EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey) + EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) { // expand key SHA512 (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH, m_ExpandedPrivateKey); m_ExpandedPrivateKey[0] &= 0xF8; // drop last 3 bits - m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x1F; // drop first 3 bits + m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x3F; // drop first 2 bits m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit + // generate and encode public key BN_CTX * ctx = BN_CTX_new (); auto publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx); GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx); + + if (signingPublicKey && memcmp (m_PublicKeyEncoded, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) + { + // keys don't match, it means older key with 0x1F + LogPrint (eLogWarning, "Older EdDSA key detected"); + m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0xDF; // drop third bit + publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx); + GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx); + } BN_CTX_free (ctx); } diff --git a/Signature.h b/Signature.h index a4f37980..c2618f91 100644 --- a/Signature.h +++ b/Signature.h @@ -424,7 +424,8 @@ namespace crypto { public: - EDDSA25519Signer (const uint8_t * signingPrivateKey); + EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr); + // we pass signingPublicKey to check if it matches private key void Sign (const uint8_t * buf, int len, uint8_t * signature) const; const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; From 11f5db871f3b1e5646239e653e4e92b5ff10da29 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 8 Jan 2017 09:07:54 -0500 Subject: [PATCH 47/60] don't copy private keys --- NTCPSession.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NTCPSession.cpp b/NTCPSession.cpp index 44b4791e..8f31e246 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -257,7 +257,7 @@ namespace transport void NTCPSession::SendPhase3 () { - auto keys = i2p::context.GetPrivateKeys (); + auto& keys = i2p::context.GetPrivateKeys (); uint8_t * buf = m_ReceiveBuffer; htobe16buf (buf, keys.GetPublic ()->GetFullLen ()); buf += 2; @@ -403,7 +403,7 @@ namespace transport s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident s.Insert (tsA); // tsA s.Insert (tsB); // tsB - auto keys = i2p::context.GetPrivateKeys (); + auto& keys = i2p::context.GetPrivateKeys (); auto signatureLen = keys.GetPublic ()->GetSignatureLen (); s.Sign (keys, m_ReceiveBuffer); size_t paddingSize = signatureLen & 0x0F; // %16 From 01ab0276152431fc1643a46e519863ab4d0ab336 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 8 Jan 2017 11:09:33 -0500 Subject: [PATCH 48/60] don't use heap allocated buffers in ssu --- SSUData.cpp | 9 +++------ SSUSession.cpp | 41 +++++++++++++++++------------------------ 2 files changed, 20 insertions(+), 30 deletions(-) diff --git a/SSUData.cpp b/SSUData.cpp index b9b1d93e..c700d8e6 100644 --- a/SSUData.cpp +++ b/SSUData.cpp @@ -154,8 +154,7 @@ namespace transport { uint32_t msgID = bufbe32toh (buf); // message ID buf += 4; - uint8_t frag[4]; - frag[0] = 0; + uint8_t frag[4] = {0}; memcpy (frag + 1, buf, 3); buf += 3; uint32_t fragmentInfo = bufbe32toh (frag); // fragment info @@ -371,7 +370,7 @@ namespace transport void SSUData::SendMsgAck (uint32_t msgID) { - uint8_t * buf = new uint8_t[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16 + uint8_t buf[48 + 18] = {0}; // 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,7 +383,6 @@ 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) @@ -394,7 +392,7 @@ namespace transport LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64"); return; } - uint8_t * buf = new uint8_t[64 + 18]; + uint8_t buf[64 + 18] = {0}; uint8_t * payload = buf + sizeof (SSUHeader); *payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag payload++; @@ -414,7 +412,6 @@ 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 050b2f9a..844ed2e4 100644 --- a/SSUSession.cpp +++ b/SSUSession.cpp @@ -350,7 +350,7 @@ namespace transport void SSUSession::SendSessionRequest () { - uint8_t * buf = new uint8_t[320 + 18]; // 304 bytes for ipv4, 320 for ipv6 + uint8_t buf[320 + 18] = {0}; // 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,7 +381,6 @@ 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) @@ -393,7 +392,7 @@ namespace transport return; } - uint8_t * buf = new uint8_t[96 + 18]; + uint8_t buf[96 + 18] = {0}; uint8_t * payload = buf + sizeof (SSUHeader); htobe32buf (payload, introducer.iTag); payload += 4; @@ -414,7 +413,6 @@ 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) @@ -429,7 +427,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 = new uint8_t[384 + 18]; + uint8_t buf[384 + 18] = {0}; uint8_t * payload = buf + sizeof (SSUHeader); memcpy (payload, m_DHKeysPair->GetPublicKey (), 256); s.Insert (payload, 256); // y @@ -475,14 +473,18 @@ namespace transport m_SignedData = std::unique_ptr(new SignedData (s)); s.Insert (payload - 4, 4); // BOB's signed on time s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature - // TODO: fill padding with random data uint8_t iv[16]; RAND_bytes (iv, 16); // random iv // encrypt signature and padding with newly created session key size_t signatureLen = i2p::context.GetIdentity ()->GetSignatureLen (); size_t paddingSize = signatureLen & 0x0F; // %16 - if (paddingSize > 0) signatureLen += (16 - paddingSize); + if (paddingSize > 0) + { + // fill random padding + RAND_bytes(payload + signatureLen, (16 - paddingSize)); + signatureLen += (16 - paddingSize); + } m_SessionKeyEncryption.SetIV (iv); m_SessionKeyEncryption.Encrypt (payload, signatureLen, payload); payload += signatureLen; @@ -491,12 +493,11 @@ 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 = new uint8_t[512 + 18]; + uint8_t buf[512 + 18] = {0}; uint8_t * payload = buf + sizeof (SSUHeader); *payload = 1; // 1 fragment payload++; // info @@ -511,9 +512,8 @@ namespace transport auto signatureLen = i2p::context.GetIdentity ()->GetSignatureLen (); size_t paddingSize = ((payload - buf) + signatureLen)%16; if (paddingSize > 0) paddingSize = 16 - paddingSize; - // TODO: fill padding + RAND_bytes(payload, paddingSize); // fill padding with random payload += paddingSize; // padding size - // signature SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time s.Insert (m_DHKeysPair->GetPublicKey (), 256); // x @@ -535,7 +535,6 @@ 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) @@ -569,7 +568,7 @@ namespace transport 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 buf[80 + 18] = {0}; // 64 Alice's ipv4 and 80 Alice's ipv6 uint8_t * payload = buf + sizeof (SSUHeader); *payload = 4; payload++; // size @@ -612,7 +611,6 @@ 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) @@ -624,7 +622,7 @@ namespace transport LogPrint (eLogWarning, "SSU: Alice's IP must be v4"); return; } - uint8_t * buf = new uint8_t[48 + 18]; + uint8_t buf[48 + 18] = {0}; uint8_t * payload = buf + sizeof (SSUHeader); *payload = 4; payload++; // size @@ -638,7 +636,6 @@ 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) @@ -1046,7 +1043,7 @@ namespace transport // toAddress is true for Alice<->Chalie communications only // sendAddress is false if message comes from Alice { - uint8_t * buf = new uint8_t[80 + 18]; + uint8_t buf[80 + 18] = {0}; uint8_t iv[16]; uint8_t * payload = buf + sizeof (SSUHeader); htobe32buf (payload, nonce); @@ -1103,7 +1100,6 @@ namespace transport FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80); Send (buf, 80); } - delete [] buf; } void SSUSession::SendPeerTest () @@ -1128,7 +1124,7 @@ namespace transport { if (m_State == eSessionStateEstablished) { - uint8_t * buf = new uint8_t[48 + 18]; + uint8_t buf[48 + 18] = {0}; uint8_t * payload = buf + sizeof (SSUHeader); *payload = 0; // flags payload++; @@ -1138,7 +1134,6 @@ namespace transport Send (buf, 48); LogPrint (eLogDebug, "SSU: keep-alive sent"); m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - delete [] buf; } } @@ -1146,7 +1141,7 @@ namespace transport { if (m_IsSessionKey) { - uint8_t * buf = new uint8_t[48 + 18]; + uint8_t buf[48 + 18] = {0}; // encrypt message with session key FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48); try @@ -1158,13 +1153,12 @@ 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 = new uint8_t[SSU_MTU_V4 + 18]; + uint8_t buf[SSU_MTU_V4 + 18] = {0}; size_t msgSize = len + sizeof (SSUHeader); size_t paddingSize = msgSize & 0x0F; // %16 if (paddingSize > 0) msgSize += (16 - paddingSize); @@ -1177,7 +1171,6 @@ 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 ca6f7556348604b2cab87a19bb91ffefb46f18c0 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 10 Jan 2017 15:08:01 -0500 Subject: [PATCH 49/60] http.enabled --- docs/configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/configuration.md b/docs/configuration.md index 2074dceb..4894bfe3 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -35,6 +35,7 @@ Windows-specific options: All options below still possible in cmdline, but better write it in config file: +* --http.enabled= - If webconsole is enabled. true by default * --http.address= - The address to listen on (HTTP server) * --http.port= - The port to listen on (HTTP server) 7070 by default * --http.auth - Enable basic HTTP auth for webconsole From feab95ce4b36ca8f293dbb5ded55846071b55970 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 10 Jan 2017 16:14:18 -0500 Subject: [PATCH 50/60] initial commit for memory pool --- util.h | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/util.h b/util.h index b8de8b5a..ac2c38d1 100644 --- a/util.h +++ b/util.h @@ -27,6 +27,47 @@ namespace i2p { namespace util { + + template + class MemoryPool + { + public: + + MemoryPool (): m_Head (nullptr) {}; + ~MemoryPool () + { + while (m_Head) + { + auto tmp = m_Head; + m_Head = static_cast(*(void * *)m_Head); // next + delete tmp; + } + } + + template + T * Acquire (TArgs... args) + { + if (!m_Head) return new T(args...); + else + { + auto tmp = m_Head; + m_Head = static_cast(*(void * *)m_Head); // next + return new (tmp)T(args...); + } + } + + void Release (T * t) + { + t->~T (); + *(void * *)t = m_Head; + m_Head = t; + } + + private: + + T * m_Head; + }; + namespace net { int GetMTU (const boost::asio::ip::address& localAddress); From 7ea0249e6effc7ea5814883a11ad6d3c6773f59c Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 10 Jan 2017 21:31:52 -0500 Subject: [PATCH 51/60] use memory poll for streaming --- Streaming.cpp | 34 +++++++++++++++++----------------- Streaming.h | 8 +++++++- util.h | 3 ++- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/Streaming.cpp b/Streaming.cpp index ff14aadb..912e21e5 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -40,15 +40,15 @@ namespace stream { auto packet = m_ReceiveQueue.front (); m_ReceiveQueue.pop (); - delete packet; + m_LocalDestination.DeletePacket (packet); } for (auto it: m_SentPackets) - delete it; + m_LocalDestination.DeletePacket (it); m_SentPackets.clear (); for (auto it: m_SavedPackets) - delete it; + m_LocalDestination.DeletePacket (it); m_SavedPackets.clear (); LogPrint (eLogDebug, "Streaming: Stream deleted"); @@ -83,7 +83,7 @@ namespace stream { // plain ack LogPrint (eLogDebug, "Streaming: Plain ACK received"); - delete packet; + m_LocalDestination.DeletePacket (packet); return; } @@ -131,7 +131,7 @@ namespace stream // we have received duplicate LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " on sSID=", m_SendStreamID); SendQuickAck (); // resend ack for previous message again - delete packet; // packet dropped + m_LocalDestination.DeletePacket (packet); // packet dropped } else { @@ -163,7 +163,7 @@ namespace stream void Stream::SavePacket (Packet * packet) { if (!m_SavedPackets.insert (packet).second) - delete packet; + m_LocalDestination.DeletePacket (packet); } void Stream::ProcessPacket (Packet * packet) @@ -216,7 +216,7 @@ namespace stream m_ReceiveTimer.cancel (); } else - delete packet; + m_LocalDestination.DeletePacket (packet); m_LastReceivedSequenceNumber = receivedSeqn; @@ -278,7 +278,7 @@ namespace stream m_RTO = m_RTT*1.5; // TODO: implement it better LogPrint (eLogDebug, "Streaming: Packet ", seqn, " acknowledged rtt=", rtt, " sentTime=", sentPacket->sendTime); m_SentPackets.erase (it++); - delete sentPacket; + m_LocalDestination.DeletePacket (sentPacket); acknowledged = true; if (m_WindowSize < WINDOW_SIZE) m_WindowSize++; // slow start @@ -345,7 +345,7 @@ namespace stream std::unique_lock l(m_SendBufferMutex); while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.eof () && numMsgs > 0)) { - Packet * p = new Packet (); + Packet * p = m_LocalDestination.NewPacket (); uint8_t * packet = p->GetBuffer (); // TODO: implement setters size_t size = 0; @@ -532,7 +532,7 @@ namespace stream void Stream::SendClose () { - Packet * p = new Packet (); + Packet * p = m_LocalDestination.NewPacket (); uint8_t * packet = p->GetBuffer (); size_t size = 0; htobe32buf (packet + size, m_SendStreamID); @@ -574,7 +574,7 @@ namespace stream if (!packet->GetLength ()) { m_ReceiveQueue.pop (); - delete packet; + m_LocalDestination.DeletePacket (packet); } } return pos; @@ -852,7 +852,7 @@ namespace stream { for (auto& it: m_SavedPackets) { - for (auto it1: it.second) delete it1; + for (auto it1: it.second) DeletePacket (it1); it.second.clear (); } m_SavedPackets.clear (); @@ -889,7 +889,7 @@ namespace stream else { LogPrint (eLogError, "Streaming: Unknown stream sSID=", sendStreamID); - delete packet; + DeletePacket (packet); } } else @@ -901,7 +901,7 @@ namespace stream { // already pending LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists"); - delete packet; // drop it, because previous should be connected + DeletePacket (packet); // drop it, because previous should be connected return; } auto incomingStream = CreateNewIncomingStream (); @@ -980,7 +980,7 @@ namespace stream auto it = s->m_SavedPackets.find (receiveStreamID); if (it != s->m_SavedPackets.end ()) { - for (auto it1: it->second) delete it1; + for (auto it1: it->second) s->DeletePacket (it1); it->second.clear (); s->m_SavedPackets.erase (it); } @@ -1074,13 +1074,13 @@ namespace stream void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len) { // unzip it - Packet * uncompressed = new Packet; + Packet * uncompressed = NewPacket (); uncompressed->offset = 0; uncompressed->len = m_Inflator.Inflate (buf, len, uncompressed->buf, MAX_PACKET_SIZE); if (uncompressed->len) HandleNextPacket (uncompressed); else - delete uncompressed; + DeletePacket (uncompressed); } std::shared_ptr StreamingDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort) diff --git a/Streaming.h b/Streaming.h index 1593f961..828fccc7 100644 --- a/Streaming.h +++ b/Streaming.h @@ -18,6 +18,7 @@ #include "I2NPProtocol.h" #include "Garlic.h" #include "Tunnel.h" +#include "util.h" // MemoryPool namespace i2p { @@ -234,6 +235,9 @@ namespace stream /** set max connections per minute per destination */ void SetMaxConnsPerMinute(const uint32_t conns); + + Packet * NewPacket () { return m_PacketsPool.Acquire (); }; + void DeletePacket (Packet * p) { if (p) m_PacketsPool.Release (p); }; private: @@ -269,7 +273,9 @@ namespace stream /** banned identities */ std::vector m_Banned; uint64_t m_LastBanClear; - + + i2p::util::MemoryPool m_PacketsPool; + public: i2p::data::GzipInflator m_Inflator; diff --git a/util.h b/util.h index ac2c38d1..84299d25 100644 --- a/util.h +++ b/util.h @@ -58,8 +58,9 @@ namespace util void Release (T * t) { + if (!t) return; t->~T (); - *(void * *)t = m_Head; + *(void * *)t = m_Head; // next m_Head = t; } From 719de94821460d918fa78a39fbd1162d4b58baeb Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 11 Jan 2017 19:45:04 -0500 Subject: [PATCH 52/60] acquire unique_ptr --- util.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/util.h b/util.h index 84299d25..3b30919e 100644 --- a/util.h +++ b/util.h @@ -1,9 +1,9 @@ #ifndef UTIL_H #define UTIL_H -#include #include -#include +#include +#include #include #ifdef ANDROID @@ -45,7 +45,7 @@ namespace util } template - T * Acquire (TArgs... args) + T * Acquire (TArgs&&... args) { if (!m_Head) return new T(args...); else @@ -64,6 +64,13 @@ namespace util m_Head = t; } + template + std::unique_ptr > AcquireUnique (TArgs&&... args) + { + return std::unique_ptr >(Acquire (args...), + std::bind (&MemoryPool::Release, this, std::placeholders::_1)); + } + private: T * m_Head; From e7b1ded486df28e17c0c6aff280e33b575bb7463 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 12 Jan 2017 14:19:57 -0500 Subject: [PATCH 53/60] correct behaviour of IsAcceptorSet --- Streaming.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Streaming.cpp b/Streaming.cpp index 912e21e5..343b9b38 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -867,6 +867,7 @@ namespace stream { ResetAcceptor (); m_PendingIncomingTimer.cancel (); + m_PendingIncomingStreams.clear (); m_ConnTrackTimer.cancel(); { std::unique_lock l(m_StreamsMutex); @@ -1020,14 +1021,16 @@ namespace stream void StreamingDestination::SetAcceptor (const Acceptor& acceptor) { - m_Owner->GetService ().post([acceptor, this](void) + m_Acceptor = acceptor; // we must set it immediately for IsAcceptorSet + auto s = shared_from_this (); + m_Owner->GetService ().post([s](void) { - m_Acceptor = acceptor; - for (auto& it: m_PendingIncomingStreams) + // take care about incoming queue + for (auto& it: s->m_PendingIncomingStreams) if (it->GetStatus () == eStreamStatusOpen) // still open? - m_Acceptor (it); - m_PendingIncomingStreams.clear (); - m_PendingIncomingTimer.cancel (); + s->m_Acceptor (it); + s->m_PendingIncomingStreams.clear (); + s->m_PendingIncomingTimer.cancel (); }); } From f46d96c4c650c7104ba42ed66ddddb1acb2fd8d2 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 12 Jan 2017 16:17:11 -0500 Subject: [PATCH 54/60] renamed maptolooback to enableuniquelocal --- ClientContext.cpp | 15 +++++++++------ ClientContext.h | 2 +- I2PTunnel.cpp | 23 ++++++++++++----------- I2PTunnel.h | 11 ++++++----- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/ClientContext.cpp b/ClientContext.cpp index af108dd7..0354e708 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -527,7 +527,7 @@ 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); + bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); // I2CP std::map options; @@ -547,10 +547,11 @@ 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) { + if(!isUniqueLocal) + { LogPrint(eLogInfo, "Clients: disabling loopback address mapping"); + serverTunnel->SetUniqueLocal(isUniqueLocal); } - serverTunnel->SetMapToLoopback(mapToLoopback); std::lock_guard lock(m_ForwardsMutex); if(m_ServerForwards.insert( std::make_pair( @@ -577,10 +578,12 @@ namespace client LogPrint(eLogInfo, "Clients: Set Max Conns To ", maxConns); serverTunnel->SetMaxConnsPerMinute(maxConns); - if(!mapToLoopback) + if(!isUniqueLocal) + { LogPrint(eLogInfo, "Clients: disabling loopback address mapping"); - serverTunnel->SetMapToLoopback(mapToLoopback); - + serverTunnel->SetUniqueLocal(isUniqueLocal); + } + if (accessList.length () > 0) { std::set idents; diff --git a/ClientContext.h b/ClientContext.h index fad042ed..1aa1e7f0 100644 --- a/ClientContext.h +++ b/ClientContext.h @@ -45,7 +45,7 @@ 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"; + const char I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL[] = "enableuniquelocal"; class ClientContext { diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index d5238e14..6f45bcbe 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -79,14 +79,14 @@ namespace client } - void I2PTunnelConnection::Connect (bool mapToLoopback) + void I2PTunnelConnection::Connect (bool isUniqueLocal) { I2PTunnelSetSocketOptions(m_Socket); - if (m_Socket) { - + if (m_Socket) + { #ifdef __linux__ - if (m_RemoteEndpoint.address ().is_v4 () && - m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127 && mapToLoopback) + if (isUniqueLocal && m_RemoteEndpoint.address ().is_v4 () && + m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127) { m_Socket->open (boost::asio::ip::tcp::v4 ()); auto ident = m_Stream->GetRemoteIdentity()->GetIdentHash(); @@ -432,7 +432,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_MapToLoopback(true), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false) + I2PService (localDestination), m_IsUniqueLocal(true), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false) { m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port, gzip); } @@ -517,7 +517,7 @@ namespace client { auto conn = std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint ()); AddHandler (conn); - conn->Connect (m_MapToLoopback); + conn->Connect (m_IsUniqueLocal); } I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& name, const std::string& address, @@ -598,12 +598,13 @@ namespace client } boost::asio::ip::address addr; /** create new udp session */ - if(m_LocalAddress.is_loopback() && m_MapToLoopback) { + if(m_IsUniqueLocal && m_LocalAddress.is_loopback()) + { auto ident = from.GetIdentHash(); addr = GetLoopbackAddressFor(ident); - } else { + } + 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(); @@ -649,7 +650,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_IsUniqueLocal(true), m_Name(name), m_LocalAddress(localAddress), m_RemoteEndpoint(forwardTo) diff --git a/I2PTunnel.h b/I2PTunnel.h index 2b895508..fab33803 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 (bool mapToLoopback = true); + void Connect (bool isUniqueLocal = true); protected: @@ -201,7 +201,7 @@ namespace client std::vector > GetSessions(); std::shared_ptr GetLocalDestination () const { return m_LocalDest; } - void SetMapToLoopback(bool mapToLoopback = true) { m_MapToLoopback = mapToLoopback; } + void SetUniqueLocal(bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; } private: @@ -209,7 +209,7 @@ namespace client UDPSessionPtr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); private: - bool m_MapToLoopback; + bool m_IsUniqueLocal; const std::string m_Name; boost::asio::ip::address m_LocalAddress; boost::asio::ip::udp::endpoint m_RemoteEndpoint; @@ -267,7 +267,7 @@ namespace client void SetAccessList (const std::set& accessList); - void SetMapToLoopback(bool mapToLoopback) { m_MapToLoopback = mapToLoopback; } + void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; } const std::string& GetAddress() const { return m_Address; } int GetPort () const { return m_Port; }; @@ -288,7 +288,8 @@ namespace client virtual void CreateI2PConnection (std::shared_ptr stream); private: - bool m_MapToLoopback; + + bool m_IsUniqueLocal; std::string m_Name, m_Address; int m_Port; boost::asio::ip::tcp::endpoint m_Endpoint; From c68aca4adad13c96b3fb731964eb7c677acea159 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 13 Jan 2017 11:45:52 -0500 Subject: [PATCH 55/60] try fixing crash in datagram code --- Datagram.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Datagram.cpp b/Datagram.cpp index 51c6c34d..38dfc3e1 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -45,7 +45,8 @@ namespace datagram owner->Sign (buf1, len, signature); auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort); - ObtainSession(identity)->SendMsg(msg); + auto session = ObtainSession(identity); + session->SendMsg(msg); } @@ -69,7 +70,8 @@ namespace datagram if (verified) { auto h = identity.GetIdentHash(); - ObtainSession(h)->Ack(); + auto session = ObtainSession(h); + session->Ack(); auto r = FindReceiver(toPort); if(r) r(identity, fromPort, toPort, buf + headerLen, len -headerLen); @@ -332,7 +334,7 @@ namespace datagram { 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(); }); + m_SendQueueTimer.async_wait([this](const boost::system::error_code & ec) { if(ec) return; FlushSendQueue(); }); } } } From a8bd87938dcaba7bc921d2383446bb16f53ba488 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 13 Jan 2017 13:47:51 -0500 Subject: [PATCH 56/60] honor enableuniquelocal for all server tunnel types --- I2PTunnel.cpp | 24 +++++++++++------------- I2PTunnel.h | 7 ++++--- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 6f45bcbe..843d129f 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -509,15 +509,17 @@ namespace client return; } } - CreateI2PConnection (stream); + // new connection + auto conn = CreateI2PConnection (stream); + AddHandler (conn); + conn->Connect (m_IsUniqueLocal); } } - void I2PServerTunnel::CreateI2PConnection (std::shared_ptr stream) + std::shared_ptr I2PServerTunnel::CreateI2PConnection (std::shared_ptr stream) { - auto conn = std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint ()); - AddHandler (conn); - conn->Connect (m_IsUniqueLocal); + return std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint ()); + } I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& name, const std::string& address, @@ -528,12 +530,10 @@ namespace client { } - void I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr stream) + std::shared_ptr I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr stream) { - auto conn = std::make_shared (this, stream, + return std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint (), m_Host); - AddHandler (conn); - conn->Connect (); } I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address, @@ -544,11 +544,9 @@ namespace client { } - void I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr stream) + std::shared_ptr I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr stream) { - auto conn = std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint (), this->m_WebircPass); - AddHandler (conn); - conn->Connect (); + return std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint (), this->m_WebircPass); } void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) diff --git a/I2PTunnel.h b/I2PTunnel.h index fab33803..4b9b2c9b 100644 --- a/I2PTunnel.h +++ b/I2PTunnel.h @@ -268,6 +268,7 @@ namespace client void SetAccessList (const std::set& accessList); void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; } + bool IsUniqueLocal () const { return m_IsUniqueLocal; } const std::string& GetAddress() const { return m_Address; } int GetPort () const { return m_Port; }; @@ -285,7 +286,7 @@ namespace client void Accept (); void HandleAccept (std::shared_ptr stream); - virtual void CreateI2PConnection (std::shared_ptr stream); + virtual std::shared_ptr CreateI2PConnection (std::shared_ptr stream); private: @@ -308,7 +309,7 @@ namespace client private: - void CreateI2PConnection (std::shared_ptr stream); + std::shared_ptr CreateI2PConnection (std::shared_ptr stream); private: @@ -325,7 +326,7 @@ namespace client private: - void CreateI2PConnection (std::shared_ptr stream); + std::shared_ptr CreateI2PConnection (std::shared_ptr stream); private: From b0b1c5af71d523b0fb0c8e3626c8ad14fce217cc Mon Sep 17 00:00:00 2001 From: Darknet Villain Date: Fri, 13 Jan 2017 14:24:53 -0500 Subject: [PATCH 57/60] Respect for netId option in api.cpp #696 --- api.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api.cpp b/api.cpp index 5148ed41..ec109439 100644 --- a/api.cpp +++ b/api.cpp @@ -33,6 +33,10 @@ namespace api #else i2p::crypto::InitCrypto (true); #endif + + int netID; i2p::config::GetOption("netid", netID); + i2p::context.SetNetID (netID); + i2p::context.Init (); } From 77918fd412c9629de89d9959200628fcbea85944 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 16 Jan 2017 07:54:56 -0500 Subject: [PATCH 58/60] use std::shared_from_this --- Datagram.cpp | 6 ++++-- Datagram.h | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Datagram.cpp b/Datagram.cpp index 38dfc3e1..705e10ef 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -182,7 +182,8 @@ namespace datagram // we used this session m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); // schedule send - m_LocalDestination->GetService().post(std::bind(&DatagramSession::HandleSend, this, msg)); + auto self = shared_from_this(); + m_LocalDestination->GetService().post(std::bind(&DatagramSession::HandleSend, self, msg)); } DatagramSession::Info DatagramSession::GetSessionInfo() const @@ -334,7 +335,8 @@ namespace datagram { boost::posix_time::milliseconds dlt(100); m_SendQueueTimer.expires_from_now(dlt); - m_SendQueueTimer.async_wait([this](const boost::system::error_code & ec) { if(ec) return; FlushSendQueue(); }); + auto self = shared_from_this(); + m_SendQueueTimer.async_wait([self](const boost::system::error_code & ec) { if(ec) return; self->FlushSendQueue(); }); } } } diff --git a/Datagram.h b/Datagram.h index 2eb180d6..a10f2646 100644 --- a/Datagram.h +++ b/Datagram.h @@ -34,7 +34,7 @@ namespace datagram // max 64 messages buffered in send queue for each datagram session const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64; - class DatagramSession + class DatagramSession : public std::enable_shared_from_this { public: DatagramSession(i2p::client::ClientDestination * localDestination, @@ -90,7 +90,9 @@ namespace datagram uint64_t m_LastUse; bool m_RequestingLS; }; - + + typedef std::shared_ptr DatagramSession_ptr; + const size_t MAX_DATAGRAM_SIZE = 32768; class DatagramDestination { @@ -132,7 +134,7 @@ namespace datagram i2p::data::IdentityEx m_Identity; Receiver m_Receiver; // default std::mutex m_SessionsMutex; - std::map > m_Sessions; + std::map m_Sessions; std::mutex m_ReceiversMutex; std::map m_ReceiversByPorts; From dc914b18064b1a2f4f544774f81c2c449e5428ac Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 16 Jan 2017 15:40:01 -0500 Subject: [PATCH 59/60] multithreaded memory pool --- util.h | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/util.h b/util.h index 3b30919e..2ee1c286 100644 --- a/util.h +++ b/util.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #ifdef ANDROID @@ -71,11 +72,44 @@ namespace util std::bind (&MemoryPool::Release, this, std::placeholders::_1)); } - private: + protected: T * m_Head; }; + template + class MemoryPoolMt: public MemoryPool + { + public: + + MemoryPoolMt () {}; + template + T * AcquireMt (TArgs&&... args) + { + if (!this->m_Head) return new T(args...); + std::lock_guard l(m_Mutex); + return this->Acquire (args...); + } + + void ReleaseMt (T * t) + { + std::lock_guard l(m_Mutex); + this->Release (t); + } + + templateclass C, typename... R> + void ReleaseMt(const C& c) + { + std::lock_guard l(m_Mutex); + for (auto& it: c) + this->Release (it); + } + + private: + + std::mutex m_Mutex; + }; + namespace net { int GetMTU (const boost::asio::ip::address& localAddress); From 9cb8e194b09b781d6b8c439eb8d2741983438618 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 16 Jan 2017 15:58:05 -0500 Subject: [PATCH 60/60] use generic container --- Queue.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Queue.h b/Queue.h index 39d139b9..9e95b2a5 100644 --- a/Queue.h +++ b/Queue.h @@ -25,7 +25,8 @@ namespace util m_NonEmpty.notify_one (); } - void Put (const std::vector& vec) + templateclass Container, typename... R> + void Put (const Container& vec) { if (!vec.empty ()) {