/ libi2pd_client / I2CP.cpp
I2CP.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 <string.h>
  10  #include <stdlib.h>
  11  #include <openssl/rand.h>
  12  #include "I2PEndian.h"
  13  #include "Log.h"
  14  #include "Timestamp.h"
  15  #include "LeaseSet.h"
  16  #include "ClientContext.h"
  17  #include "Transports.h"
  18  #include "Signature.h"
  19  #include "Config.h"
  20  #include "I2CP.h"
  21  
  22  namespace i2p
  23  {
  24  namespace client
  25  {
  26  
  27  	I2CPDestination::I2CPDestination (boost::asio::io_context& service, std::shared_ptr<I2CPSession> owner,
  28  		std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, bool isSameThread,
  29  	    const i2p::util::Mapping& params):
  30  		LeaseSetDestination (service, isPublic, &params),
  31  		m_Owner (owner), m_Identity (identity), m_IsCreatingLeaseSet (false), m_IsSameThread (isSameThread),
  32  		m_LeaseSetCreationTimer (service), m_ReadinessCheckTimer (service)
  33  	{
  34  	}
  35  
  36  	void I2CPDestination::Stop ()
  37  	{
  38  		m_LeaseSetCreationTimer.cancel ();
  39  		m_ReadinessCheckTimer.cancel ();
  40  		LeaseSetDestination::Stop ();
  41  		m_Owner = nullptr;
  42  	}
  43  
  44  	void I2CPDestination::SetEncryptionPrivateKey (i2p::data::CryptoKeyType keyType, const uint8_t * key)
  45  	{
  46  		bool exist = false;
  47  		auto it = m_EncryptionKeys.find (keyType);
  48  		if (it != m_EncryptionKeys.end () && !memcmp (it->second->priv.data (), key, it->second->priv.size ())) // new key?
  49  			exist = true;
  50  		if (!exist)
  51  		{
  52  			auto encryptionKey = std::make_shared<i2p::crypto::LocalEncryptionKey> (keyType);
  53  			memcpy (encryptionKey->priv.data (), key, encryptionKey->priv.size ());
  54  			encryptionKey->CreateDecryptor (); // from private key
  55  			m_EncryptionKeys.insert_or_assign (keyType, encryptionKey);
  56  		}
  57  	}
  58  
  59  	void I2CPDestination::SetECIESx25519EncryptionPrivateKey (i2p::data::CryptoKeyType keyType, const uint8_t * key)
  60  	{
  61  		bool exist = false;
  62  		auto it = m_EncryptionKeys.find (keyType);
  63  		if (it != m_EncryptionKeys.end () && !memcmp (it->second->priv.data (), key, it->second->priv.size ())) // new key?
  64  			exist = true;
  65  		if (!exist)
  66  		{
  67  			auto encryptionKey = std::make_shared<i2p::crypto::LocalEncryptionKey> (keyType);
  68  			memcpy (encryptionKey->priv.data (), key, encryptionKey->priv.size ());
  69  			// create decryptor manually
  70  			auto decryptor = std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key, true); // calculate publicKey
  71  			encryptionKey->decryptor = decryptor;
  72  			// assign public key from decryptor
  73  			memcpy (encryptionKey->pub.data (), decryptor->GetPubicKey (), encryptionKey->pub.size ());
  74  			// insert or replace new
  75  			m_EncryptionKeys.insert_or_assign (keyType, encryptionKey);
  76  		}
  77  	}
  78  
  79  	bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const
  80  	{
  81  		std::shared_ptr<i2p::crypto::LocalEncryptionKey> encryptionKey;
  82  		if (!m_EncryptionKeys.empty ())
  83  		{
  84  			if (m_EncryptionKeys.rbegin ()->first == preferredCrypto)
  85  				encryptionKey = m_EncryptionKeys.rbegin ()->second;
  86  			else
  87  			{
  88  				auto it = m_EncryptionKeys.find (preferredCrypto);
  89  				if (it != m_EncryptionKeys.end ())
  90  					encryptionKey = it->second;
  91  			}
  92  			if (!encryptionKey)
  93  				encryptionKey = m_EncryptionKeys.rbegin ()->second;
  94  		}
  95  		if (encryptionKey)
  96  			return encryptionKey->decryptor->Decrypt (encrypted, data);
  97  		else
  98  			LogPrint (eLogError, "I2CP: Decryptor is not set");
  99  		return false;
 100  	}
 101  
 102  	const uint8_t * I2CPDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const
 103  	{
 104  		auto it = m_EncryptionKeys.find (keyType);
 105  		if (it != m_EncryptionKeys.end ())
 106  			return it->second->pub.data ();
 107  		return nullptr;
 108  	}
 109  
 110  	bool I2CPDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const
 111  	{
 112  #if __cplusplus >= 202002L // C++20
 113  		return m_EncryptionKeys.contains (keyType);
 114  #else
 115  		return m_EncryptionKeys.count (keyType) > 0;
 116  #endif
 117  	}
 118  
 119  	i2p::data::CryptoKeyType I2CPDestination::GetPreferredCryptoType () const
 120  	{
 121  		return !m_EncryptionKeys.empty () ? m_EncryptionKeys.rbegin ()->first : 0;
 122  	}
 123  
 124  	i2p::data::CryptoKeyType I2CPDestination::GetRatchetsHighestCryptoType () const
 125  	{
 126  		if (m_EncryptionKeys.empty ()) return 0;
 127  		auto cryptoType = m_EncryptionKeys.rbegin ()->first;
 128  		return cryptoType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? cryptoType : 0;
 129  	}
 130  
 131  	void I2CPDestination::ScheduleSessionResponseTimer (std::shared_ptr<i2p::garlic::ECIESX25519AEADRatchetSession> session)
 132  	{
 133  		if (!session) return;
 134  		auto timer = std::make_shared<boost::asio::steady_timer>(GetService ());
 135  		timer->expires_after (std::chrono::milliseconds(I2CP_RATCHETS_RESPONSE_TIMEOUT));
 136  		timer->async_wait ([session, s = GetSharedFromThis (), timer](const boost::system::error_code& ecode)
 137  		{
 138  			if (ecode != boost::asio::error::operation_aborted &&
 139  				!session->IsTerminated () && session->IsResponseRequired () && // response still required
 140  				session->GetDestinationPtr ()) // we know ident hash
 141  			{
 142  				auto pool = s->GetTunnelPool ();
 143  				if (!pool) return;
 144  				auto ls = s->FindLeaseSet (*session->GetDestinationPtr ());
 145  				if (!ls) return;
 146  				auto garlic = session->WrapSingleMessage (nullptr); // no data messages
 147  				if (!garlic) return;
 148  				auto leases = ls->GetNonExpiredLeases (false);
 149  				if (!leases.empty ())
 150  				{
 151  					auto remoteLease = leases[s->GetRng ()() % leases.size ()];
 152  					auto outboundTunnel = pool->GetNextOutboundTunnel ();
 153  					if (outboundTunnel)
 154  					{
 155  						LogPrint (eLogDebug, "I2CP: Sending ratchets session response");
 156  						s->SendMsg (garlic, outboundTunnel, remoteLease);
 157  					}
 158  				}
 159  			}
 160  		});
 161  	}
 162  
 163  	void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len,
 164  		i2p::garlic::ECIESX25519AEADRatchetSession * from)
 165  	{
 166  		uint32_t length = bufbe32toh (buf);
 167  		if (length > len - 4) length = len - 4;
 168  		if (m_Owner)
 169  			m_Owner->SendMessagePayloadMessage (buf + 4, length);
 170  	}
 171  
 172  	void I2CPDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
 173  	{
 174  		boost::asio::post (GetService (), std::bind (&I2CPDestination::PostCreateNewLeaseSet, GetSharedFromThis (), tunnels));
 175  	}
 176  
 177  	void I2CPDestination::PostCreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
 178  	{
 179  		if (m_IsCreatingLeaseSet)
 180  		{
 181  			LogPrint (eLogInfo, "I2CP: LeaseSet is being created");
 182  			return;
 183  		}
 184  		m_ReadinessCheckTimer.cancel ();
 185  		auto pool = GetTunnelPool ();
 186  		if (!pool || pool->GetOutboundTunnels ().empty ())
 187  		{
 188  			// try again later
 189  			m_ReadinessCheckTimer.expires_from_now (boost::posix_time::seconds(I2CP_DESTINATION_READINESS_CHECK_INTERVAL));
 190  			m_ReadinessCheckTimer.async_wait(
 191  				[s=GetSharedFromThis (), tunnels=std::move(tunnels)](const boost::system::error_code& ecode)
 192  			    {
 193  					if (ecode != boost::asio::error::operation_aborted)
 194  						s->PostCreateNewLeaseSet (tunnels);
 195  				});
 196  			return;
 197  		}
 198  		uint8_t priv[256] = {0};
 199  		i2p::data::LocalLeaseSet ls (m_Identity, priv, tunnels); // we don't care about encryption key, we need leases only
 200  		m_LeaseSetExpirationTime = ls.GetExpirationTime ();
 201  		uint8_t * leases = ls.GetLeases ();
 202  		int numLeases = leases[-1];
 203  		if (m_Owner && numLeases)
 204  		{
 205  			uint16_t sessionID = m_Owner->GetSessionID ();
 206  			if (sessionID != 0xFFFF)
 207  			{
 208  				m_IsCreatingLeaseSet = true;
 209  				htobe16buf (leases - 3, sessionID);
 210  				size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*numLeases;
 211  				m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l);
 212  				m_LeaseSetCreationTimer.expires_from_now (boost::posix_time::seconds (I2CP_LEASESET_CREATION_TIMEOUT));
 213  				auto s = GetSharedFromThis ();
 214  				m_LeaseSetCreationTimer.async_wait ([s](const boost::system::error_code& ecode)
 215  				{
 216  					if (ecode != boost::asio::error::operation_aborted)
 217  					{
 218  						LogPrint (eLogInfo, "I2CP: LeaseSet creation timeout expired. Terminate");
 219  						if (s->m_Owner) s->m_Owner->Stop ();
 220  					}
 221  				});
 222  			}
 223  		}
 224  		else
 225  			LogPrint (eLogError, "I2CP: Can't request LeaseSet");
 226  	}
 227  
 228  	void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len)
 229  	{
 230  		m_IsCreatingLeaseSet = false;
 231  		m_LeaseSetCreationTimer.cancel ();
 232  		auto ls = std::make_shared<i2p::data::LocalLeaseSet> (m_Identity, buf, len);
 233  		ls->SetExpirationTime (m_LeaseSetExpirationTime);
 234  		SetLeaseSet (ls);
 235  	}
 236  
 237  	void I2CPDestination::LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len)
 238  	{
 239  		m_IsCreatingLeaseSet = false;
 240  		m_LeaseSetCreationTimer.cancel ();
 241  		auto ls = (storeType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ?
 242  			std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (m_Identity, buf, len):
 243  			std::make_shared<i2p::data::LocalLeaseSet2> (storeType, m_Identity, buf, len);
 244  		ls->SetExpirationTime (m_LeaseSetExpirationTime);
 245  		SetLeaseSet (ls);
 246  	}
 247  
 248  	void I2CPDestination::SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce)
 249  	{
 250  		auto msg = m_I2NPMsgsPool.AcquireSharedMt ();
 251  		uint8_t * buf = msg->GetPayload ();
 252  		htobe32buf (buf, len);
 253  		memcpy (buf + 4, payload, len);
 254  		msg->len += len + 4;
 255  		msg->FillI2NPMessageHeader (eI2NPData);
 256  		auto remote = FindLeaseSet (ident);
 257  		if (remote)
 258  		{
 259  			if (m_IsSameThread)
 260  			{
 261  				// send right a way
 262  				bool sent = SendMsg (msg, remote);
 263  				if (m_Owner)
 264  					m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
 265  			}
 266  			else
 267  			{
 268  				// send in destination's thread
 269  				auto s = GetSharedFromThis ();
 270  				boost::asio::post (GetService (),
 271  					[s, msg, remote, nonce]()
 272  					{
 273  						bool sent = s->SendMsg (msg, remote);
 274  						if (s->m_Owner)
 275  							s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
 276  					});
 277  			}
 278  		}
 279  		else
 280  		{
 281  			auto s = GetSharedFromThis ();
 282  			RequestDestination (ident,
 283  				[s, msg, nonce](std::shared_ptr<i2p::data::LeaseSet> ls)
 284  				{
 285  					if (ls)
 286  					{
 287  						bool sent = s->SendMsg (msg, ls);
 288  						if (s->m_Owner)
 289  							s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
 290  					}
 291  					else if (s->m_Owner)
 292  						s->m_Owner->SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLeaseSet);
 293  				});
 294  		}
 295  	}
 296  
 297  	bool I2CPDestination::SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
 298  	{
 299  		auto remoteSession = GetRoutingSession (remote, true);
 300  		if (!remoteSession)
 301  		{
 302  			LogPrint (eLogError, "I2CP: Failed to create remote session");
 303  			return false;
 304  		}
 305  		auto garlic = remoteSession->WrapSingleMessage (msg); // shared routing path mitgh be dropped here
 306  		auto path = remoteSession->GetSharedRoutingPath ();
 307  		std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
 308  		std::shared_ptr<const i2p::data::Lease> remoteLease;
 309  		if (path)
 310  		{
 311  			if (!remoteSession->CleanupUnconfirmedTags ()) // no stuck tags
 312  			{
 313  				outboundTunnel = path->outboundTunnel;
 314  				remoteLease = path->remoteLease;
 315  			}
 316  			else
 317  				remoteSession->SetSharedRoutingPath (nullptr);
 318  		}
 319  		if (!outboundTunnel || !remoteLease)
 320  		{
 321  			auto leases = remote->GetNonExpiredLeases (false); // without threshold
 322  			if (leases.empty ())
 323  				leases = remote->GetNonExpiredLeases (true); // with threshold
 324  			if (!leases.empty ())
 325  			{
 326  				remoteLease = leases[GetRng ()() % leases.size ()];
 327  				auto leaseRouter = i2p::data::netdb.FindRouter (remoteLease->tunnelGateway);
 328  				auto pool = GetTunnelPool ();
 329  				if (pool)
 330  					outboundTunnel = pool->GetNextOutboundTunnel (nullptr,
 331  						leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports);
 332  				else
 333  					outboundTunnel = nullptr;
 334  			}
 335  			if (remoteLease && outboundTunnel)
 336  				remoteSession->SetSharedRoutingPath (std::make_shared<i2p::garlic::GarlicRoutingPath> (
 337  					i2p::garlic::GarlicRoutingPath{outboundTunnel, remoteLease, 10000, 0})); // 10 secs RTT
 338  			else
 339  				remoteSession->SetSharedRoutingPath (nullptr);
 340  		}
 341  		m_Owner->AddRoutingSession (remote->GetIdentity ()->GetStandardIdentity ().signingKey + 96, remoteSession); // last 32 bytes
 342  		return SendMsg (garlic, outboundTunnel, remoteLease);
 343  	}
 344  
 345  	bool I2CPDestination::SendMsg (std::shared_ptr<I2NPMessage> garlic,
 346  	    std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel, std::shared_ptr<const i2p::data::Lease> remoteLease)
 347  	{
 348  		if (remoteLease && outboundTunnel)
 349  		{
 350  			outboundTunnel->SendTunnelDataMsgs (
 351  			    {
 352  					i2p::tunnel::TunnelMessageBlock
 353  					{
 354  						i2p::tunnel::eDeliveryTypeTunnel,
 355  						remoteLease->tunnelGateway, remoteLease->tunnelID,
 356  						garlic
 357  					}
 358  				});
 359  			return true;
 360  		}
 361  		else
 362  		{
 363  			if (outboundTunnel)
 364  				LogPrint (eLogWarning, "I2CP: Failed to send message. All leases expired");
 365  			else
 366  				LogPrint (eLogWarning, "I2CP: Failed to send message. No outbound tunnels");
 367  			return false;
 368  		}
 369  	}
 370  
 371  	bool I2CPDestination::SendMsg (const uint8_t * payload, size_t len,
 372  		std::shared_ptr<i2p::garlic::GarlicRoutingSession> remoteSession, uint32_t nonce)
 373  	{
 374  		if (!remoteSession) return false;
 375  		auto path = remoteSession->GetSharedRoutingPath ();
 376  		if (!path) return false;
 377  		// get tunnels
 378  		std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
 379  		std::shared_ptr<const i2p::data::Lease> remoteLease;
 380  		if (!remoteSession->CleanupUnconfirmedTags ()) // no stuck tags
 381  		{
 382  			outboundTunnel = path->outboundTunnel;
 383  			remoteLease = path->remoteLease;
 384  		}
 385  		else
 386  		{
 387  			remoteSession->SetSharedRoutingPath (nullptr);
 388  			return false;
 389  		}
 390  		// create Data message
 391  		auto msg = m_I2NPMsgsPool.AcquireSharedMt ();
 392  		uint8_t * buf = msg->GetPayload ();
 393  		htobe32buf (buf, len);
 394  		memcpy (buf + 4, payload, len);
 395  		msg->len += len + 4;
 396  		msg->FillI2NPMessageHeader (eI2NPData);
 397  		// wrap in gralic
 398  		auto garlic = remoteSession->WrapSingleMessage (msg);
 399  		// send
 400  		bool sent = SendMsg (garlic, outboundTunnel, remoteLease);
 401  		m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
 402  		if (!sent)
 403  			remoteSession->SetSharedRoutingPath (nullptr);
 404  		return sent;
 405  	}
 406  
 407  	void I2CPDestination::CleanupDestination ()
 408  	{
 409  		m_I2NPMsgsPool.CleanUpMt ();
 410  		if (m_Owner) m_Owner->CleanupRoutingSessions ();
 411  	}
 412  
 413  	RunnableI2CPDestination::RunnableI2CPDestination (std::shared_ptr<I2CPSession> owner,
 414  		std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const i2p::util::Mapping& params):
 415  		RunnableService ("I2CP"),
 416  		I2CPDestination (GetIOService (), owner, identity, isPublic, false, params)
 417  	{
 418  	}
 419  
 420  	RunnableI2CPDestination::~RunnableI2CPDestination ()
 421  	{
 422  		if (IsRunning ())
 423  			Stop ();
 424  	}
 425  
 426  	void RunnableI2CPDestination::Start ()
 427  	{
 428  		if (!IsRunning ())
 429  		{
 430  			I2CPDestination::Start ();
 431  			StartIOService ();
 432  		}
 433  	}
 434  
 435  	void RunnableI2CPDestination::Stop ()
 436  	{
 437  		if (IsRunning ())
 438  		{
 439  			I2CPDestination::Stop ();
 440  			StopIOService ();
 441  		}
 442  	}
 443  
 444  	I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
 445  		m_Owner (owner), m_Socket (socket), m_SessionID (0xFFFF), m_MessageID (0),
 446  		m_IsSendAccepted (true), m_IsSending (false)
 447  	{
 448  	}
 449  
 450  	I2CPSession::~I2CPSession ()
 451  	{
 452  		Terminate ();
 453  	}
 454  
 455  	void I2CPSession::Start ()
 456  	{
 457  		if (m_Socket)
 458  		{
 459  			m_Socket->set_option (boost::asio::socket_base::receive_buffer_size (I2CP_MAX_MESSAGE_LENGTH));
 460  			m_Socket->set_option (boost::asio::socket_base::send_buffer_size (I2CP_MAX_MESSAGE_LENGTH));
 461  		}
 462  		ReadProtocolByte ();
 463  	}
 464  
 465  	void I2CPSession::Stop ()
 466  	{
 467  		Terminate ();
 468  	}
 469  
 470  	void I2CPSession::ReadProtocolByte ()
 471  	{
 472  		if (m_Socket)
 473  		{
 474  			auto s = shared_from_this ();
 475  			m_Socket->async_read_some (boost::asio::buffer (m_Header, 1),
 476  				[s](const boost::system::error_code& ecode, std::size_t bytes_transferred)
 477  					{
 478  						if (!ecode && bytes_transferred > 0 && s->m_Header[0] == I2CP_PROTOCOL_BYTE)
 479  							s->ReceiveHeader ();
 480  						else
 481  							s->Terminate ();
 482  					});
 483  		}
 484  	}
 485  
 486  	void I2CPSession::ReceiveHeader ()
 487  	{
 488  		if (!m_Socket)
 489  		{
 490  			LogPrint (eLogError, "I2CP: Can't receive header");
 491  			return;
 492  		}
 493  		boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Header, I2CP_HEADER_SIZE),
 494  			boost::asio::transfer_all (),
 495  			std::bind (&I2CPSession::HandleReceivedHeader, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
 496  	}
 497  
 498  	void I2CPSession::HandleReceivedHeader (const boost::system::error_code& ecode, std::size_t bytes_transferred)
 499  	{
 500  		if (ecode)
 501  			Terminate ();
 502  		else
 503  		{
 504  			m_PayloadLen = bufbe32toh (m_Header + I2CP_HEADER_LENGTH_OFFSET);
 505  			if (m_PayloadLen > 0)
 506  			{
 507  				if (m_PayloadLen <= I2CP_MAX_MESSAGE_LENGTH)
 508  				{
 509  					if (!m_Socket) return;
 510  					boost::system::error_code ec;
 511  					size_t moreBytes = m_Socket->available(ec);
 512  					if (!ec)
 513  					{
 514  						if (moreBytes >= m_PayloadLen)
 515  						{
 516  							// read and process payload immediately if available
 517  							moreBytes = boost::asio::read (*m_Socket, boost::asio::buffer(m_Payload, m_PayloadLen), boost::asio::transfer_all (), ec);
 518  							HandleReceivedPayload (ec, moreBytes);
 519  						}
 520  						else
 521  							ReceivePayload ();
 522  					}
 523  					else
 524  					{
 525  						LogPrint (eLogWarning, "I2CP: Socket error: ", ec.message ());
 526  						Terminate ();
 527  					}
 528  				}
 529  				else
 530  				{
 531  					LogPrint (eLogError, "I2CP: Unexpected payload length ", m_PayloadLen);
 532  					Terminate ();
 533  				}
 534  			}
 535  			else // no following payload
 536  			{
 537  				HandleMessage ();
 538  				ReceiveHeader (); // next message
 539  			}
 540  		}
 541  	}
 542  
 543  	void I2CPSession::ReceivePayload ()
 544  	{
 545  		if (!m_Socket)
 546  		{
 547  			LogPrint (eLogError, "I2CP: Can't receive payload");
 548  			return;
 549  		}
 550  		boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Payload, m_PayloadLen),
 551  			boost::asio::transfer_all (),
 552  			std::bind (&I2CPSession::HandleReceivedPayload, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
 553  	}
 554  
 555  	void I2CPSession::HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred)
 556  	{
 557  		if (ecode)
 558  			Terminate ();
 559  		else
 560  		{
 561  			HandleMessage ();
 562  			m_PayloadLen = 0;
 563  			ReceiveHeader (); // next message
 564  		}
 565  	}
 566  
 567  	void I2CPSession::HandleMessage ()
 568  	{
 569  		auto handler = m_Owner.GetMessagesHandlers ()[m_Header[I2CP_HEADER_TYPE_OFFSET]];
 570  		if (handler)
 571  			(this->*handler)(m_Payload, m_PayloadLen);
 572  		else
 573  			LogPrint (eLogError, "I2CP: Unknown I2CP message ", (int)m_Header[I2CP_HEADER_TYPE_OFFSET]);
 574  	}
 575  
 576  	void I2CPSession::Terminate ()
 577  	{
 578  		if (m_Destination)
 579  		{
 580  			m_Destination->Stop ();
 581  			m_Destination = nullptr;
 582  		}
 583  		if (m_Socket)
 584  		{
 585  			m_Socket->close ();
 586  			m_Socket = nullptr;
 587  		}
 588  		if (!m_SendQueue.IsEmpty ())
 589  			m_SendQueue.CleanUp ();
 590  		if (m_SessionID != 0xFFFF)
 591  		{
 592  			m_Owner.RemoveSession (GetSessionID ());
 593  			LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " terminated");
 594  			m_SessionID = 0xFFFF;
 595  		}
 596  	}
 597  
 598  	void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len)
 599  	{
 600  		auto l = len + I2CP_HEADER_SIZE;
 601  		if (l > I2CP_MAX_MESSAGE_LENGTH)
 602  		{
 603  			LogPrint (eLogError, "I2CP: Message to send is too long ", l);
 604  			return;
 605  		}
 606  		auto sendBuf = m_IsSending ? std::make_shared<i2p::stream::SendBuffer> (l) : nullptr;
 607  		uint8_t * buf = sendBuf ? sendBuf->buf : m_SendBuffer;
 608  		htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len);
 609  		buf[I2CP_HEADER_TYPE_OFFSET] = type;
 610  		memcpy (buf + I2CP_HEADER_SIZE, payload, len);
 611  		if (sendBuf)
 612  		{
 613  			if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE)
 614  				m_SendQueue.Add (std::move(sendBuf));
 615  			else
 616  			{
 617  				LogPrint (eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE);
 618  				return;
 619  			}
 620  		}
 621  		else
 622  		{
 623  			auto socket = m_Socket;
 624  			if (socket)
 625  			{
 626  				m_IsSending = true;
 627  				boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l),
 628  					boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent,
 629  					shared_from_this (), std::placeholders::_1, std::placeholders::_2));
 630  			}
 631  		}
 632  	}
 633  
 634  	void I2CPSession::HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
 635  	{
 636  		if (ecode)
 637  		{
 638  			if (ecode != boost::asio::error::operation_aborted)
 639  				Terminate ();
 640  		}
 641  		else if (!m_SendQueue.IsEmpty ())
 642  		{
 643  			auto socket = m_Socket;
 644  			if (socket)
 645  			{
 646  				auto len = m_SendQueue.Get (m_SendBuffer, I2CP_MAX_MESSAGE_LENGTH);
 647  				boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, len),
 648  					boost::asio::transfer_all (),std::bind(&I2CPSession::HandleI2CPMessageSent,
 649  					shared_from_this (), std::placeholders::_1, std::placeholders::_2));
 650  			}
 651  			else
 652  				m_IsSending = false;
 653  		}
 654  		else
 655  			m_IsSending = false;
 656  	}
 657  
 658  	void I2CPSession::GetDateMessageHandler (const uint8_t * buf, size_t len)
 659  	{
 660  		constexpr std::string_view version(I2P_VERSION);
 661  		std::array<uint8_t, version.size() + 8 + 1> payload;
 662  		// set date
 663  		auto ts = i2p::util::GetMillisecondsSinceEpoch ();
 664  		htobe64buf (payload.data(), ts);
 665  		// send our version back
 666  		i2p::util::Mapping::WriteString (version, payload.data() + 8, payload.size() - 8);
 667  		SendI2CPMessage (I2CP_SET_DATE_MESSAGE, payload.data(), payload.size());
 668  	}
 669  
 670  	void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len)
 671  	{
 672  		RAND_bytes ((uint8_t *)&m_SessionID, 2);
 673  		auto identity = std::make_shared<i2p::data::IdentityEx>();
 674  		size_t offset = identity->FromBuffer (buf, len);
 675  		if (!offset)
 676  		{
 677  			LogPrint (eLogError, "I2CP: Create session malformed identity");
 678  			SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
 679  			return;
 680  		}
 681  		if (m_Owner.FindSessionByIdentHash (identity->GetIdentHash ()))
 682  		{
 683  			LogPrint (eLogError, "I2CP: Create session duplicate address ", identity->GetIdentHash ().ToBase32 ());
 684  			SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
 685  			return;
 686  		}
 687  		uint16_t optionsSize = bufbe16toh (buf + offset);
 688  		offset += 2;
 689  		if (optionsSize > len - offset)
 690  		{
 691  			LogPrint (eLogError, "I2CP: Options size ", optionsSize, "exceeds message size");
 692  			SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
 693  			return;
 694  		}
 695  		i2p::util::Mapping params;
 696  		params.FromBuffer (optionsSize, buf + offset, len - offset);
 697  		offset += optionsSize; // options
 698  		if (params[I2CP_PARAM_MESSAGE_RELIABILITY] == "none") m_IsSendAccepted = false;
 699  
 700  		offset += 8; // date
 701  		if (identity->Verify (buf, offset, buf + offset)) // signature
 702  		{
 703  			if (!m_Destination)
 704  			{
 705  				m_Destination = m_Owner.IsSingleThread () ?
 706  					std::make_shared<I2CPDestination>(m_Owner.GetService (), shared_from_this (), identity, true, true, params):
 707  					std::make_shared<RunnableI2CPDestination>(shared_from_this (), identity, true, params);
 708  				if (m_Owner.InsertSession (shared_from_this ()))
 709  				{
 710  					LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " created");
 711  					m_Destination->Start ();
 712  					SendSessionStatusMessage (eI2CPSessionStatusCreated); // created
 713  				}
 714  				else
 715  				{
 716  					LogPrint (eLogError, "I2CP: Session already exists");
 717  					SendSessionStatusMessage (eI2CPSessionStatusRefused);
 718  				}
 719  			}
 720  			else
 721  			{
 722  				LogPrint (eLogError, "I2CP: Session already exists");
 723  				SendSessionStatusMessage (eI2CPSessionStatusRefused); // refused
 724  			}
 725  		}
 726  		else
 727  		{
 728  			LogPrint (eLogError, "I2CP: Create session signature verification failed");
 729  			SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
 730  		}
 731  	}
 732  
 733  	void I2CPSession::DestroySessionMessageHandler (const uint8_t * buf, size_t len)
 734  	{
 735  		SendSessionStatusMessage (eI2CPSessionStatusDestroyed); // destroy
 736  		LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " destroyed");
 737  		Terminate ();
 738  	}
 739  
 740  	void I2CPSession::ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len)
 741  	{
 742  		I2CPSessionStatus status = eI2CPSessionStatusInvalid; // rejected
 743  		if(len > sizeof(uint16_t))
 744  		{
 745  			uint16_t sessionID = bufbe16toh(buf);
 746  			if(sessionID == m_SessionID)
 747  			{
 748  				buf += sizeof(uint16_t);
 749  				const uint8_t * body = buf;
 750  				i2p::data::IdentityEx ident;
 751  				if(ident.FromBuffer(buf, len - sizeof(uint16_t)))
 752  				{
 753  					if (ident == *m_Destination->GetIdentity())
 754  					{
 755  						size_t identsz = ident.GetFullLen();
 756  						buf += identsz;
 757  						uint16_t optssize = bufbe16toh(buf);
 758  						if (optssize <= len - sizeof(uint16_t) - sizeof(uint64_t) - identsz - ident.GetSignatureLen() - sizeof(uint16_t))
 759  						{
 760  							buf += sizeof(uint16_t);
 761  							i2p::util::Mapping opts;
 762  							opts.FromBuffer (optssize, buf, optssize);
 763  							buf += optssize;
 764  							//uint64_t date = bufbe64toh(buf);
 765  							buf += sizeof(uint64_t);
 766  							const uint8_t * sig = buf;
 767  							if(ident.Verify(body, len - sizeof(uint16_t) - ident.GetSignatureLen(), sig))
 768  							{
 769  								if(m_Destination->Reconfigure(opts))
 770  								{
 771  									LogPrint(eLogInfo, "I2CP: Reconfigured destination");
 772  									status = eI2CPSessionStatusUpdated; // updated
 773  								}
 774  								else
 775  									LogPrint(eLogWarning, "I2CP: Failed to reconfigure destination");
 776  							}
 777  							else
 778  								LogPrint(eLogError, "I2CP: Invalid reconfigure message signature");
 779  						}
 780  						else
 781  							LogPrint(eLogError, "I2CP: Mapping size mismatch");
 782  					}
 783  					else
 784  						LogPrint(eLogError, "I2CP: Destination mismatch");
 785  				}
 786  				else
 787  					LogPrint(eLogError, "I2CP: Malfromed destination");
 788  			}
 789  			else
 790  				LogPrint(eLogError, "I2CP: Session mismatch");
 791  		}
 792  		else
 793  			LogPrint(eLogError, "I2CP: Short message");
 794  		SendSessionStatusMessage (status);
 795  	}
 796  
 797  	void I2CPSession::SendSessionStatusMessage (I2CPSessionStatus status)
 798  	{
 799  		uint8_t buf[3];
 800  		htobe16buf (buf, m_SessionID);
 801  		buf[2] = (uint8_t)status;
 802  		SendI2CPMessage (I2CP_SESSION_STATUS_MESSAGE, buf, 3);
 803  	}
 804  
 805  	void I2CPSession::SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status)
 806  	{
 807  		if (!nonce) return; // don't send status with zero nonce
 808  		uint8_t buf[15];
 809  		htobe16buf (buf, m_SessionID);
 810  		htobe32buf (buf + 2, m_MessageID++);
 811  		buf[6] = (uint8_t)status;
 812  		memset (buf + 7, 0, 4); // size
 813  		htobe32buf (buf + 11, nonce);
 814  		SendI2CPMessage (I2CP_MESSAGE_STATUS_MESSAGE, buf, 15);
 815  	}
 816  
 817  	void I2CPSession::AddRoutingSession (const i2p::data::IdentHash& signingKey, std::shared_ptr<i2p::garlic::GarlicRoutingSession> remoteSession)
 818  	{
 819  		if (!remoteSession) return;
 820  		remoteSession->SetAckRequestInterval (I2CP_SESSION_ACK_REQUEST_INTERVAL);
 821  		std::lock_guard<std::mutex> l(m_RoutingSessionsMutex);
 822  		m_RoutingSessions[signingKey] = remoteSession;
 823  	}
 824  
 825  	void I2CPSession::CleanupRoutingSessions ()
 826  	{
 827  		std::lock_guard<std::mutex> l(m_RoutingSessionsMutex);
 828  		for (auto it = m_RoutingSessions.begin (); it != m_RoutingSessions.end ();)
 829  		{
 830  			if (it->second->IsTerminated ())
 831  				it = m_RoutingSessions.erase (it);
 832  			else
 833  				it++;
 834  		}
 835  	}
 836  
 837  	void I2CPSession::CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len)
 838  	{
 839  		uint16_t sessionID = bufbe16toh (buf);
 840  		if (sessionID == m_SessionID)
 841  		{
 842  			size_t offset = 2;
 843  			if (m_Destination)
 844  			{
 845  				offset += i2p::crypto::DSA_PRIVATE_KEY_LENGTH; // skip signing private key
 846  				// we always assume this field as 20 bytes (DSA) regardless actual size
 847  				// instead of
 848  				//offset += m_Destination->GetIdentity ()->GetSigningPrivateKeyLen ();
 849  				m_Destination->SetEncryptionPrivateKey (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, buf + offset);
 850  				offset += 256;
 851  				m_Destination->LeaseSetCreated (buf + offset, len - offset);
 852  			}
 853  		}
 854  		else
 855  			LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID);
 856  	}
 857  
 858  	void I2CPSession::CreateLeaseSet2MessageHandler (const uint8_t * buf, size_t len)
 859  	{
 860  		uint16_t sessionID = bufbe16toh (buf);
 861  		if (sessionID == m_SessionID)
 862  		{
 863  			size_t offset = 2;
 864  			if (m_Destination)
 865  			{
 866  				uint8_t storeType = buf[offset]; offset++; // store type
 867  				i2p::data::LeaseSet2 ls (storeType, buf + offset, len - offset); // outer layer only for encrypted
 868  				if (!ls.IsValid ())
 869  				{
 870  					LogPrint (eLogError, "I2CP: Invalid LeaseSet2 of type ", storeType);
 871  					return;
 872  				}
 873  				offset += ls.GetBufferLen ();
 874  				// private keys
 875  				int numPrivateKeys = buf[offset]; offset++;
 876  				for (int i = 0; i < numPrivateKeys; i++)
 877  				{
 878  					if (offset + 4 > len) return;
 879  					uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption type
 880  					uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length
 881  					if (offset + keyLen > len) return;
 882  					if (keyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
 883  						m_Destination->SetECIESx25519EncryptionPrivateKey (keyType, buf + offset);
 884  					else
 885  						m_Destination->SetEncryptionPrivateKey (keyType, buf + offset); // from identity
 886  					offset += keyLen;
 887  				}
 888  
 889  				m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ());
 890  			}
 891  		}
 892  		else
 893  			LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID);
 894  	}
 895  
 896  	void I2CPSession::SendMessageMessageHandler (const uint8_t * buf, size_t len)
 897  	{
 898  		uint16_t sessionID = bufbe16toh (buf);
 899  		if (sessionID == m_SessionID)
 900  		{
 901  			size_t offset = 2;
 902  			if (m_Destination)
 903  			{
 904  				const uint8_t * ident = buf + offset;
 905  				size_t identSize = i2p::data::GetIdentityBufferLen (ident, len - offset);
 906  				if (identSize)
 907  				{
 908  					offset += identSize;
 909  					uint32_t payloadLen = bufbe32toh (buf + offset);
 910  					if (payloadLen + offset <= len)
 911  					{
 912  						offset += 4;
 913  						uint32_t nonce = bufbe32toh (buf + offset + payloadLen);
 914  						if (m_Destination->IsReady ())
 915  						{
 916  							if (m_IsSendAccepted)
 917  								SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted
 918  							std::shared_ptr<i2p::garlic::GarlicRoutingSession> remoteSession;
 919  							{
 920  								std::lock_guard<std::mutex> l(m_RoutingSessionsMutex);
 921  								auto it = m_RoutingSessions.find (ident + i2p::data::DEFAULT_IDENTITY_SIZE - 35); // 32 bytes signing key
 922  								if (it != m_RoutingSessions.end ())
 923  								{
 924  									if (!it->second->IsTerminated () && it->second->IsReadyToSend ())
 925  										remoteSession = it->second;
 926  									else
 927  										m_RoutingSessions.erase (it);
 928  								}
 929  							}
 930  							if (!remoteSession || !m_Destination->SendMsg (buf + offset, payloadLen, remoteSession, nonce))
 931  							{
 932  								i2p::data::IdentHash identHash;
 933  								SHA256(ident, identSize, identHash); // calculate ident hash, because we don't need full identity
 934  								m_Destination->SendMsgTo (buf + offset, payloadLen, identHash, nonce);
 935  							}
 936  						}
 937  						else
 938  						{
 939  							LogPrint(eLogInfo, "I2CP: Destination is not ready");
 940  							SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLocalTunnels);
 941  						}
 942  					}
 943  					else
 944  						LogPrint(eLogError, "I2CP: Cannot send message, too big");
 945  				}
 946  				else
 947  					LogPrint(eLogError, "I2CP: Invalid identity");
 948  			}
 949  		}
 950  		else
 951  			LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID);
 952  	}
 953  
 954  	void I2CPSession::SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len)
 955  	{
 956  		SendMessageMessageHandler (buf, len - 8); // ignore flags(2) and expiration(6)
 957  	}
 958  
 959  	void I2CPSession::HostLookupMessageHandler (const uint8_t * buf, size_t len)
 960  	{
 961  		uint16_t sessionID = bufbe16toh (buf);
 962  		if (sessionID == m_SessionID || sessionID == 0xFFFF) // -1 means without session
 963  		{
 964  			uint32_t requestID = bufbe32toh (buf + 2);
 965  			//uint32_t timeout = bufbe32toh (buf + 6);
 966  			i2p::data::IdentHash ident;
 967  			switch (buf[10])
 968  			{
 969  				case 0: // hash
 970  					ident = i2p::data::IdentHash (buf + 11);
 971  				break;
 972  				case 1: // address
 973  				{
 974  					auto name = i2p::util::Mapping::ExtractString (buf + 11, len - 11);
 975  					auto addr = i2p::client::context.GetAddressBook ().GetAddress (name);
 976  					if (!addr || !addr->IsIdentHash ())
 977  					{
 978  						// TODO: handle blinded addresses
 979  						LogPrint (eLogError, "I2CP: Address ", name, " not found");
 980  						SendHostReplyMessage (requestID, nullptr);
 981  						return;
 982  					}
 983  					else
 984  						ident = addr->identHash;
 985  					break;
 986  				}
 987  				default:
 988  					LogPrint (eLogError, "I2CP: Request type ", (int)buf[10], " is not supported");
 989  					SendHostReplyMessage (requestID, nullptr);
 990  					return;
 991  			}
 992  
 993  			std::shared_ptr<LeaseSetDestination> destination = m_Destination;
 994  			if(!destination) destination = i2p::client::context.GetSharedLocalDestination ();
 995  			if (destination)
 996  			{
 997  				auto ls = destination->FindLeaseSet (ident);
 998  				if (ls)
 999  					SendHostReplyMessage (requestID, ls->GetIdentity ());
1000  				else
1001  				{
1002  					auto s = shared_from_this ();
1003  					destination->RequestDestination (ident,
1004  						[s, requestID](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
1005  						{
1006  							s->SendHostReplyMessage (requestID, leaseSet ? leaseSet->GetIdentity () : nullptr);
1007  						});
1008  				}
1009  			}
1010  			else
1011  				SendHostReplyMessage (requestID, nullptr);
1012  		}
1013  		else
1014  			LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID);
1015  	}
1016  
1017  	void I2CPSession::SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity)
1018  	{
1019  		if (identity)
1020  		{
1021  			size_t l = identity->GetFullLen () + 7;
1022  			uint8_t * buf = new uint8_t[l];
1023  			htobe16buf (buf, m_SessionID);
1024  			htobe32buf (buf + 2, requestID);
1025  			buf[6] = 0; // result code
1026  			identity->ToBuffer (buf + 7, l - 7);
1027  			SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, l);
1028  			delete[] buf;
1029  		}
1030  		else
1031  		{
1032  			uint8_t buf[7];
1033  			htobe16buf (buf, m_SessionID);
1034  			htobe32buf (buf + 2, requestID);
1035  			buf[6] = 1; // result code
1036  			SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, 7);
1037  		}
1038  	}
1039  
1040  	void I2CPSession::DestLookupMessageHandler (const uint8_t * buf, size_t len)
1041  	{
1042  		if (m_Destination)
1043  		{
1044  			auto ls = m_Destination->FindLeaseSet (buf);
1045  			if (ls)
1046  			{
1047  				auto l = ls->GetIdentity ()->GetFullLen ();
1048  				uint8_t * identBuf = new uint8_t[l];
1049  				ls->GetIdentity ()->ToBuffer (identBuf, l);
1050  				SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l);
1051  				delete[] identBuf;
1052  			}
1053  			else
1054  			{
1055  				auto s = shared_from_this ();
1056  				i2p::data::IdentHash ident (buf);
1057  				m_Destination->RequestDestination (ident,
1058  					[s, ident](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
1059  					{
1060  						if (leaseSet) // found
1061  						{
1062  							auto l = leaseSet->GetIdentity ()->GetFullLen ();
1063  							uint8_t * identBuf = new uint8_t[l];
1064  							leaseSet->GetIdentity ()->ToBuffer (identBuf, l);
1065  							s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l);
1066  							delete[] identBuf;
1067  						}
1068  						else
1069  							s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, ident, 32); // not found
1070  					});
1071  			}
1072  		}
1073  		else
1074  			SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, buf, 32);
1075  	}
1076  
1077  	void I2CPSession::GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len)
1078  	{
1079  		uint8_t limits[64];
1080  		memset (limits, 0, 64);
1081  		uint32_t limit; i2p::config::GetOption("i2cp.inboundlimit", limit);
1082  		if (!limit) limit = i2p::context.GetBandwidthLimit ();
1083  		htobe32buf (limits, limit); // inbound
1084  		i2p::config::GetOption("i2cp.outboundlimit", limit);
1085  		if (!limit) limit = i2p::context.GetBandwidthLimit ();
1086  		htobe32buf (limits + 4, limit); // outbound
1087  		SendI2CPMessage (I2CP_BANDWIDTH_LIMITS_MESSAGE, limits, 64);
1088  	}
1089  
1090  	void I2CPSession::SendMessagePayloadMessage (const uint8_t * payload, size_t len)
1091  	{
1092  		// we don't use SendI2CPMessage to eliminate additional copy
1093  		auto l = len + 10 + I2CP_HEADER_SIZE;
1094  		if (l > I2CP_MAX_MESSAGE_LENGTH)
1095  		{
1096  			LogPrint (eLogError, "I2CP: Message to send is too long ", l);
1097  			return;
1098  		}
1099  		auto sendBuf = m_IsSending ? std::make_shared<i2p::stream::SendBuffer> (l) : nullptr;
1100  		uint8_t * buf = sendBuf ? sendBuf->buf : m_SendBuffer;
1101  		htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len + 10);
1102  		buf[I2CP_HEADER_TYPE_OFFSET] = I2CP_MESSAGE_PAYLOAD_MESSAGE;
1103  		htobe16buf (buf + I2CP_HEADER_SIZE, m_SessionID);
1104  		htobe32buf (buf + I2CP_HEADER_SIZE + 2, m_MessageID++);
1105  		htobe32buf (buf + I2CP_HEADER_SIZE + 6, len);
1106  		memcpy (buf + I2CP_HEADER_SIZE + 10, payload, len);
1107  		if (sendBuf)
1108  		{
1109  			if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE)
1110  				m_SendQueue.Add (std::move(sendBuf));
1111  			else
1112  			{
1113  				LogPrint (eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE);
1114  				return;
1115  			}
1116  		}
1117  		else
1118  		{
1119  			auto socket = m_Socket;
1120  			if (socket)
1121  			{
1122  				m_IsSending = true;
1123  				boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l),
1124  					boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent,
1125  					shared_from_this (), std::placeholders::_1, std::placeholders::_2));
1126  			}
1127  		}
1128  	}
1129  
1130  	I2CPServer::I2CPServer (const std::string& interface, uint16_t port, bool isSingleThread):
1131  		RunnableService ("I2CP"), m_IsSingleThread (isSingleThread),
1132  		m_Acceptor (GetIOService (),
1133  		boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(interface), port))
1134  	{
1135  		memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers));
1136  		m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler;
1137  		m_MessagesHandlers[I2CP_CREATE_SESSION_MESSAGE] = &I2CPSession::CreateSessionMessageHandler;
1138  		m_MessagesHandlers[I2CP_DESTROY_SESSION_MESSAGE] = &I2CPSession::DestroySessionMessageHandler;
1139  		m_MessagesHandlers[I2CP_RECONFIGURE_SESSION_MESSAGE] = &I2CPSession::ReconfigureSessionMessageHandler;
1140  		m_MessagesHandlers[I2CP_CREATE_LEASESET_MESSAGE] = &I2CPSession::CreateLeaseSetMessageHandler;
1141  		m_MessagesHandlers[I2CP_CREATE_LEASESET2_MESSAGE] = &I2CPSession::CreateLeaseSet2MessageHandler;
1142  		m_MessagesHandlers[I2CP_SEND_MESSAGE_MESSAGE] = &I2CPSession::SendMessageMessageHandler;
1143  		m_MessagesHandlers[I2CP_SEND_MESSAGE_EXPIRES_MESSAGE] = &I2CPSession::SendMessageExpiresMessageHandler;
1144  		m_MessagesHandlers[I2CP_HOST_LOOKUP_MESSAGE] = &I2CPSession::HostLookupMessageHandler;
1145  		m_MessagesHandlers[I2CP_DEST_LOOKUP_MESSAGE] = &I2CPSession::DestLookupMessageHandler;
1146  		m_MessagesHandlers[I2CP_GET_BANDWIDTH_LIMITS_MESSAGE] = &I2CPSession::GetBandwidthLimitsMessageHandler;
1147  	}
1148  
1149  	I2CPServer::~I2CPServer ()
1150  	{
1151  		if (IsRunning ())
1152  			Stop ();
1153  	}
1154  
1155  	void I2CPServer::Start ()
1156  	{
1157  		Accept ();
1158  		StartIOService ();
1159  	}
1160  
1161  	void I2CPServer::Stop ()
1162  	{
1163  		m_Acceptor.cancel ();
1164  
1165  		decltype(m_Sessions) sessions;
1166  		m_Sessions.swap (sessions);
1167  		for (auto& it: sessions)
1168  			it.second->Stop ();
1169  
1170  		StopIOService ();
1171  	}
1172  
1173  	void I2CPServer::Accept ()
1174  	{
1175  		auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (GetIOService ());
1176  		m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this,
1177  			std::placeholders::_1, newSocket));
1178  	}
1179  
1180  	void I2CPServer::HandleAccept(const boost::system::error_code& ecode,
1181  		std::shared_ptr<boost::asio::ip::tcp::socket> socket)
1182  	{
1183  		if (!ecode && socket)
1184  		{
1185  			boost::system::error_code ec;
1186  			auto ep = socket->remote_endpoint (ec);
1187  			if (!ec)
1188  			{
1189  				LogPrint (eLogDebug, "I2CP: New connection from ", ep);
1190  				auto session = std::make_shared<I2CPSession>(*this, socket);
1191  				session->Start ();
1192  			}
1193  			else
1194  				LogPrint (eLogError, "I2CP: Incoming connection error ", ec.message ());
1195  		}
1196  		else
1197  			LogPrint (eLogError, "I2CP: Accept error: ", ecode.message ());
1198  
1199  		if (ecode != boost::asio::error::operation_aborted)
1200  			Accept ();
1201  	}
1202  
1203  	bool I2CPServer::InsertSession (std::shared_ptr<I2CPSession> session)
1204  	{
1205  		if (!session) return false;
1206  		if (!m_Sessions.insert({session->GetSessionID (), session}).second)
1207  		{
1208  			LogPrint (eLogError, "I2CP: Duplicate session id ", session->GetSessionID ());
1209  			return false;
1210  		}
1211  		return true;
1212  	}
1213  
1214  	void I2CPServer::RemoveSession (uint16_t sessionID)
1215  	{
1216  		m_Sessions.erase (sessionID);
1217  	}
1218  
1219  	std::shared_ptr<I2CPSession> I2CPServer::FindSessionByIdentHash (const i2p::data::IdentHash& ident) const
1220  	{
1221  		for (const auto& it: m_Sessions)
1222  		{
1223  			if (it.second)
1224  			{
1225  				auto dest = it.second->GetDestination ();
1226  				if (dest && dest->GetIdentHash () == ident)
1227  					return it.second;
1228  			}
1229  		}
1230  		return nullptr;
1231  	}
1232  }
1233  }