/ libi2pd / TransitTunnel.cpp
TransitTunnel.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 "I2PEndian.h"
 11  #include "Crypto.h"
 12  #include "Log.h"
 13  #include "Identity.h"
 14  #include "RouterInfo.h"
 15  #include "RouterContext.h"
 16  #include "I2NPProtocol.h"
 17  #include "Garlic.h"
 18  #include "ECIESX25519AEADRatchetSession.h"
 19  #include "Tunnel.h"
 20  #include "Transports.h"
 21  #include "TransitTunnel.h"
 22  
 23  namespace i2p
 24  {
 25  namespace tunnel
 26  {
 27  	TransitTunnel::TransitTunnel (uint32_t receiveTunnelID,
 28  		const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
 29  		const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey):
 30  			TunnelBase (receiveTunnelID, nextTunnelID, nextIdent),
 31  			m_LayerKey (layerKey), m_IVKey (ivKey)
 32  	{
 33  	}
 34  
 35  	void TransitTunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
 36  	{
 37  		if (!m_Encryption)
 38  		{
 39  			m_Encryption.reset (new i2p::crypto::TunnelEncryption);
 40  			m_Encryption->SetKeys (m_LayerKey, m_IVKey);
 41  		}
 42  		m_Encryption->Encrypt (in->GetPayload () + 4, out->GetPayload () + 4);
 43  		i2p::transport::transports.UpdateTotalTransitTransmittedBytes (TUNNEL_DATA_MSG_SIZE);
 44  	}
 45  
 46  	std::string TransitTunnel::GetNextPeerName () const
 47  	{
 48  		return i2p::data::GetIdentHashAbbreviation (GetNextIdentHash ());
 49  	}
 50  
 51  	void TransitTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
 52  	{
 53  		LogPrint (eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID ());
 54  	}
 55  
 56  	void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg)
 57  	{
 58  		LogPrint (eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID ());
 59  	}
 60  
 61  	TransitTunnelParticipant::~TransitTunnelParticipant ()
 62  	{
 63  	}
 64  
 65  	void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg)
 66  	{
 67  		EncryptTunnelMsg (tunnelMsg, tunnelMsg);
 68  
 69  		m_NumTransmittedBytes += tunnelMsg->GetLength ();
 70  		htobe32buf (tunnelMsg->GetPayload (), GetNextTunnelID ());
 71  		// update header, expiration and size remain the same
 72  		tunnelMsg->SetMsgID (i2p::tunnel::tunnels.GetRng ()()); // assign new msgID
 73  		tunnelMsg->UpdateChks (); // new checksum TODO: remove later
 74  		m_TunnelDataMsgs.push_back (tunnelMsg);
 75  	}
 76  
 77  	void TransitTunnelParticipant::FlushTunnelDataMsgs ()
 78  	{
 79  		if (!m_TunnelDataMsgs.empty ())
 80  		{
 81  			auto num = m_TunnelDataMsgs.size ();
 82  			if (num > 1)
 83  				LogPrint (eLogDebug, "TransitTunnel: ", GetTunnelID (), "->", GetNextTunnelID (), " ", num);
 84  			if (!m_Sender) m_Sender = std::make_unique<TunnelTransportSender>();
 85  			m_Sender->SendMessagesTo (GetNextIdentHash (), m_TunnelDataMsgs); // send and clear
 86  		}
 87  	}
 88  
 89  	std::string TransitTunnelParticipant::GetNextPeerName () const
 90  	{
 91  		if (m_Sender)
 92  		{
 93  			auto transport = m_Sender->GetCurrentTransport ();
 94  			if (transport)
 95  				return TransitTunnel::GetNextPeerName () + "-" +
 96  					i2p::data::RouterInfo::GetTransportName (transport->GetTransportType ());
 97  		}
 98  		return TransitTunnel::GetNextPeerName ();
 99  	}
