/ libi2pd / Destination.cpp
Destination.cpp
   1  /*
   2  * Copyright (c) 2013-2026, 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 <algorithm>
  10  #include <cassert>
  11  #include <string>
  12  #include <set>
  13  #include <vector>
  14  #include <charconv>
  15  #include <boost/algorithm/string.hpp>
  16  #include "Crypto.h"
  17  #include "ECIESX25519AEADRatchetSession.h"
  18  #include "Log.h"
  19  #include "FS.h"
  20  #include "Timestamp.h"
  21  #include "NetDb.hpp"
  22  #include "Destination.h"
  23  
  24  namespace i2p
  25  {
  26  namespace client
  27  {
  28  	LeaseSetDestination::LeaseSetDestination (boost::asio::io_context& service,
  29  		bool isPublic, const i2p::util::Mapping * params):
  30  		m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0),
  31  		m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
  32  		m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service),
  33  		m_LeaseSetType (DEFAULT_LEASESET_TYPE), m_AuthType (i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE)
  34  	{
  35  		int inLen   = DEFAULT_INBOUND_TUNNEL_LENGTH;
  36  		int inQty   = DEFAULT_INBOUND_TUNNELS_QUANTITY;
  37  		int outLen  = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
  38  		int outQty  = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
  39  		int inVar   = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE;
  40  		int outVar  = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE;
  41  		int numTags = DEFAULT_TAGS_TO_SEND;
  42  		bool isHighBandwidth = true;
  43  		std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
  44  		std::string_view explicitPeersStr, trustedRoutersStr;
  45  		try
  46  		{
  47  			if (params)
  48  			{
  49  				params->Get (I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen);
  50  				params->Get (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen);
  51  				params->Get (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, inQty);
  52  				params->Get (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, outQty);
  53  				params->Get (I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, inVar);
  54  				params->Get (I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, outVar);
  55  				params->Get (I2CP_PARAM_TAGS_TO_SEND, numTags);
  56  				LogPrint (eLogInfo, "Destination: Parameters for tunnel set to: ", inQty, " inbound (", inLen, " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags");
  57  				int ratchetsInboundTags = 0;
  58  				if (params->Get (I2CP_PARAM_RATCHET_INBOUND_TAGS, ratchetsInboundTags))
  59  				{
  60  					if (ratchetsInboundTags && ratchetsInboundTags < i2p::garlic::ECIESX25519_MIN_NUM_GENERATED_TAGS)
  61  						ratchetsInboundTags = i2p::garlic::ECIESX25519_MIN_NUM_GENERATED_TAGS;
  62  					SetNumRatchetInboundTags (ratchetsInboundTags);
  63  				}
  64  				explicitPeersStr = (*params)[I2CP_PARAM_EXPLICIT_PEERS];
  65  				trustedRoutersStr = (*params)[I2CP_PARAM_TRUSTED_ROUTERS];
  66  				m_Nickname = (*params)[I2CP_PARAM_INBOUND_NICKNAME];
  67  				if (m_Nickname.empty ()) // try outbound
  68  					m_Nickname = (*params)[I2CP_PARAM_OUTBOUND_NICKNAME];
  69  				// otherwise we set default nickname in Start when we know local address
  70  				params->Get (I2CP_PARAM_DONT_PUBLISH_LEASESET, m_IsPublic); // override isPublic
  71  				params->Get (I2CP_PARAM_LEASESET_TYPE, m_LeaseSetType);
  72  				if (m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
  73  				{
  74  					// authentication for encrypted LeaseSet
  75  					int authType = 0;
  76  					if (params->Get (I2CP_PARAM_LEASESET_AUTH_TYPE, authType))
  77  					{
  78  						if (authType >= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE && authType <= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK)
  79  							m_AuthType = authType;
  80  						else
  81  							LogPrint (eLogError, "Destination: Unknown auth type: ", authType);
  82  					}
  83  				}
  84  				auto leaseSetPrivKey = (*params)[I2CP_PARAM_LEASESET_PRIV_KEY];
  85  				if (!leaseSetPrivKey.empty ())
  86  				{
  87  					m_LeaseSetPrivKey.reset (new i2p::data::Tag<32>());
  88  					if (m_LeaseSetPrivKey->FromBase64 (leaseSetPrivKey) != 32)
  89  					{
  90  						LogPrint(eLogCritical, "Destination: Invalid value i2cp.leaseSetPrivKey: ", leaseSetPrivKey);
  91  						m_LeaseSetPrivKey.reset (nullptr);
  92  					}
  93  				}
  94  				int streamingProfile = 0;
  95  				if (params->Get (I2CP_PARAM_STREAMING_PROFILE, streamingProfile))
  96  					isHighBandwidth = streamingProfile != STREAMING_PROFILE_INTERACTIVE;
  97  			}
  98  		}
  99  		catch (std::exception & ex)
 100  		{
 101  			LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what());
 102  		}
 103  		SetNumTags (numTags);
 104  		m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar, isHighBandwidth);
 105  		if (!explicitPeersStr.empty ())
 106  			m_Pool->SetExplicitPeers (i2p::data::ExtractIdentHashes (explicitPeersStr));
 107  		if (!trustedRoutersStr.empty ())
 108  			m_Pool->SetTrustedRouters (i2p::data::ExtractIdentHashes (trustedRoutersStr));
 109  		if(params)
 110  		{
 111  			int maxLatency = 0;
 112  			if (params->Get (I2CP_PARAM_MAX_TUNNEL_LATENCY, maxLatency))
 113  			{
 114  				int minLatency = 0;
 115  				if (params->Get (I2CP_PARAM_MIN_TUNNEL_LATENCY, minLatency))
 116  				{
 117  					if (minLatency > 0 && maxLatency > 0)
 118  					{
 119  						// set tunnel pool latency
 120  						LogPrint(eLogInfo, "Destination: Requiring tunnel latency [", minLatency, "ms, ", maxLatency, "ms]");
 121  						m_Pool->RequireLatency(minLatency, maxLatency);
 122  					}
 123  				}
 124  			}
 125  		}
 126  	}
 127  
 128  	LeaseSetDestination::~LeaseSetDestination ()
 129  	{
 130  		if (m_Pool)
 131  			i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
 132  		for (auto& it: m_LeaseSetRequests)
 133  			it.second->Complete (nullptr);
 134  	}
 135  
 136  	void LeaseSetDestination::Start ()
 137  	{
 138  		if (m_Nickname.empty ())
 139  			m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname
 140  		LoadTags ();
 141  		m_Pool->SetLocalDestination (shared_from_this ());
 142  		m_Pool->SetActive (true);
 143  		m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT));
 144  		m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
 145  			shared_from_this (), std::placeholders::_1));
 146  	}
 147  
 148  	void LeaseSetDestination::Stop ()
 149  	{
 150  		m_CleanupTimer.cancel ();
 151  		m_PublishConfirmationTimer.cancel ();
 152  		m_PublishVerificationTimer.cancel ();
 153  		if (m_Pool)
 154  		{
 155  			m_Pool->SetLocalDestination (nullptr);
 156  			i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
 157  		}
 158  		SaveTags ();
 159  		CleanUp (); // GarlicDestination
 160  	}
 161  
 162  	bool LeaseSetDestination::Reconfigure(const i2p::util::Mapping& params)
 163  	{
 164  		params.Get (I2CP_PARAM_DONT_PUBLISH_LEASESET, m_IsPublic);
 165  
 166  		auto numTags = GetNumTags ();
 167  		params.Get (I2CP_PARAM_TAGS_TO_SEND, numTags);
 168  		auto numRatchetInboundTags = GetNumRatchetInboundTags ();
 169  		params.Get (I2CP_PARAM_RATCHET_INBOUND_TAGS, numRatchetInboundTags);
 170  		auto pool = GetTunnelPool();
 171  		auto inLen = pool->GetNumInboundHops();
 172  		params.Get (I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen);
 173  		auto outLen = pool->GetNumOutboundHops();
 174  		params.Get (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen);
 175  		auto inQuant = pool->GetNumInboundTunnels();
 176  		params.Get (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, inQuant);
 177  		auto outQuant = pool->GetNumOutboundTunnels();
 178  		params.Get (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, outQuant);
 179  		int minLatency = 0;
 180  		params.Get (I2CP_PARAM_MIN_TUNNEL_LATENCY, minLatency);
 181  		int maxLatency = 0;
 182  		params.Get (I2CP_PARAM_MAX_TUNNEL_LATENCY, maxLatency);
 183  
 184  		SetNumTags (numTags);
 185  		SetNumRatchetInboundTags (numRatchetInboundTags);
 186  		pool->RequireLatency(minLatency, maxLatency);
 187  		return pool->Reconfigure(inLen, outLen, inQuant, outQuant);
 188  	}
 189  
 190  	std::shared_ptr<i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
 191  	{
 192  		std::shared_ptr<i2p::data::LeaseSet> remoteLS;
 193  		{
 194  			std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
 195  			auto it = m_RemoteLeaseSets.find (ident);
 196  			if (it != m_RemoteLeaseSets.end ())
 197  				remoteLS = it->second;
 198  		}
 199  
 200  		if (remoteLS)
 201  		{
 202  			if (!remoteLS->IsExpired ())
 203  			{
 204  				if (remoteLS->ExpiresSoon())
 205  				{
 206  					LogPrint(eLogDebug, "Destination: Lease Set expires soon, updating before expire");
 207  					// update now before expiration for smooth handover
 208  					auto s = shared_from_this ();
 209  					RequestDestination(ident, [s, ident] (std::shared_ptr<i2p::data::LeaseSet> ls) {
 210  						if(ls && !ls->IsExpired())
 211  						{
 212  							ls->PopulateLeases();
 213  							{
 214  								std::lock_guard<std::mutex> _lock(s->m_RemoteLeaseSetsMutex);
 215  								s->m_RemoteLeaseSets[ident] = ls;
 216  							}
 217  						}
 218  					});
 219  				}
 220  				return remoteLS;
 221  			}
 222  			else
 223  			{
 224  				LogPrint (eLogWarning, "Destination: Remote LeaseSet expired");
 225  				std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
 226  				m_RemoteLeaseSets.erase (ident);
 227  				return nullptr;
 228  			}
 229  		}
 230  		return nullptr;
 231  	}
 232  
 233  	std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSet ()
 234  	{
 235  		if (!m_Pool) return nullptr;
 236  		if (!m_LeaseSet)
 237  			UpdateLeaseSet ();
 238  		auto ls = GetLeaseSetMt ();
 239  		return (ls && ls->GetInnerLeaseSet ()) ? ls->GetInnerLeaseSet () : ls; // always non-encrypted
 240  	}
 241  
 242  	std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSetMt ()
 243  	{
 244  		std::lock_guard<std::mutex> l(m_LeaseSetMutex);
 245  		return m_LeaseSet;
 246  	}
 247  
 248  	void LeaseSetDestination::SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet)
 249  	{
 250  		{
 251  			std::lock_guard<std::mutex> l(m_LeaseSetMutex);
 252  			m_LeaseSet = newLeaseSet;
 253  		}
 254  		i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
 255  		if (m_IsPublic)
 256  		{
 257  			auto s = shared_from_this ();
 258  			boost::asio::post (m_Service, [s](void)
 259  			{
 260  				s->m_PublishVerificationTimer.cancel ();
 261  				s->Publish ();
 262  			});
 263  		}
 264  	}
 265  
 266  	void LeaseSetDestination::UpdateLeaseSet ()
 267  	{
 268  		int numTunnels = m_Pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
 269  		if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum
 270  		auto tunnels = m_Pool->GetInboundTunnels (numTunnels);
 271  		if (!tunnels.empty ())
 272  			CreateNewLeaseSet (tunnels);
 273  		else
 274  			LogPrint (eLogInfo, "Destination: No inbound tunnels for LeaseSet");
 275  	}
 276  
 277  	bool LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
 278  	{
 279  		struct
 280  		{
 281  			uint8_t k[32], t[32];
 282  		} data;
 283  		memcpy (data.k, key, 32);
 284  		memcpy (data.t, tag, 32);
 285  		auto s = shared_from_this ();
 286  		boost::asio::post (m_Service, [s,data](void)
 287  			{
 288  				s->AddSessionKey (data.k, data.t);
 289  			});
 290  		return true;
 291  	}
 292  
 293  	void LeaseSetDestination::SubmitECIESx25519Key (const uint8_t * key, uint64_t tag)
 294  	{
 295  		struct
 296  		{
 297  			uint8_t k[32];
 298  			uint64_t t;
 299  		} data;
 300  		memcpy (data.k, key, 32);
 301  		data.t = tag;
 302  		auto s = shared_from_this ();
 303  		boost::asio::post (m_Service, [s,data](void)
 304  			{
 305  				s->AddECIESx25519Key (data.k, data.t);
 306  			});
 307  	}
 308  
 309  	void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
 310  	{
 311  		if (!msg) return;
 312  		bool empty = false;
 313  		{
 314  			std::lock_guard<std::mutex> l(m_IncomingMsgsQueueMutex);
 315  			empty = m_IncomingMsgsQueue.empty ();
 316  			m_IncomingMsgsQueue.push_back (msg);
 317  		}
 318  		if (empty)
 319  			boost::asio::post (m_Service, [s = shared_from_this ()]()
 320  			{
 321  				std::list<std::shared_ptr<I2NPMessage> > receivedMsgs;
 322  				{
 323  					std::lock_guard<std::mutex> l(s->m_IncomingMsgsQueueMutex);
 324  					s->m_IncomingMsgsQueue.swap (receivedMsgs);
 325  				}
 326  			    for (auto& it: receivedMsgs)
 327  			    	s->HandleGarlicMessage (it);
 328  			});
 329  	}
 330  
 331  	void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
 332  	{
 333  		uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
 334  		boost::asio::post (m_Service, std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID));
 335  	}
 336  
 337  	void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len)
 338  	{
 339  		I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]);
 340  		uint32_t msgID = bufbe32toh (buf + I2NP_HEADER_MSGID_OFFSET);
 341  		LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE,
 342  			GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID, nullptr);
 343  	}
 344  
 345  	bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
 346  		size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from)
 347  	{
 348  		switch (typeID)
 349  		{
 350  			case eI2NPData:
 351  				HandleDataMessage (payload, len, from);
 352  			break;
 353  			case eI2NPDeliveryStatus:
 354  				HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET));
 355  			break;
 356  			case eI2NPTunnelTest:
 357  				if (m_Pool)
 358  					m_Pool->ProcessTunnelTest (bufbe32toh (payload + TUNNEL_TEST_MSGID_OFFSET), bufbe64toh (payload + TUNNEL_TEST_TIMESTAMP_OFFSET));
 359  			break;
 360  			case eI2NPDatabaseStore:
 361  				HandleDatabaseStoreMessage (payload, len, from);
 362  			break;
 363  			case eI2NPDatabaseSearchReply:
 364  				HandleDatabaseSearchReplyMessage (payload, len);
 365  			break;
 366  			case eI2NPShortTunnelBuildReply: // might come as garlic encrypted
 367  				i2p::HandleI2NPMessage (CreateI2NPMessage (typeID, payload, len, msgID));
 368  			break;
 369  			default:
 370  				LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID);
 371  				return false;
 372  		}
 373  		return true;
 374  	}
 375  
 376  	void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len,
 377  		i2p::garlic::ECIESX25519AEADRatchetSession * from)
 378  	{
 379  		if (len < DATABASE_STORE_HEADER_SIZE)
 380  		{
 381  			LogPrint (eLogError, "Destination: Database store msg is too short ", len);
 382  			return;
 383  		}
 384  		uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
 385  		size_t offset = DATABASE_STORE_HEADER_SIZE;
 386  		if (replyToken)
 387  		{
 388  			LogPrint (eLogInfo, "Destination: Reply token is ignored for DatabaseStore");
 389  			offset += 36;
 390  		}
 391  		if (offset > len || len > i2p::data::MAX_LS_BUFFER_SIZE + offset)
 392  		{
 393  			LogPrint (eLogError, "Destination: Database store message is too long ", len);
 394  			return;
 395  		}
 396  		i2p::data::IdentHash key (buf + DATABASE_STORE_KEY_OFFSET);
 397  		std::shared_ptr<i2p::data::LeaseSet> leaseSet;
 398  		std::shared_ptr<LeaseSetRequest> request;
 399  		switch (buf[DATABASE_STORE_TYPE_OFFSET])
 400  		{
 401  			case i2p::data::NETDB_STORE_TYPE_LEASESET: // 1
 402  			case i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2: // 3
 403  			{
 404  				LogPrint (eLogDebug, "Destination: Remote LeaseSet");
 405  				std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
 406  				auto it = m_RemoteLeaseSets.find (key);
 407  				if (it != m_RemoteLeaseSets.end () &&
 408  					it->second->GetStoreType () == buf[DATABASE_STORE_TYPE_OFFSET]) // update only if same type
 409  				{
 410  					leaseSet = it->second;
 411  					if (leaseSet->IsNewer (buf + offset, len - offset))
 412  					{
 413  						leaseSet->Update (buf + offset, len - offset, shared_from_this(), true);
 414  						if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
 415  							LogPrint (eLogDebug, "Destination: Remote LeaseSet updated");
 416  						else
 417  						{
 418  							LogPrint (eLogDebug, "Destination: Remote LeaseSet update failed");
 419  							m_RemoteLeaseSets.erase (it);
 420  							leaseSet = nullptr;
 421  						}
 422  					}
 423  					else
 424  						LogPrint (eLogDebug, "Destination: Remote LeaseSet is older. Not updated");
 425  				}
 426  				else
 427  				{
 428  					// add or replace
 429  					if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET)
 430  						leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet
 431  					else
 432  					{
 433  						leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET],
 434  							buf + offset, len - offset, true, shared_from_this (),
 435  						    from ? from->GetRemoteStaticKeyType () : GetPreferredCryptoType () ); // LeaseSet2
 436  						if (from)
 437  						{
 438  							uint8_t pub[32];
 439  							leaseSet->Encrypt (nullptr, pub);
 440  							if (!memcmp (from->GetRemoteStaticKey (), pub, 32))
 441  								from->SetDestination (leaseSet->GetIdentHash ());
 442  							else
 443  							{
 444  								LogPrint (eLogError, "Destination: Remote LeaseSet static key mismatch");
 445  								leaseSet = nullptr;
 446  							}
 447  						}
 448  					}
 449  					if (leaseSet && leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
 450  					{
 451  						if (leaseSet->GetIdentHash () != GetIdentHash ())
 452  						{
 453  							LogPrint (eLogDebug, "Destination: New remote LeaseSet added");
 454  							m_RemoteLeaseSets.insert_or_assign (key, leaseSet);
 455  							if (from)
 456  								from->SetDestination (key);
 457  						}
 458  						else
 459  							LogPrint (eLogDebug, "Destination: Own remote LeaseSet dropped");
 460  					}
 461  					else
 462  					{
 463  						LogPrint (eLogError, "Destination: New remote LeaseSet failed");
 464  						leaseSet = nullptr;
 465  					}
 466  				}
 467  				break;
 468  			}
 469  			case i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2: // 5
 470  			{
 471  				auto it2 = m_LeaseSetRequests.find (key);
 472  				if (it2 != m_LeaseSetRequests.end ())
 473  				{
 474  					request = it2->second;
 475  					m_LeaseSetRequests.erase (it2);
 476  					if (request->requestedBlindedKey)
 477  					{
 478  						auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset,
 479  							request->requestedBlindedKey, shared_from_this (),
 480  						    m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr, GetPreferredCryptoType ());
 481  						if (ls2->IsValid () && !ls2->IsExpired ())
 482  						{
 483  							leaseSet = ls2;
 484  							std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
 485  							m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key
 486  							m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup
 487  						}
 488  						else
 489  							LogPrint (eLogError, "Destination: New remote encrypted LeaseSet2 failed");
 490  					}
 491  					else
 492  					{
 493  						// publishing verification doesn't have requestedBlindedKey
 494  						auto localLeaseSet = GetLeaseSetMt ();
 495  						if (localLeaseSet->GetStoreHash () == key)
 496  						{
 497  							auto ls = std::make_shared<i2p::data::LeaseSet2> (i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2,
 498  								localLeaseSet->GetBuffer (), localLeaseSet->GetBufferLen (), false);
 499  							leaseSet = ls;
 500  						}
 501  						else
 502  							LogPrint (eLogWarning, "Destination: Encrypted LeaseSet2 received for request without blinded key");
 503  					}
 504  				}
 505  				else
 506  					LogPrint (eLogWarning, "Destination: Couldn't find request for encrypted LeaseSet2");
 507  				break;
 508  			}
 509  			default:
 510  				LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped");
 511  		}
 512  
 513  		if (!request)
 514  		{
 515  			auto it1 = m_LeaseSetRequests.find (key);
 516  			if (it1 != m_LeaseSetRequests.end ())
 517  			{
 518  				request = it1->second;
 519  				m_LeaseSetRequests.erase (it1);
 520  			}
 521  		}
 522  		if (request)
 523  		{
 524  			request->requestTimeoutTimer.cancel ();
 525  			request->Complete (leaseSet);
 526  		}
 527  	}
 528  
 529  	void LeaseSetDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
 530  	{
 531  		i2p::data::IdentHash key (buf);
 532  		int num = buf[32]; // num
 533  		LogPrint (eLogDebug, "Destination: DatabaseSearchReply for ", key.ToBase64 (), " num=", num);
 534  		auto it = m_LeaseSetRequests.find (key);
 535  		if (it != m_LeaseSetRequests.end ())
 536  		{
 537  			auto request = it->second;
 538  			for (int i = 0; i < num; i++)
 539  			{
 540  				i2p::data::IdentHash peerHash (buf + 33 + i*32);
 541  				if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash))
 542  				{
 543  					LogPrint (eLogInfo, "Destination: Found new floodfill, request it");
 544  					i2p::data::netdb.RequestDestination (peerHash, nullptr, false); // through exploratory
 545  				}
 546  			}
 547  			SendNextLeaseSetRequest (key, request);
 548  		}
 549  		else
 550  			LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
 551  	}
 552  
 553  	void LeaseSetDestination::SendNextLeaseSetRequest (const i2p::data::IdentHash& key,
 554  		std::shared_ptr<LeaseSetRequest> request)
 555  	{
 556  		bool found = false;
 557  		if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST)
 558  		{
 559  			auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded);
 560  			if (floodfill)
 561  			{
 562  				LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 ());
 563  				if (SendLeaseSetRequest (key, floodfill, request))
 564  					found = true;
 565  			}
 566  		}
 567  		if (!found)
 568  		{
 569  			LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
 570  			request->Complete (nullptr);
 571  			m_LeaseSetRequests.erase (key);
 572  		}
 573  	}
 574  
 575  	void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID)
 576  	{
 577  		if (msgID == m_PublishReplyToken)
 578  		{
 579  			LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32());
 580  			m_ExcludedFloodfills.clear ();
 581  			m_PublishReplyToken = 0;
 582  			// schedule verification
 583  			m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT +
 584  				GetRng ()() % PUBLISH_VERIFICATION_TIMEOUT_VARIANCE));
 585  			m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
 586  			shared_from_this (), std::placeholders::_1));
 587  		}
 588  		else
 589  			i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
 590  	}
 591  
 592  	void LeaseSetDestination::SetLeaseSetUpdated (bool post)
 593  	{
 594  		if (post)
 595  			boost::asio::post (m_Service, [s = shared_from_this ()]() { s->UpdateLeaseSet (); });
 596  		else
 597  			UpdateLeaseSet ();
 598  	}
 599  
 600  	void LeaseSetDestination::Publish ()
 601  	{
 602  		auto leaseSet = GetLeaseSetMt ();
 603  		if (!leaseSet || !m_Pool)
 604  		{
 605  			LogPrint (eLogError, "Destination: Can't publish non-existing LeaseSet");
 606  			return;
 607  		}
 608  		if (m_PublishReplyToken)
 609  		{
 610  			LogPrint (eLogDebug, "Destination: Publishing LeaseSet is pending");
 611  			return;
 612  		}
 613  		auto ts = i2p::util::GetSecondsSinceEpoch ();
 614  		if (ts < m_LastSubmissionTime + PUBLISH_MIN_INTERVAL)
 615  		{
 616  			LogPrint (eLogDebug, "Destination: Publishing LeaseSet is too fast. Wait for ", PUBLISH_MIN_INTERVAL, " seconds");
 617  			m_PublishDelayTimer.cancel ();
 618  			m_PublishDelayTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_MIN_INTERVAL));
 619  			m_PublishDelayTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishDelayTimer,
 620  				shared_from_this (), std::placeholders::_1));
 621  			return;
 622  		}
 623  		auto floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetStoreHash (), m_ExcludedFloodfills);
 624  		if (!floodfill)
 625  		{
 626  			LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found");
 627  			m_ExcludedFloodfills.clear ();
 628  			return;
 629  		}
 630  		auto outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false));
 631  		auto inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true));
 632  		if (!outbound || !inbound)
 633  		{
 634  			if (!m_Pool->GetInboundTunnels ().empty () && !m_Pool->GetOutboundTunnels ().empty ())
 635  			{
 636  				LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill");
 637  				m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
 638  				floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetStoreHash (), m_ExcludedFloodfills);
 639  				if (floodfill)
 640  				{
 641  					outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false));
 642  					if (outbound)
 643  					{
 644  						inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true));
 645  						if (!inbound)
 646  							LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels");
 647  					}
 648  					else
 649  						LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels");
 650  				}
 651  				else
 652  					LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found");
 653  			}
 654  			else
 655  				LogPrint (eLogDebug, "Destination: No tunnels in pool");
 656  
 657  			if (!floodfill || !outbound || !inbound)
 658  			{
 659  				// we can't publish now
 660  				m_ExcludedFloodfills.clear ();
 661  				m_PublishReplyToken = 1; // dummy non-zero value
 662  				// try again after a while
 663  				LogPrint (eLogInfo, "Destination: Can't publish LeasetSet because destination is not ready. Try publishing again after ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds");
 664  				m_PublishConfirmationTimer.expires_from_now (boost::posix_time::milliseconds(PUBLISH_CONFIRMATION_TIMEOUT));
 665  				m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
 666  					shared_from_this (), std::placeholders::_1));
 667  				return;
 668  			}
 669  		}
 670  		m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
 671  		LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
 672  		RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
 673  		auto msg = WrapMessageForRouter (floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound));
 674  		auto s = shared_from_this ();
 675  		msg->onDrop = [s]()
 676  			{
 677  				boost::asio::post (s->GetService (), [s]()
 678  					{
 679  						s->m_PublishConfirmationTimer.cancel ();
 680  						s->HandlePublishConfirmationTimer (boost::system::error_code());
 681  					});
 682  			};
 683  		m_PublishConfirmationTimer.expires_from_now (boost::posix_time::milliseconds(PUBLISH_CONFIRMATION_TIMEOUT));
 684  		m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
 685  			shared_from_this (), std::placeholders::_1));
 686  		outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, msg);
 687  		m_LastSubmissionTime = ts;
 688  	}
 689  
 690  	void LeaseSetDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
 691  	{
 692  		if (ecode != boost::asio::error::operation_aborted)
 693  		{
 694  			if (m_PublishReplyToken)
 695  			{
 696  				LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds or failed. will try again");
 697  				m_PublishReplyToken = 0;
 698  				Publish ();
 699  			}
 700  		}
 701  	}
 702  
 703  	void LeaseSetDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode)
 704  	{
 705  		if (ecode != boost::asio::error::operation_aborted)
 706  		{
 707  			auto ls = GetLeaseSetMt ();
 708  			if (!ls)
 709  			{
 710  				LogPrint (eLogWarning, "Destination: Couldn't verify LeaseSet for ", GetIdentHash().ToBase32());
 711  				return;
 712  			}
 713  			auto s = shared_from_this ();
 714  			RequestLeaseSet (ls->GetStoreHash (),
 715  				[s, ls](std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
 716  				{
 717  					if (leaseSet)
 718  					{
 719  						if (*ls == *leaseSet)
 720  						{
 721  							// we got latest LeasetSet
 722  							LogPrint (eLogDebug, "Destination: Published LeaseSet verified for ", s->GetIdentHash().ToBase32());
 723  							s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
 724  							s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
 725  							return;
 726  						}
 727  						else
 728  							LogPrint (eLogDebug, "Destination: LeaseSet is different than just published for ", s->GetIdentHash().ToBase32());
 729  					}
 730  					else
 731  						LogPrint (eLogWarning, "Destination: Couldn't find published LeaseSet for ", s->GetIdentHash().ToBase32());
 732  					// we have to publish again
 733  					s->Publish ();
 734  				});
 735  		}
 736  	}
 737  
 738  	void LeaseSetDestination::HandlePublishDelayTimer (const boost::system::error_code& ecode)
 739  	{
 740  		if (ecode != boost::asio::error::operation_aborted)
 741  			Publish ();
 742  	}
 743  
 744  	bool LeaseSetDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
 745  	{
 746  		if (!m_Pool || !IsReady ())
 747  		{
 748  			if (requestComplete)
 749  				boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);});
 750  			return false;
 751  		}
 752  		boost::asio::post (m_Service, std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete, nullptr));
 753  		return true;
 754  	}
 755  
 756  	bool LeaseSetDestination::RequestDestinationWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, RequestComplete requestComplete)
 757  	{
 758  		if (!dest || !m_Pool || !IsReady ())
 759  		{
 760  			if (requestComplete)
 761  				boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);});
 762  			return false;
 763  		}
 764  		auto storeHash = dest->GetStoreHash ();
 765  		auto leaseSet = FindLeaseSet (storeHash);
 766  		if (leaseSet)
 767  		{
 768  			if (requestComplete)
 769  				boost::asio::post (m_Service, [requestComplete, leaseSet](void){requestComplete (leaseSet);});
 770  			return true;
 771  		}
 772  		boost::asio::post (m_Service, std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest));
 773  		return true;
 774  	}
 775  
 776  	void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify)
 777  	{
 778  		auto s = shared_from_this ();
 779  		boost::asio::post (m_Service, [dest, notify, s](void)
 780  			{
 781  				auto it = s->m_LeaseSetRequests.find (dest);
 782  				if (it != s->m_LeaseSetRequests.end ())
 783  				{
 784  					auto requestComplete = it->second;
 785  					s->m_LeaseSetRequests.erase (it);
 786  					if (notify && requestComplete) requestComplete->Complete (nullptr);
 787  				}
 788  			});
 789  	}
 790  
 791  	void LeaseSetDestination::CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify)
 792  	{
 793  		if (dest)
 794  			CancelDestinationRequest (dest->GetStoreHash (), notify);
 795  	}
 796  
 797  	void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey)
 798  	{
 799  		std::unordered_set<i2p::data::IdentHash> excluded;
 800  		auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
 801  		if (floodfill)
 802  		{
 803  			auto request = std::make_shared<LeaseSetRequest> (m_Service);
 804  			request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2
 805  			if (requestComplete)
 806  				request->requestComplete.push_back (requestComplete);
 807  			auto ts = i2p::util::GetMillisecondsSinceEpoch ();
 808  			auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
 809  			if (ret.second) // inserted
 810  			{
 811  				request->requestTime = ts;
 812  				if (!SendLeaseSetRequest (dest, floodfill, request))
 813  				{
 814  					// try another
 815  					LogPrint (eLogWarning, "Destination: Couldn't send LeaseSet request to ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another");
 816  					request->excluded.insert (floodfill->GetIdentHash ());
 817  					floodfill = i2p::data::netdb.GetClosestFloodfill (dest, request->excluded);
 818  					if (!SendLeaseSetRequest (dest, floodfill, request))
 819  					{
 820  						// request failed
 821  						LogPrint (eLogWarning, "Destination: LeaseSet request for ", dest.ToBase32 (), " was not sent");
 822  						m_LeaseSetRequests.erase (ret.first);
 823  						if (requestComplete) requestComplete (nullptr);
 824  					}
 825  				}
 826  			}
 827  			else // duplicate
 828  			{
 829  				LogPrint (eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
 830  				if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
 831  				{
 832  					// something went wrong
 833  					m_LeaseSetRequests.erase (ret.first);
 834  					if (requestComplete) requestComplete (nullptr);
 835  				}
 836  				else if (requestComplete)
 837  					ret.first->second->requestComplete.push_back (requestComplete);
 838  			}
 839  		}
 840  		else
 841  		{
 842  			LogPrint (eLogError, "Destination: Can't request LeaseSet, no floodfills found");
 843  			if (requestComplete) requestComplete (nullptr);
 844  		}
 845  	}
 846  
 847  	bool LeaseSetDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
 848  		std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request)
 849  	{
 850  		if (!request->replyTunnel || !request->replyTunnel->IsEstablished ())
 851  			request->replyTunnel = m_Pool->GetNextInboundTunnel (nullptr, nextFloodfill->GetCompatibleTransports (false)); // outbound from floodfill
 852  		if (!request->replyTunnel) LogPrint (eLogWarning, "Destination: Can't send LeaseSet request, no compatible inbound tunnels found");
 853  		if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ())
 854  			request->outboundTunnel = m_Pool->GetNextOutboundTunnel (nullptr, nextFloodfill->GetCompatibleTransports (true)); // inbound from floodfill
 855  		if (!request->outboundTunnel) LogPrint (eLogWarning, "Destination: Can't send LeaseSet request, no compatible outbound tunnels found");
 856  
 857  		if (request->replyTunnel && request->outboundTunnel)
 858  		{
 859  			request->excluded.insert (nextFloodfill->GetIdentHash ());
 860  			request->requestTimeoutTimer.cancel ();
 861  
 862  			bool isECIES = SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) &&
 863  				nextFloodfill->GetVersion () >= MAKE_VERSION_NUMBER(0, 9, 46); // >= 0.9.46;
 864  			uint8_t replyKey[32], replyTag[32];
 865  			RAND_bytes (replyKey, 32); // random session key
 866  			RAND_bytes (replyTag, isECIES ? 8 : 32); // random session tag
 867  			if (isECIES)
 868  				AddECIESx25519Key (replyKey, replyTag);
 869  			else
 870  				AddSessionKey (replyKey, replyTag);
 871  
 872  			auto msg = WrapMessageForRouter (nextFloodfill,
 873  				CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES));
 874  			auto s = shared_from_this ();
 875  			msg->onDrop = [s, dest, request]()
 876  				{
 877  					boost::asio::post (s->GetService (), [s, dest, request]()
 878  						{
 879  							s->SendNextLeaseSetRequest (dest, request);
 880  						});
 881  				};
 882  			request->outboundTunnel->SendTunnelDataMsgs (
 883  				{
 884  					i2p::tunnel::TunnelMessageBlock
 885  					{
 886  						i2p::tunnel::eDeliveryTypeRouter,
 887  						nextFloodfill->GetIdentHash (), 0, msg
 888  					}
 889  				});
 890  			request->requestTimeoutTimer.expires_from_now (boost::posix_time::milliseconds(LEASESET_REQUEST_TIMEOUT));
 891  			request->requestTimeoutTimer.async_wait (std::bind (&LeaseSetDestination::HandleRequestTimoutTimer,
 892  				shared_from_this (), std::placeholders::_1, dest));
 893  		}
 894  		else
 895  			return false;
 896  		return true;
 897  	}
 898  
 899  	void LeaseSetDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest)
 900  	{
 901  		if (ecode != boost::asio::error::operation_aborted)
 902  		{
 903  			auto it = m_LeaseSetRequests.find (dest);
 904  			if (it != m_LeaseSetRequests.end ())
 905  			{
 906  				bool done = false;
 907  				uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
 908  				if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
 909  				{
 910  					auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
 911  					if (floodfill)
 912  					{
 913  						// reset tunnels, because one them might fail
 914  						it->second->outboundTunnel = nullptr;
 915  						it->second->replyTunnel = nullptr;
 916  						done = !SendLeaseSetRequest (dest, floodfill, it->second);
 917  					}
 918  					else
 919  						done = true;
 920  				}
 921  				else
 922  				{
 923  					LogPrint (eLogWarning, "Destination: ", dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds");
 924  					done = true;
 925  				}
 926  
 927  				if (done)
 928  				{
 929  					auto requestComplete = it->second;
 930  					m_LeaseSetRequests.erase (it);
 931  					if (requestComplete) requestComplete->Complete (nullptr);
 932  				}
 933  			}
 934  		}
 935  	}
 936  
 937  	void LeaseSetDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
 938  	{
 939  		if (ecode != boost::asio::error::operation_aborted)
 940  		{
 941  			CleanupExpiredTags ();
 942  			CleanupRemoteLeaseSets ();
 943  			CleanupDestination ();
 944  			m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT +
 945  				GetRng ()() % DESTINATION_CLEANUP_TIMEOUT_VARIANCE));
 946  			m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
 947  				shared_from_this (), std::placeholders::_1));
 948  		}
 949  	}
 950  
 951  	void LeaseSetDestination::CleanupRemoteLeaseSets ()
 952  	{
 953  		auto ts = i2p::util::GetMillisecondsSinceEpoch ();
 954  		std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
 955  		for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
 956  		{
 957  			if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
 958  			{
 959  				LogPrint (eLogDebug, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
 960  				it = m_RemoteLeaseSets.erase (it);
 961  			}
 962  			else
 963  				++it;
 964  		}
 965  	}
 966  
 967  	ClientDestination::ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys,
 968  		bool isPublic, const i2p::util::Mapping * params):
 969  		LeaseSetDestination (service, isPublic, params),
 970  		m_Keys (keys), m_PreferredCryptoType (0), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
 971  		m_StreamingOutboundSpeed (DEFAULT_MAX_OUTBOUND_SPEED),
 972  		m_StreamingInboundSpeed (DEFAULT_MAX_INBOUND_SPEED),
 973  		m_StreamingMaxConcurrentStreams (DEFAULT_MAX_CONCURRENT_STREAMS),
 974  		m_StreamingMaxConnsPerMinute (DEFAULT_MAX_CONNS_PER_MINUTE),
 975  		m_StreamingMaxWindowSize (i2p::stream::MAX_WINDOW_SIZE),
 976  		m_StreamingMaxResends (i2p::stream::MAX_NUM_RESEND_ATTEMPTS),
 977  		m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_IsStreamingDontSign (DEFAULT_DONT_SIGN),
 978  		m_LastPort (0), m_DatagramDestination (nullptr), m_RefCounter (0),
 979  		m_LastPublishedTimestamp (0), m_ReadyChecker(service)
 980  	{
 981  		if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
 982  			SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only
 983  
 984  		// extract encryption type params for LS2
 985  		std::set<i2p::data::CryptoKeyType> encryptionKeyTypes;
 986  		if (params)
 987  		{
 988  			auto encryptionTypesStr = (*params)[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE];
 989  			if (!encryptionTypesStr.empty ())
 990  			{
 991  				// comma-separated values
 992  				std::vector<std::string> values;
 993  				boost::split(values, encryptionTypesStr, boost::is_any_of(","));
 994  				for (auto& it1: values)
 995  				{
 996  					try
 997  					{
 998  						i2p::data::CryptoKeyType cryptoType = std::stoi(it1);
 999  #if !OPENSSL_PQ
1000  						if (cryptoType <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // skip PQ keys if not supported
1001  #endif
1002  						{
1003  							if (!m_PreferredCryptoType && cryptoType)
1004  								m_PreferredCryptoType = cryptoType; // first non-zero in the list
1005  							encryptionKeyTypes.insert (cryptoType);
1006  						}
1007  					}
1008  					catch (std::exception& ex)
1009  					{
1010  						LogPrint (eLogInfo, "Destination: Unexpected crypto type ", it1, ". ", ex.what ());
1011  						continue;
1012  					}
1013  				}
1014  			}
1015  		}
1016  		// if no encryption type specified use 0,4 or 0,4,6 if post quantum
1017  		if (encryptionKeyTypes.empty ())
1018  		{
1019  			encryptionKeyTypes.insert ( { GetIdentity ()->GetCryptoKeyType (),
1020  #if OPENSSL_PQ
1021  				i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD,
1022  #endif
1023  				i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD });
1024  #if OPENSSL_PQ
1025  			m_PreferredCryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD;
1026  #else
1027  			m_PreferredCryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
1028  #endif
1029  		}
1030  
1031  		for (auto& it: encryptionKeyTypes)
1032  		{
1033  			auto encryptionKey = std::make_shared<i2p::crypto::LocalEncryptionKey> (it);
1034  			if (IsPublic ())
1035  				PersistTemporaryKeys (encryptionKey);
1036  			else
1037  				encryptionKey->GenerateKeys ();
1038  			encryptionKey->CreateDecryptor ();
1039  			if (it > i2p::data::CRYPTO_KEY_TYPE_ELGAMAL && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
1040  				SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Only DSA can use LeaseSet1
1041  			m_EncryptionKeys.emplace (it, encryptionKey);
1042  		}
1043  
1044  		if (IsPublic ())
1045  			LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
1046  
1047  		try
1048  		{
1049  			if (params)
1050  			{
1051  				// extract streaming params
1052  				params->Get (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, m_StreamingAckDelay);
1053  				params->Get (I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED, m_StreamingOutboundSpeed);
1054  				params->Get (I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED, m_StreamingInboundSpeed);
1055  				params->Get (I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS, m_StreamingMaxConcurrentStreams);
1056  				params->Get (I2CP_PARAM_STREAMING_MAX_CONNS_PER_MINUTE, m_StreamingMaxConnsPerMinute);
1057  				if (params->Get (I2CP_PARAM_STREAMING_MAX_WINDOW_SIZE, m_StreamingMaxWindowSize) &&
1058  					(m_StreamingMaxWindowSize < i2p::stream::MIN_WINDOW_SIZE))
1059  						m_StreamingMaxWindowSize = i2p::stream::MIN_WINDOW_SIZE;
1060  				params->Get (I2CP_PARAM_STREAMING_ANSWER_PINGS, m_IsStreamingAnswerPings);
1061  				params->Get (I2CP_PARAM_STREAMING_DONT_SIGN, m_IsStreamingDontSign);
1062                  params->Get (I2CP_PARAM_STREAMING_MAX_RESENDS, m_StreamingMaxResends);
1063  
1064  				if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
1065  				{
1066  					// authentication for encrypted LeaseSet
1067  					auto authType = GetAuthType ();
1068  					if (authType > 0)
1069  					{
1070  						m_AuthKeys = std::make_shared<std::vector<i2p::data::AuthPublicKey> >();
1071  						if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_DH)
1072  							ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_DH, params);
1073  						else if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK)
1074  							ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_PSK, params);
1075  						else
1076  							LogPrint (eLogError, "Destination: Unexpected auth type: ", authType);
1077  						if (m_AuthKeys->size ())
1078  							LogPrint (eLogInfo, "Destination: ", m_AuthKeys->size (), " auth keys read");
1079  						else
1080  						{
1081  							LogPrint (eLogCritical, "Destination: No auth keys read for auth type: ", authType);
1082  							m_AuthKeys = nullptr;
1083  						}
1084  					}
1085  				}
1086  			}
1087  		}
1088  		catch (std::exception & ex)
1089  		{
1090  			LogPrint(eLogCritical, "Destination: Unable to parse parameters for destination: ", ex.what());
1091  		}
1092  	}
1093  
1094  	ClientDestination::~ClientDestination ()
1095  	{
1096  	}
1097  
1098  	void ClientDestination::Start ()
1099  	{
1100  		LeaseSetDestination::Start ();
1101  		m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO:
1102  		m_StreamingDestination->Start ();
1103  		for (auto& it: m_StreamingDestinationsByPorts)
1104  			it.second->Start ();
1105  	}
1106  
1107  	void ClientDestination::Stop ()
1108  	{
1109  		LogPrint(eLogDebug, "Destination: Stopping destination ", GetIdentHash().ToBase32(), ".b32.i2p");
1110  		m_ReadyChecker.cancel();
1111  		LogPrint(eLogDebug, "Destination: -> Stopping Streaming Destination");
1112  		m_StreamingDestination->Stop ();
1113  		//m_StreamingDestination->SetOwner (nullptr);
1114  		m_StreamingDestination = nullptr;
1115  
1116  		LogPrint(eLogDebug, "Destination: -> Stopping Streaming Destination by ports");
1117  		for (auto& it: m_StreamingDestinationsByPorts)
1118  		{
1119  			it.second->Stop ();
1120  			//it.second->SetOwner (nullptr);
1121  		}
1122  		m_StreamingDestinationsByPorts.clear ();
1123  		m_LastStreamingDestination = nullptr;
1124  
1125  		if (m_DatagramDestination)
1126  		{
1127  			LogPrint(eLogDebug, "Destination: -> Stopping Datagram Destination");
1128  			delete m_DatagramDestination;
1129  			m_DatagramDestination = nullptr;
1130  		}
1131  		LeaseSetDestination::Stop ();
1132  		LogPrint(eLogDebug, "Destination: -> Stopping done");
1133  	}
1134  
1135  	void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len,
1136  		i2p::garlic::ECIESX25519AEADRatchetSession * from)
1137  	{
1138  		uint32_t length = bufbe32toh (buf);
1139  		if(length > len - 4)
1140  		{
1141  			LogPrint(eLogError, "Destination: Data message length ", length, " exceeds buffer length ", len);
1142  			return;
1143  		}
1144  		buf += 4;
1145  		// we assume I2CP payload
1146  		uint16_t fromPort = bufbe16toh (buf + 4), // source
1147  			toPort = bufbe16toh (buf + 6); // destination
1148  		switch (buf[9])
1149  		{
1150  			case PROTOCOL_TYPE_STREAMING:
1151  			{
1152  				// streaming protocol
1153  				if (toPort != m_LastPort || !m_LastStreamingDestination)
1154  				{
1155  					m_LastStreamingDestination = GetStreamingDestination (toPort);
1156  					if (!m_LastStreamingDestination)
1157  						m_LastStreamingDestination = m_StreamingDestination; // if no destination on port use default
1158  					m_LastPort = toPort;
1159  				}
1160  				if (m_LastStreamingDestination)
1161  					m_LastStreamingDestination->HandleDataMessagePayload (buf, length, from);
1162  				else
1163  					LogPrint (eLogError, "Destination: Missing streaming destination");
1164  			}
1165  			break;
1166  			case PROTOCOL_TYPE_DATAGRAM:
1167  			case PROTOCOL_TYPE_RAW:
1168  			case PROTOCOL_TYPE_DATAGRAM2:
1169  			case PROTOCOL_TYPE_DATAGRAM3:
1170  				// datagram protocol
1171  				if (m_DatagramDestination)
1172  					m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length, buf[9], from);
1173  				else
1174  					LogPrint (eLogError, "Destination: Missing datagram destination");
1175  			break;
1176  			default:
1177  				LogPrint (eLogError, "Destination: Data: Unexpected protocol ", buf[9]);
1178  		}
1179  	}
1180  
1181  	void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, uint16_t port)
1182  	{
1183  		if (!streamRequestComplete)
1184  		{
1185  			LogPrint (eLogError, "Destination: Request callback is not specified in CreateStream");
1186  			return;
1187  		}
1188  		auto leaseSet = FindLeaseSet (dest);
1189  		if (leaseSet)
1190  		{
1191  			auto stream = CreateStream (leaseSet, port);
1192  			boost::asio::post (GetService (), [streamRequestComplete, stream]()
1193  				{
1194  					streamRequestComplete(stream);
1195  				});
1196  		}
1197  		else
1198  		{
1199  			auto s = GetSharedFromThis ();
1200  			RequestDestination (dest,
1201  				[s, streamRequestComplete, port](std::shared_ptr<const i2p::data::LeaseSet> ls)
1202  				{
1203  					if (ls)
1204  						streamRequestComplete(s->CreateStream (ls, port));
1205  					else
1206  						streamRequestComplete (nullptr);
1207  				});
1208  		}
1209  	}
1210  
1211  	void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, uint16_t port)
1212  	{
1213  		if (!streamRequestComplete)
1214  		{
1215  			LogPrint (eLogError, "Destination: Request callback is not specified in CreateStream");
1216  			return;
1217  		}
1218  		auto s = GetSharedFromThis ();
1219  		RequestDestinationWithEncryptedLeaseSet (dest,
1220  			[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
1221  			{
1222  				if (ls)
1223  					streamRequestComplete(s->CreateStream (ls, port));
1224  				else
1225  					streamRequestComplete (nullptr);
1226  			});
1227  	}
1228  
1229  	template<typename Dest>
1230  	std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStreamSync (const Dest& dest, uint16_t port)
1231  	{
1232  		volatile bool done = false;
1233  		std::shared_ptr<i2p::stream::Stream> stream;
1234  		std::condition_variable streamRequestComplete;
1235  		std::mutex streamRequestCompleteMutex;
1236  		CreateStream (
1237  			[&done, &streamRequestComplete, &streamRequestCompleteMutex, &stream](std::shared_ptr<i2p::stream::Stream> s)
1238  		    {
1239  				stream = s;
1240  				std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
1241  				streamRequestComplete.notify_all ();
1242  				done = true;
1243  			},
1244  		    dest, port);
1245  		while (!done)
1246  		{
1247  			std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
1248  			if (!done)
1249  				streamRequestComplete.wait (l);
1250  		}
1251  		return stream;
1252  	}
1253  
1254  	std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (const i2p::data::IdentHash& dest, uint16_t port)
1255  	{
1256  		return CreateStreamSync (dest, port);
1257  	}
1258  
1259  	std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, uint16_t port)
1260  	{
1261  		return CreateStreamSync (dest, port);
1262  	}
1263  
1264  	std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, uint16_t port)
1265  	{
1266  		if (m_StreamingDestination)
1267  			return m_StreamingDestination->CreateNewOutgoingStream (remote, port);
1268  		else
1269  			return nullptr;
1270  	}
1271  
1272  	void ClientDestination::SendPing (const i2p::data::IdentHash& to)
1273  	{
1274  		if (m_StreamingDestination)
1275  		{
1276  			auto leaseSet = FindLeaseSet (to);
1277  			if (leaseSet)
1278  				m_StreamingDestination->SendPing (leaseSet);
1279  			else
1280  			{
1281  				auto s = m_StreamingDestination;
1282  				RequestDestination (to,
1283  					[s](std::shared_ptr<const i2p::data::LeaseSet> ls)
1284  					{
1285  						if (ls) s->SendPing (ls);
1286  					});
1287  			}
1288  		}
1289  	}
1290  
1291  	void ClientDestination::SendPing (std::shared_ptr<const i2p::data::BlindedPublicKey> to)
1292  	{
1293  		auto s = m_StreamingDestination;
1294  		RequestDestinationWithEncryptedLeaseSet (to,
1295  			[s](std::shared_ptr<const i2p::data::LeaseSet> ls)
1296  			{
1297  				if (ls) s->SendPing (ls);
1298  			});
1299  	}
1300  
1301  	std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::GetStreamingDestination (uint16_t port) const
1302  	{
1303  		if (port)
1304  		{
1305  			auto it = m_StreamingDestinationsByPorts.find (port);
1306  			if (it != m_StreamingDestinationsByPorts.end ())
1307  				return it->second;
1308  		}
1309  		else // if port is zero, use default destination
1310  			return m_StreamingDestination;
1311  		return nullptr;
1312  	}
1313  
1314  	void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor)
1315  	{
1316  		if (m_StreamingDestination)
1317  			m_StreamingDestination->SetAcceptor (acceptor);
1318  	}
1319  
1320  	void ClientDestination::StopAcceptingStreams ()
1321  	{
1322  		if (m_StreamingDestination)
1323  			m_StreamingDestination->ResetAcceptor ();
1324  	}
1325  
1326  	bool ClientDestination::IsAcceptingStreams () const
1327  	{
1328  		if (m_StreamingDestination)
1329  			return m_StreamingDestination->IsAcceptorSet ();
1330  		return false;
1331  	}
1332  
1333  	void ClientDestination::AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor)
1334  	{
1335  		if (m_StreamingDestination)
1336  			m_StreamingDestination->AcceptOnce (acceptor);
1337  	}
1338  
1339  	std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (uint16_t port, bool gzip)
1340  	{
1341  		auto dest = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis (), port, gzip);
1342  		if (port)
1343  			m_StreamingDestinationsByPorts[port] = dest;
1344  		else // update default
1345  			m_StreamingDestination = dest;
1346  		return dest;
1347  	}
1348  
1349  	std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::RemoveStreamingDestination (uint16_t port)
1350  	{
1351  		if (port)
1352  		{
1353  			auto it = m_StreamingDestinationsByPorts.find (port);
1354  			if (it != m_StreamingDestinationsByPorts.end ())
1355  			{
1356  				auto ret = it->second;
1357  				m_StreamingDestinationsByPorts.erase (it);
1358  				return ret;
1359  			}
1360  		}
1361  		return nullptr;
1362  	}
1363  
1364  	i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination (bool gzip,
1365  		i2p::datagram::DatagramVersion version)
1366  	{
1367  		if (!m_DatagramDestination)
1368  		{
1369  			if (!GetNumRatchetInboundTags ())
1370  				SetNumRatchetInboundTags (i2p::garlic::ECIESX25519_MAX_NUM_GENERATED_TAGS); // set max tags if not specified
1371  			m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis (), gzip, version);
1372  		}
1373  		return m_DatagramDestination;
1374  	}
1375  
1376  	std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const
1377  	{
1378  		std::vector<std::shared_ptr<const i2p::stream::Stream> > ret;
1379  		if (m_StreamingDestination)
1380  		{
1381  			for (auto& it: m_StreamingDestination->GetStreams ())
1382  				ret.push_back (it.second);
1383  		}
1384  		for (auto& it: m_StreamingDestinationsByPorts)
1385  			for (auto& it1: it.second->GetStreams ())
1386  				ret.push_back (it1.second);
1387  		return ret;
1388  	}
1389  
1390  	void ClientDestination::PersistTemporaryKeys (std::shared_ptr<i2p::crypto::LocalEncryptionKey> keys)
1391  	{
1392  		if (!keys) return;
1393  		std::string ident = GetIdentHash().ToBase32();
1394  		std::string path  = i2p::fs::DataDirPath("destinations", ident + "." + std::to_string (keys->keyType) + ".dat");
1395  		std::ifstream f(path, std::ifstream::binary);
1396  		if (f)
1397  		{
1398  			size_t len = 0;
1399  			if (keys->keyType == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
1400  				len = 512;
1401  			else if (keys->keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
1402  			{
1403  				f.seekg (0, std::ios::end);
1404  				len = f.tellg();
1405  				f.seekg (0, std::ios::beg);
1406  			}
1407  
1408  			if (len == 512)
1409  			{
1410  				char pub[256], priv[256];
1411  				f.read (pub, 256);
1412  				memcpy (keys->pub.data(), pub, keys->pub.size());
1413  				f.read (priv, 256);
1414  				memcpy (keys->priv.data (), priv, keys->priv.size ());
1415  			}
1416  			else
1417  			{
1418  				f.read ((char *)keys->pub.data(), keys->pub.size());
1419  				f.read ((char *)keys->priv.data(), keys->priv.size());
1420  			}
1421  			if (f)
1422  				return;
1423  			else
1424  				LogPrint(eLogWarning, "Destination: Can't read keys from ", path);
1425  		}
1426  
1427  		LogPrint (eLogInfo, "Destination: Creating new temporary keys of type ", keys->keyType, " for address ", ident, ".b32.i2p");
1428  		memset (keys->priv.data (), 0, keys->priv.size ());
1429  		memset (keys->pub.data (), 0, keys->pub.size ());
1430  		keys->GenerateKeys ();
1431  
1432  		std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
1433  		if (f1)
1434  		{
1435  			f1.write ((char *)keys->pub.data (), keys->pub.size ());
1436  			f1.write ((char *)keys->priv.data (), keys->priv.size ());
1437  		}
1438  		if (!f1)
1439  			LogPrint(eLogError, "Destination: Can't save keys to ", path);
1440  	}
1441  
1442  	void ClientDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
1443  	{
1444  		std::shared_ptr<i2p::data::LocalLeaseSet> leaseSet;
1445  		if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
1446  		{
1447  			auto it = m_EncryptionKeys.find (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL);
1448  			if (it != m_EncryptionKeys.end ())
1449  			{
1450  				leaseSet = std::make_shared<i2p::data::LocalLeaseSet> (GetIdentity (), it->second->pub.data (), tunnels);
1451  				// sign
1452  				Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ());
1453  			}
1454  			else
1455  				LogPrint (eLogError, "Destinations: Wrong encryption key type for LeaseSet type 1");
1456  		}
1457  		else
1458  		{
1459  			// standard LS2 (type 3) first
1460  			if (m_EncryptionKeys.empty ())
1461  			{
1462  				LogPrint (eLogError, "Destinations: No encryption keys");
1463  				return;
1464  			}
1465  
1466  			i2p::data::LocalLeaseSet2::EncryptionKeys keySections;
1467  			std::shared_ptr<const i2p::crypto::LocalEncryptionKey> preferredSection;
1468  			if (m_EncryptionKeys.size () == 1)
1469  				preferredSection = m_EncryptionKeys.begin ()->second; // only key
1470  			else
1471  			{
1472  				for (const auto& it: m_EncryptionKeys)
1473  					if (it.first == m_PreferredCryptoType)
1474  						preferredSection = it.second;
1475  					else
1476  						keySections.push_back (it.second);
1477  			}
1478  			if (preferredSection)
1479  				keySections.push_front (preferredSection); // make preferred first
1480  
1481  			auto publishedTimestamp = i2p::util::GetSecondsSinceEpoch ();
1482  			if (publishedTimestamp <= m_LastPublishedTimestamp)
1483  			{
1484  				LogPrint (eLogDebug, "Destination: LeaseSet update at the same second");
1485  				publishedTimestamp++; // force newer timestamp
1486  			}
1487  			bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
1488  			auto ls2 = std::make_shared<i2p::data::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2,
1489  				m_Keys, keySections, tunnels, IsPublic (), publishedTimestamp, isPublishedEncrypted);
1490  			if (isPublishedEncrypted) // encrypt if type 5
1491  				ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys, GetAuthType (), m_AuthKeys);
1492  			leaseSet = ls2;
1493  			m_LastPublishedTimestamp = publishedTimestamp;
1494  		}
1495  		SetLeaseSet (leaseSet);
1496  	}
1497  
1498  	void ClientDestination::CleanupDestination ()
1499  	{
1500  		if (m_DatagramDestination) m_DatagramDestination->CleanUp ();
1501  	}
1502  
1503  	bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const
1504  	{
1505  		std::shared_ptr<i2p::crypto::LocalEncryptionKey> encryptionKey;
1506  		if (!m_EncryptionKeys.empty ())
1507  		{
1508  			if (m_EncryptionKeys.rbegin ()->first == preferredCrypto)
1509  				encryptionKey = m_EncryptionKeys.rbegin ()->second;
1510  			else
1511  			{
1512  				auto it = m_EncryptionKeys.find (preferredCrypto);
1513  				if (it != m_EncryptionKeys.end ())
1514  					encryptionKey = it->second;
1515  			}
1516  			if (!encryptionKey)
1517  				encryptionKey = m_EncryptionKeys.rbegin ()->second;
1518  		}
1519  		if (encryptionKey)
1520  			return encryptionKey->decryptor->Decrypt (encrypted, data);
1521  		else
1522  			LogPrint (eLogError, "Destinations: Decryptor is not set");
1523  		return false;
1524  	}
1525  
1526  	bool ClientDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const
1527  	{
1528  #if __cplusplus >= 202002L // C++20
1529  		return m_EncryptionKeys.contains (keyType);
1530  #else
1531  		return m_EncryptionKeys.count (keyType) > 0;
1532  #endif
1533  	}
1534  
1535  	i2p::data::CryptoKeyType ClientDestination::GetRatchetsHighestCryptoType () const
1536  	{
1537  		if (m_EncryptionKeys.empty ()) return 0;
1538  		auto cryptoType = m_EncryptionKeys.rbegin ()->first;
1539  		return cryptoType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? cryptoType : 0;
1540  	}
1541  
1542  	const uint8_t * ClientDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const
1543  	{
1544  		auto it = m_EncryptionKeys.find (keyType);
1545  		if (it != m_EncryptionKeys.end ())
1546  			return it->second->pub.data ();
1547  		return nullptr;
1548  	}
1549  
1550  	void ClientDestination::ReadAuthKey (const std::string& group, const i2p::util::Mapping * params)
1551  	{
1552  		for (const auto& it: params->GetOptions ())
1553  			if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group))
1554  			{
1555  				auto pos = it.second.find (':');
1556  				if (pos != std::string::npos)
1557  				{
1558  					i2p::data::AuthPublicKey pubKey;
1559  					if (pubKey.FromBase64 (it.second.substr (pos+1)))
1560  						m_AuthKeys->push_back (pubKey);
1561  					else
1562  						LogPrint (eLogCritical, "Destination: Unexpected auth key: ", it.second.substr (pos+1));
1563  				}
1564  			}
1565  	}
1566  
1567  	bool ClientDestination::DeleteStream (uint32_t recvStreamID)
1568  	{
1569  		if (m_StreamingDestination->DeleteStream (recvStreamID))
1570  			return true;
1571  		for (auto it: m_StreamingDestinationsByPorts)
1572  			if (it.second->DeleteStream (recvStreamID))
1573  				return true;
1574  		return false;
1575  	}
1576  
1577  	RunnableClientDestination::RunnableClientDestination (const i2p::data::PrivateKeys& keys,
1578  		bool isPublic, const i2p::util::Mapping * params):
1579  		RunnableService ("Destination"), ClientDestination (GetIOService (), keys, isPublic, params)
1580  	{
1581  		if (!GetNickname ().empty ())
1582  			RunnableService::SetName (GetNickname ());
1583  	}
1584  
1585  	RunnableClientDestination::~RunnableClientDestination ()
1586  	{
1587  		if (IsRunning ())
1588  			Stop ();
1589  	}
1590  
1591  	void RunnableClientDestination::Start ()
1592  	{
1593  		if (!IsRunning ())
1594  		{
1595  			ClientDestination::Start ();
1596  			StartIOService ();
1597  		}
1598  	}
1599  
1600  	void RunnableClientDestination::Stop ()
1601  	{
1602  		if (IsRunning ())
1603  		{
1604  			ClientDestination::Stop ();
1605  			StopIOService ();
1606  		}
1607  	}
1608  
1609  }
1610  }