/ 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  }