100  
101  	void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
102  	{
103  		TunnelMessageBlock block;
104  		block.deliveryType = eDeliveryTypeLocal;
105  		block.data = msg;
106  		std::lock_guard<std::mutex> l(m_SendMutex);
107  		m_Gateway.PutTunnelDataMsg (block);
108  	}
109  
110  	void TransitTunnelGateway::FlushTunnelDataMsgs ()
111  	{
112  		std::lock_guard<std::mutex> l(m_SendMutex);
113  		m_Gateway.SendBuffer ();
114  	}
115  
116  	std::string TransitTunnelGateway::GetNextPeerName () const
117  	{
118  		const auto& sender = m_Gateway.GetSender ();
119  		if (sender)
120  		{
121  			auto transport = sender->GetCurrentTransport ();
122  			if (transport)
123  				return TransitTunnel::GetNextPeerName () + "-" +
124  					i2p::data::RouterInfo::GetTransportName (transport->GetTransportType ());
125  		}
126  		return TransitTunnel::GetNextPeerName ();
127  	}
128  
129  	void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg)
130  	{
131  		auto newMsg = CreateEmptyTunnelDataMsg (true);
132  		EncryptTunnelMsg (tunnelMsg, newMsg);
133  
134  		LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ());
135  		std::lock_guard<std::mutex> l(m_HandleMutex);
136  		if (!m_Endpoint) m_Endpoint = std::make_unique<TunnelEndpoint>(false); // transit endpoint is always outbound
137  		m_Endpoint->HandleDecryptedTunnelDataMsg (newMsg);
138  	}
139  
140  	void TransitTunnelEndpoint::FlushTunnelDataMsgs ()
141  	{
142  		if (m_Endpoint)
143  		{
144  			std::lock_guard<std::mutex> l(m_HandleMutex);
145  			m_Endpoint->FlushI2NPMsgs ();
146  		}
147  	}
148  
149  	void TransitTunnelEndpoint::Cleanup ()
150  	{
151  		if (m_Endpoint)
152  		{
153  			std::lock_guard<std::mutex> l(m_HandleMutex);
154  			m_Endpoint->Cleanup ();
155  		}
156  	}
157  
158  	std::string TransitTunnelEndpoint::GetNextPeerName () const
159  	{
160  		if (!m_Endpoint) return "";
161  		auto hash = m_Endpoint->GetCurrentHash ();
162  		if (hash)
163  		{
164  			const auto& sender = m_Endpoint->GetSender ();
165  			if (sender)
166  			{
167  				auto transport = sender->GetCurrentTransport ();
168  				if (transport)
169  					return i2p::data::GetIdentHashAbbreviation (*hash) + "-" +
170  						i2p::data::RouterInfo::GetTransportName (transport->GetTransportType ());
171  				else
172  					return i2p::data::GetIdentHashAbbreviation (*hash);
173  			}
174  		}
175  		return "";
176  	}
177  
178  	std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID,
179  		const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
180  		const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey,
181  		bool isGateway, bool isEndpoint)
182  	{
183  		if (isEndpoint)
184  		{
185  			LogPrint (eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created");
186  			return std::make_shared<TransitTunnelEndpoint> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
187  		}
188  		else if (isGateway)
189  		{
190  			LogPrint (eLogInfo, "TransitTunnel: gateway ", receiveTunnelID, " created");
191  			return std::make_shared<TransitTunnelGateway> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
192  		}
193  		else
194  		{
195  			LogPrint (eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
196  			return std::make_shared<TransitTunnelParticipant> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
197  		}
198  	}
199  
200  	TransitTunnels::TransitTunnels ():
201  		m_IsRunning (false), m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL)
202  	{
203  	}
204  
205  	TransitTunnels::~TransitTunnels ()
206  	{
207  		Stop ();
208  	}
209  
210  	void TransitTunnels::Start ()
211  	{
212  		m_IsRunning = true;
213  		m_Thread.reset (new std::thread (std::bind (&TransitTunnels::Run, this)));
214  	}
215  
216  	void TransitTunnels::Stop ()
217  	{
218  		m_IsRunning = false;
219  		m_TunnelBuildMsgQueue.WakeUp ();
220  		if (m_Thread)
221  		{
222  			m_Thread->join ();
223  			m_Thread = nullptr;
224  		}
225  		m_TransitTunnels.clear ();
226  	}
227  
228  	void TransitTunnels::Run ()
229  	{
230  		i2p::util::SetThreadName("TBM");
231  		uint64_t lastTs = 0;
232  		std::list<std::shared_ptr<I2NPMessage> > msgs;
233  		while (m_IsRunning)
234  		{
235  			try
236  			{
237  				if (m_TunnelBuildMsgQueue.Wait (TRANSIT_TUNNELS_QUEUE_WAIT_INTERVAL, 0))
238  				{
239  					m_TunnelBuildMsgQueue.GetWholeQueue (msgs);
240  					while (!msgs.empty ())
241  					{
242  						auto msg = msgs.front (); msgs.pop_front ();
243  						if (!msg) continue;
244  						uint8_t typeID = msg->GetTypeID ();
245  						switch (typeID)
246  						{
247  							case eI2NPShortTunnelBuild:
248  								HandleShortTransitTunnelBuildMsg (std::move (msg));
249  							break;
250  							case eI2NPVariableTunnelBuild:
251  								HandleVariableTransitTunnelBuildMsg (std::move (msg));
252  							break;
253  							default:
254  								LogPrint (eLogWarning, "TransitTunnel: Unexpected message type ", (int) typeID);
255  						}
256  						if (!m_IsRunning) break;
257  					}
258  				}
259  				if (m_IsRunning)
260  				{
261  					uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
262  					if (ts  >= lastTs + TUNNEL_MANAGE_INTERVAL || ts + TUNNEL_MANAGE_INTERVAL < lastTs)
263  					{
264  						ManageTransitTunnels (ts);
265  						lastTs = ts;
266  					}
267  				}
268  			}
269  			catch (std::exception& ex)
270  			{
271  				LogPrint (eLogError, "TransitTunnel: Runtime exception: ", ex.what ());
272  			}
273  		}
274  	}
275  
276  	void TransitTunnels::PostTransitTunnelBuildMsg  (std::shared_ptr<I2NPMessage>&& msg)
277  	{
278  		if (msg)
279  		{
280  			if (m_TunnelBuildMsgQueue.GetSize () < TRANSIT_TUNNELS_BUILD_MSG_QUEUE_MAX_SIZE)
281  				m_TunnelBuildMsgQueue.Put (msg);
282  			else
283  				m_TunnelBuildMsgQueue.WakeUp (); // tell TBM thread to process queue
284  		}
285  	}
286  
287  	void TransitTunnels::HandleShortTransitTunnelBuildMsg (std::shared_ptr<I2NPMessage>&& msg)
288  	{
289  		if (!msg) return;
290  		uint8_t * buf = msg->GetPayload();
291  		size_t len = msg->GetPayloadLength();
292  		int num = buf[0];
293  		LogPrint (eLogDebug, "TransitTunnel: ShortTunnelBuild ", num, " records");
294  		if (num > i2p::tunnel::MAX_NUM_RECORDS)
295  		{
296  			LogPrint (eLogError, "TransitTunnel: Too many records in ShortTunnelBuild message ", num);
297  			return;
298  		}
299  		if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1)
300  		{
301  			LogPrint (eLogError, "TransitTunnel: ShortTunnelBuild message of ", num, " records is too short ", len);
302  			return;
303  		}
304  		const uint8_t * record = buf + 1;
305  		for (int i = 0; i < num; i++)
306  		{
307  			if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
308  			{
309  				LogPrint (eLogDebug, "TransitTunnel: Short request record ", i, " is ours");
310  				uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE];
311  				if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
312  				{
313  					LogPrint (eLogWarning, "TransitTunnel: Can't decrypt short request record ", i);
314  					return;
315  				}
316  				if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES
317  				{
318  					LogPrint (eLogWarning, "TransitTunnel: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record");
319  					return;
320  				}
321  				auto& noiseState = i2p::context.GetCurrentNoiseState ();
322  				uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305
323  				i2p::crypto::AESKey layerKey, ivKey; // AES
324  				i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK);
325  				memcpy (replyKey, noiseState.m_CK + 32, 32);
326  				i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK);
327  				memcpy (layerKey, noiseState.m_CK + 32, 32);
328  				bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
329  				if (isEndpoint)
330  				{
331  					i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK);
332  					memcpy (ivKey, noiseState.m_CK + 32, 32);
333  				}
334  				else
335  				{
336  					if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours
337  					{
338  						LogPrint (eLogWarning, "TransitTunnel: Next ident is ours in short request record");
339  						return;
340  					}
341  					memcpy (ivKey, noiseState.m_CK , 32);
342  				}
343  
344  				// check if we accept this tunnel
345  				std::shared_ptr<i2p::tunnel::TransitTunnel> transitTunnel;
346  				uint8_t retCode = 0;
347  				if (i2p::context.AcceptsTunnels () && !i2p::context.IsLimitedConnectivity ())
348  				{
349  					auto congestionLevel = i2p::context.GetCongestionLevel (false);
350  					if (congestionLevel < CONGESTION_LEVEL_FULL)
351  					{
352  						if (congestionLevel >= CONGESTION_LEVEL_MEDIUM)
353  						{
354  							// random reject depending on congestion level
355  							int level = m_Rng () % (CONGESTION_LEVEL_FULL - CONGESTION_LEVEL_MEDIUM) + CONGESTION_LEVEL_MEDIUM;
356  							if (congestionLevel > level)
357  								retCode = 30;
358  						}
359  					}
360  					else
361  						retCode = 30;
362  				}
363  				else
364  					retCode = 30;
365  
366  				if (!retCode)
367  				{
368  					i2p::data::IdentHash nextIdent(clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET);
369  					bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
370  					if (isEndpoint || !i2p::data::IsRouterDuplicated (nextIdent))
371  					{
372  						// create new transit tunnel
373  						transitTunnel = CreateTransitTunnel (
374  							bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
375  							nextIdent,
376  							bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
377  							layerKey, ivKey,
378  							clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
379  							isEndpoint);
380  						if (!AddTransitTunnel (transitTunnel))
381  							retCode = 30;
382  					}
383  					else
384  						// decline tunnel going to duplicated router
385  						retCode = 30;
386  				}
387  
388  				// encrypt reply
389  				uint8_t nonce[12];
390  				memset (nonce, 0, 12);
391  				uint8_t * reply = buf + 1;
392  				for (int j = 0; j < num; j++)
393  				{
394  					nonce[4] = j; // nonce is record #
395  					if (j == i)
396  					{
397  						memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
398  						reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode;
399  						if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16,
400  							noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
401  						{
402  							LogPrint (eLogWarning, "TransitTunnel: Short reply AEAD encryption failed");
403  							return;
404  						}
405  					}
406  					else
407  						i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply);
408  					reply += SHORT_TUNNEL_BUILD_RECORD_SIZE;
409  				}
410  				// send reply
411  				auto onDrop = [transitTunnel]()
412  					{
413  						if (transitTunnel)
414  						{
415  							LogPrint (eLogDebug, "TransitTunnel: Failed to send reply for transit tunnel ", transitTunnel->GetTunnelID ());
416  							auto t = transitTunnel->GetCreationTime ();
417  							if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT)
418  								// make transit tunnel expired
419  								transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT);
420  						}
421  					};
422  				if (isEndpoint)
423  				{
424  					auto replyMsg = NewI2NPShortMessage ();
425  					replyMsg->Concat (buf, len);
426  					replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
427  					if (transitTunnel) replyMsg->onDrop = onDrop;
428  					if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (),
429  						clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local?
430  					{
431  						i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK);
432  						uint64_t tag;
433  						memcpy (&tag, noiseState.m_CK, 8);
434  						// we send it to reply tunnel
435  						i2p::transport::transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
436  						CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
437  							i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag)));
438  					}
439  					else
440  					{
441  						// IBGW is local
442  						uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET);
443  						auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID);
444  						if (tunnel)
445  						{
446  							tunnel->SendTunnelDataMsg (replyMsg);
447  							tunnel->FlushTunnelDataMsgs ();
448  						}
449  						else
450  							LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply");
451  					}
452  				}
453  				else
454  				{
455  					auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len,
456  							bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
457  					if (transitTunnel) msg->onDrop = onDrop;
458  					i2p::transport::transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg);
459  				}
460  				return;
461  			}
462  			record += SHORT_TUNNEL_BUILD_RECORD_SIZE;
463  		}
464  	}
465  
466  	bool TransitTunnels::HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
467  	{
468  		for (int i = 0; i < num; i++)
469  		{
470  			uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE;
471  			if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
472  			{
473  				LogPrint (eLogDebug, "TransitTunnel: Build request record ", i, " is ours");
474  				if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
475  				{
476  					LogPrint (eLogWarning, "TransitTunnel: Failed to decrypt tunnel build record");
477  					return false;
478  				}
479  				if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours
480  				    !(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint
481  				{
482  					LogPrint (eLogWarning, "TransitTunnel: Next ident is ours in tunnel build record");
483  					return false;
484  				}
485  				uint8_t retCode = 0;
486  				// decide if we should accept tunnel
487  				bool accept = i2p::context.AcceptsTunnels () && !i2p::context.IsLimitedConnectivity ();
488  				if (accept)
489  				{
490  					auto congestionLevel = i2p::context.GetCongestionLevel (false);
491  					if (congestionLevel >= CONGESTION_LEVEL_MEDIUM)
492  					{
493  						if (congestionLevel < CONGESTION_LEVEL_FULL)
494  						{
495  							// random reject depending on congestion level
496  							int level = m_Rng () % (CONGESTION_LEVEL_FULL - CONGESTION_LEVEL_MEDIUM) + CONGESTION_LEVEL_MEDIUM;
497  							if (congestionLevel > level)
498  								accept = false;
499  						}
500  						else
501  							accept = false;
502  					}
503  				}
504  
505  				if (accept)
506  				{
507  					i2p::data::IdentHash nextIdent(clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET);
508  					bool isEndpoint = clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
509  					if (isEndpoint || !i2p::data::IsRouterDuplicated (nextIdent))
510  					{
511  						auto transitTunnel = CreateTransitTunnel (
512  								bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
513  								nextIdent,
514  								bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
515  								clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
516  								clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
517  								clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
518  								isEndpoint);
519  						if (!AddTransitTunnel (transitTunnel))
520  							retCode = 30;
521  					}
522  					else
523  						// decline tunnel going to duplicated router
524  						retCode = 30;
525  				}
526  				else
527  					retCode = 30; // always reject with bandwidth reason (30)
528  
529  				// replace record to reply
530  				memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
531  				record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
532  				// encrypt reply
533  				i2p::crypto::CBCEncryption encryption;
534  				for (int j = 0; j < num; j++)
535  				{
536  					uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
537  					if (j == i)
538  					{
539  						uint8_t nonce[12];
540  						memset (nonce, 0, 12);
541  						auto& noiseState = i2p::context.GetCurrentNoiseState ();
542  						if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16,
543  							noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
544  						{
545  							LogPrint (eLogWarning, "TransitTunnel: Reply AEAD encryption failed");
546  							return false;
547  						}
548  					}
549  					else
550  					{
551  						encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
552  						encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, reply);
553  					}
554  				}
555  				return true;
556  			}
557  		}
558  		return false;
559  	}
560  
561  	void TransitTunnels::HandleVariableTransitTunnelBuildMsg (std::shared_ptr<I2NPMessage>&& msg)
562  	{
563  		if (!msg) return;
564  		uint8_t * buf = msg->GetPayload();
565  		size_t len = msg->GetPayloadLength();
566  		int num = buf[0];
567  		LogPrint (eLogDebug, "TransitTunnel: VariableTunnelBuild ", num, " records");
568  		if (num > i2p::tunnel::MAX_NUM_RECORDS)
569  		{
570  			LogPrint (eLogError, "TransitTunnle: Too many records in VaribleTunnelBuild message ", num);
571  			return;
572  		}
573  		if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1)
574  		{
575  			LogPrint (eLogError, "TransitTunnel: VaribleTunnelBuild message of ", num, " records is too short ", len);
576  			return;
577  		}
578  		uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
579  		if (HandleBuildRequestRecords (num, buf + 1, clearText))
580  		{
581  			if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
582  			{
583  				// so we send it to reply tunnel
584  				i2p::transport::transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
585  					CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
586  						eI2NPVariableTunnelBuildReply, buf, len,
587  						bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
588  			}
589  			else
590  				i2p::transport::transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
591  					CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
592  						bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
593  		}
594  	}
595  
596  	bool TransitTunnels::AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel)
597  	{
598  		if (tunnels.AddTunnel (tunnel))
599  			m_TransitTunnels.push_back (tunnel);
600  		else
601  		{
602  			LogPrint (eLogError, "TransitTunnel: Tunnel with id ", tunnel->GetTunnelID (), " already exists");
603  			return false;
604  		}
605  		return true;
606  	}
607  
608  	void TransitTunnels::ManageTransitTunnels (uint64_t ts)
609  	{
610  		for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();)
611  		{
612  			auto tunnel = *it;
613  			if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT ||
614  			    ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime () ||
615  			    (!tunnel->GetNumTransmittedBytes () && ts > tunnel->GetCreationTime () + 2*TUNNEL_EXPIRATION_THRESHOLD)) // inactive?
616  			{
617  				LogPrint (eLogDebug, "TransitTunnel: Transit tunnel with id ", tunnel->GetTunnelID (), " expired or inactive");
618  				tunnels.RemoveTunnel (tunnel->GetTunnelID ());
619  				it = m_TransitTunnels.erase (it);
620  			}
621  			else
622  			{
623  				tunnel->Cleanup ();
624  				it++;
625  			}
626  		}
627  	}
628  
629  	int TransitTunnels::GetTransitTunnelsExpirationTimeout ()
630  	{
631  		int timeout = 0;
632  		uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
633  		// TODO: possible race condition with I2PControl
634  		for (const auto& it : m_TransitTunnels)
635  		{
636  			int t = it->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts;
637  			if (t > timeout) timeout = t;
638  		}
639  		return timeout;
640  	}
641  }
642  }