/ libi2pd_client / I2PService.cpp
I2PService.cpp
1 /* 2 * Copyright (c) 2013-2025, The PurpleI2P Project 3 * 4 * This file is part of Purple i2pd project and licensed under BSD3 5 * 6 * See full license text in LICENSE file at top of project tree 7 */ 8 9 #include "Destination.h" 10 #include "Identity.h" 11 #include "ClientContext.h" 12 #include "I2PService.h" 13 #include <boost/asio/error.hpp> 14 15 namespace i2p 16 { 17 namespace client 18 { 19 static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519; 20 21 I2PService::I2PService (std::shared_ptr<ClientDestination> localDestination): 22 m_LocalDestination (localDestination ? localDestination : 23 i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE)), 24 m_ReadyTimer(m_LocalDestination->GetService()), 25 m_ReadyTimerTriggered(false), 26 m_ConnectTimeout(0), 27 isUpdated (true) 28 { 29 m_LocalDestination->Acquire (); 30 } 31 32 I2PService::I2PService (i2p::data::SigningKeyType kt): 33 m_LocalDestination (i2p::client::context.CreateNewLocalDestination (false, kt)), 34 m_ReadyTimer(m_LocalDestination->GetService()), 35 m_ConnectTimeout(0), 36 isUpdated (true) 37 { 38 m_LocalDestination->Acquire (); 39 } 40 41 I2PService::~I2PService () 42 { 43 ClearHandlers (); 44 if (m_LocalDestination) m_LocalDestination->Release (); 45 } 46 47 void I2PService::ClearHandlers () 48 { 49 if(m_ConnectTimeout) 50 m_ReadyTimer.cancel(); 51 std::unique_lock<std::mutex> l(m_HandlersMutex); 52 for (auto it: m_Handlers) 53 it->Terminate (); 54 m_Handlers.clear(); 55 } 56 57 void I2PService::SetConnectTimeout(uint32_t timeout) 58 { 59 m_ConnectTimeout = timeout; 60 } 61 62 void I2PService::AddReadyCallback(ReadyCallback cb) 63 { 64 uint32_t now = i2p::util::GetSecondsSinceEpoch(); 65 uint32_t tm = (m_ConnectTimeout) ? now + m_ConnectTimeout : NEVER_TIMES_OUT; 66 67 LogPrint(eLogDebug, "I2PService::AddReadyCallback() ", tm, " ", now); 68 m_ReadyCallbacks.push_back({cb, tm}); 69 if (!m_ReadyTimerTriggered) TriggerReadyCheckTimer(); 70 } 71 72 void I2PService::TriggerReadyCheckTimer() 73 { 74 m_ReadyTimer.expires_from_now(boost::posix_time::seconds (1)); 75 m_ReadyTimer.async_wait(std::bind(&I2PService::HandleReadyCheckTimer, shared_from_this (), std::placeholders::_1)); 76 m_ReadyTimerTriggered = true; 77 78 } 79 80 void I2PService::HandleReadyCheckTimer(const boost::system::error_code &ec) 81 { 82 if(ec || m_LocalDestination->IsReady()) 83 { 84 for(auto & itr : m_ReadyCallbacks) 85 itr.first(ec); 86 m_ReadyCallbacks.clear(); 87 } 88 else if(!m_LocalDestination->IsReady()) 89 { 90 // expire timed out requests 91 uint32_t now = i2p::util::GetSecondsSinceEpoch (); 92 auto itr = m_ReadyCallbacks.begin(); 93 while(itr != m_ReadyCallbacks.end()) 94 { 95 if(itr->second != NEVER_TIMES_OUT && now >= itr->second) 96 { 97 itr->first(boost::asio::error::timed_out); 98 itr = m_ReadyCallbacks.erase(itr); 99 } 100 else 101 ++itr; 102 } 103 } 104 if(!ec && m_ReadyCallbacks.size()) 105 TriggerReadyCheckTimer(); 106 else 107 m_ReadyTimerTriggered = false; 108 } 109 110 void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, std::string_view dest, uint16_t port) { 111 assert(streamRequestComplete); 112 auto address = i2p::client::context.GetAddressBook ().GetAddress (dest); 113 if (address) 114 CreateStream(streamRequestComplete, address, port); 115 else 116 { 117 LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest); 118 streamRequestComplete (nullptr); 119 } 120 } 121 122 void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, std::shared_ptr<const Address> address, uint16_t port) 123 { 124 if(m_ConnectTimeout && !m_LocalDestination->IsReady()) 125 { 126 AddReadyCallback([this, streamRequestComplete, address, port] (const boost::system::error_code & ec) 127 { 128 if(ec) 129 { 130 LogPrint(eLogWarning, "I2PService::CreateStream() ", ec.message()); 131 streamRequestComplete(nullptr); 132 } 133 else 134 { 135 if (address->IsIdentHash ()) 136 this->m_LocalDestination->CreateStream(streamRequestComplete, address->identHash, port); 137 else 138 this->m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port); 139 } 140 }); 141 } 142 else 143 { 144 if (address->IsIdentHash ()) 145 m_LocalDestination->CreateStream (streamRequestComplete, address->identHash, port); 146 else 147 m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port); 148 } 149 } 150 } 151 }