#ifndef TUNNEL_H__ #define TUNNEL_H__ #include #include #include #include #include #include #include #include #include #include "Queue.h" #include "Crypto.h" #include "TunnelConfig.h" #include "TunnelPool.h" #include "TransitTunnel.h" #include "TunnelEndpoint.h" #include "TunnelGateway.h" #include "TunnelBase.h" #include "I2NPProtocol.h" #include "Event.h" namespace i2p { namespace tunnel { template static void EmitTunnelEvent(const std::string & ev, const TunnelT & t) { #ifdef WITH_EVENTS EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}}); #else (void) ev; (void) t; #endif } template static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const T & val) { #ifdef WITH_EVENTS EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", std::to_string(val)}, {"inbound", std::to_string(t->IsInbound())}}); #else (void) ev; (void) t; (void) val; #endif } template static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const std::string & val) { #ifdef WITH_EVENTS EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", val}, {"inbound", std::to_string(t->IsInbound())}}); #else (void) ev; (void) t; (void) val; #endif } const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds const int STANDARD_NUM_RECORDS = 5; // in VariableTunnelBuild message enum TunnelState { eTunnelStatePending, eTunnelStateBuildReplyReceived, eTunnelStateBuildFailed, eTunnelStateEstablished, eTunnelStateTestFailed, eTunnelStateFailed, eTunnelStateExpiring }; class OutboundTunnel; class InboundTunnel; class Tunnel: public TunnelBase { struct TunnelHop { std::shared_ptr ident; i2p::crypto::TunnelDecryption decryption; }; public: Tunnel (std::shared_ptr config); ~Tunnel (); void Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel = nullptr); std::shared_ptr GetTunnelConfig () const { return m_Config; } std::vector > GetPeers () const; std::vector > GetInvertedPeers () const; TunnelState GetState () const { return m_State; }; void SetState (TunnelState state); bool IsEstablished () const { return m_State == eTunnelStateEstablished; }; bool IsFailed () const { return m_State == eTunnelStateFailed; }; bool IsRecreated () const { return m_IsRecreated; }; void SetIsRecreated () { m_IsRecreated = true; }; virtual bool IsInbound() const = 0; std::shared_ptr GetTunnelPool () const { return m_Pool; }; void SetTunnelPool (std::shared_ptr pool) { m_Pool = pool; }; bool HandleTunnelBuildResponse (uint8_t * msg, size_t len); virtual void Print (std::stringstream&) const {}; // implements TunnelBase void SendTunnelDataMsg (std::shared_ptr msg); void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out); protected: void PrintHops (std::stringstream& s) const; private: std::shared_ptr m_Config; std::vector > m_Hops; std::shared_ptr m_Pool; // pool, tunnel belongs to, or null TunnelState m_State; bool m_IsRecreated; }; class OutboundTunnel: public Tunnel { public: OutboundTunnel (std::shared_ptr config): Tunnel (config), m_Gateway (this), m_EndpointIdentHash (config->GetLastIdentHash ()) {}; void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg); virtual void SendTunnelDataMsg (const std::vector& msgs); // multiple messages const i2p::data::IdentHash& GetEndpointIdentHash () const { return m_EndpointIdentHash; }; virtual size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); }; void Print (std::stringstream& s) const; // implements TunnelBase void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); bool IsInbound() const { return false; } private: std::mutex m_SendMutex; TunnelGateway m_Gateway; i2p::data::IdentHash m_EndpointIdentHash; }; class InboundTunnel: public Tunnel, public std::enable_shared_from_this { public: InboundTunnel (std::shared_ptr config): Tunnel (config), m_Endpoint (true) {}; void HandleTunnelDataMsg (std::shared_ptr msg); virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; void Print (std::stringstream& s) const; bool IsInbound() const { return true; } // override TunnelBase void Cleanup () { m_Endpoint.Cleanup (); }; private: TunnelEndpoint m_Endpoint; }; class ZeroHopsInboundTunnel: public InboundTunnel { public: ZeroHopsInboundTunnel (); void SendTunnelDataMsg (std::shared_ptr msg); void Print (std::stringstream& s) const; size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; private: size_t m_NumReceivedBytes; }; class ZeroHopsOutboundTunnel: public OutboundTunnel { public: ZeroHopsOutboundTunnel (); void SendTunnelDataMsg (const std::vector& msgs); void Print (std::stringstream& s) const; size_t GetNumSentBytes () const { return m_NumSentBytes; }; private: size_t m_NumSentBytes; }; class Tunnels { public: Tunnels (); ~Tunnels (); void Start (); void Stop (); std::shared_ptr GetPendingInboundTunnel (uint32_t replyMsgID); std::shared_ptr GetPendingOutboundTunnel (uint32_t replyMsgID); std::shared_ptr GetNextInboundTunnel (); std::shared_ptr GetNextOutboundTunnel (); std::shared_ptr GetExploratoryPool () const { return m_ExploratoryPool; }; std::shared_ptr GetTunnel (uint32_t tunnelID); int GetTransitTunnelsExpirationTimeout (); void AddTransitTunnel (std::shared_ptr tunnel); void AddOutboundTunnel (std::shared_ptr newTunnel); void AddInboundTunnel (std::shared_ptr newTunnel); std::shared_ptr CreateInboundTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel); std::shared_ptr CreateOutboundTunnel (std::shared_ptr config); void PostTunnelData (std::shared_ptr msg); void PostTunnelData (const std::vector >& msgs); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); std::shared_ptr CreateTunnelPool (int numInboundHops, int numOuboundHops, int numInboundTunnels, int numOutboundTunnels); void DeleteTunnelPool (std::shared_ptr pool); void StopTunnelPool (std::shared_ptr pool); private: template std::shared_ptr CreateTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel = nullptr); template std::shared_ptr GetPendingTunnel (uint32_t replyMsgID, const std::map >& pendingTunnels); void HandleTunnelGatewayMsg (std::shared_ptr tunnel, std::shared_ptr msg); void Run (); void ManageTunnels (); void ManageOutboundTunnels (); void ManageInboundTunnels (); void ManageTransitTunnels (); void ManagePendingTunnels (); template void ManagePendingTunnels (PendingTunnels& pendingTunnels); void ManageTunnelPools (); std::shared_ptr CreateZeroHopsInboundTunnel (); std::shared_ptr CreateZeroHopsOutboundTunnel (); private: bool m_IsRunning; std::thread * m_Thread; std::map > m_PendingInboundTunnels; // by replyMsgID std::map > m_PendingOutboundTunnels; // by replyMsgID std::list > m_InboundTunnels; std::list > m_OutboundTunnels; std::list > m_TransitTunnels; std::unordered_map > m_Tunnels; // tunnelID->tunnel known by this id std::mutex m_PoolsMutex; std::list> m_Pools; std::shared_ptr m_ExploratoryPool; i2p::util::Queue > m_Queue; // some stats int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations; public: // for HTTP only const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; const decltype(m_TransitTunnels)& GetTransitTunnels () const { return m_TransitTunnels; }; size_t CountTransitTunnels() const; size_t CountInboundTunnels() const; size_t CountOutboundTunnels() const; int GetQueueSize () { return m_Queue.GetSize (); }; int GetTunnelCreationSuccessRate () const // in percents { int totalNum = m_NumSuccesiveTunnelCreations + m_NumFailedTunnelCreations; return totalNum ? m_NumSuccesiveTunnelCreations*100/totalNum : 0; } }; extern Tunnels tunnels; } } #endif