/ libi2pd / TunnelPool.cpp
TunnelPool.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 "I2PEndian.h"
 11  #include "Crypto.h"
 12  #include "Tunnel.h"
 13  #include "NetDb.hpp"
 14  #include "Timestamp.h"
 15  #include "Garlic.h"
 16  #include "ECIESX25519AEADRatchetSession.h"
 17  #include "Transports.h"
 18  #include "Log.h"
 19  #include "Tunnel.h"
 20  #include "TunnelPool.h"
 21  #include "Destination.h"
 22  
 23  namespace i2p
 24  {
 25  namespace tunnel
 26  {
 27  	void Path::Add (std::shared_ptr<const i2p::data::RouterInfo> r)
 28  	{
 29  		if (r)
 30  		{
 31  			peers.push_back (r->GetRouterIdentity ());
 32  			if (r->GetVersion () < i2p::data::NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION ||
 33  				r->GetRouterIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
 34  				isShort = false;
 35  		}
 36  	}
 37  
 38  	void Path::Reverse ()
 39  	{
 40  		std::reverse (peers.begin (), peers.end ());
 41  	}
 42  
 43  	TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels,
 44  		int numOutboundTunnels, int inboundVariance, int outboundVariance, bool isHighBandwidth):
 45  		m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
 46  		m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels),
 47  		m_InboundVariance (inboundVariance), m_OutboundVariance (outboundVariance),
 48  		m_IsActive (true), m_IsHighBandwidth (isHighBandwidth), m_CustomPeerSelector(nullptr)
 49  	{
 50  		if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY)
 51  			m_NumInboundTunnels = TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY;
 52  		if (m_NumOutboundTunnels > TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY)
 53  			m_NumOutboundTunnels = TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY;
 54  		if (m_InboundVariance < 0 && m_NumInboundHops + m_InboundVariance <= 0)
 55  			m_InboundVariance = m_NumInboundHops ? -m_NumInboundHops + 1 : 0;
 56  		if (m_OutboundVariance < 0 && m_NumOutboundHops + m_OutboundVariance <= 0)
 57  			m_OutboundVariance = m_NumOutboundHops ? -m_NumOutboundHops + 1 : 0;
 58  		if (m_InboundVariance > 0 && m_NumInboundHops + m_InboundVariance > STANDARD_NUM_RECORDS)
 59  			m_InboundVariance = (m_NumInboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumInboundHops : 0;
 60  		if (m_OutboundVariance > 0 && m_NumOutboundHops + m_OutboundVariance > STANDARD_NUM_RECORDS)
 61  			m_OutboundVariance = (m_NumOutboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumOutboundHops : 0;
 62  		m_NextManageTime = i2p::util::GetSecondsSinceEpoch () + rand () % TUNNEL_POOL_MANAGE_INTERVAL;
 63  	}
 64  
 65  	TunnelPool::~TunnelPool ()
 66  	{
 67  		DetachTunnels ();
 68  	}
 69  
 70  	void TunnelPool::SetExplicitPeers (std::vector<i2p::data::IdentHash> explicitPeers)
 71  	{
 72  		m_ExplicitPeers.swap (explicitPeers);
 73  		int size = m_ExplicitPeers.size ();
 74  		if (size > 0)
 75  		{
 76  			if (m_NumInboundHops > size)
 77  			{
 78  				m_NumInboundHops = size;
 79  				LogPrint (eLogInfo, "Tunnels: Inbound tunnel length has been adjusted to ", size, " for explicit peers");
 80  			}
 81  			if (m_NumOutboundHops > size)
 82  			{
 83  				m_NumOutboundHops = size;
 84  				LogPrint (eLogInfo, "Tunnels: Outbound tunnel length has been adjusted to ", size, " for explicit peers");
 85  			}
 86  			m_NumInboundTunnels = 1;
 87  			m_NumOutboundTunnels = 1;
 88  		}
 89  	}
 90  
 91  	void TunnelPool::SetTrustedRouters (std::vector<i2p::data::IdentHash> routers)
 92  	{
 93  		m_TrustedRouters.swap (routers);
 94  	}
 95  
 96  	void TunnelPool::DetachTunnels ()
 97  	{
 98  		{
 99  			std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
100  			for (auto& it: m_InboundTunnels)
101  				it->SetTunnelPool (nullptr);
102  			m_InboundTunnels.clear ();
103  		}
104  		{
105  			std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
106  			for (auto& it: m_OutboundTunnels)
107  				it->SetTunnelPool (nullptr);
108  			m_OutboundTunnels.clear ();
109  		}
110  		{
111  			std::unique_lock<std::mutex> l(m_TestsMutex);
112  			m_Tests.clear ();
113  		}
114  	}
115  
116  	bool TunnelPool::Reconfigure(int inHops, int outHops, int inQuant, int outQuant)
117  	{
118  		if( inHops >= 0 && outHops >= 0 && inQuant > 0 && outQuant > 0)
119  		{
120  			m_NumInboundHops = inHops;
121  			m_NumOutboundHops = outHops;
122  			m_NumInboundTunnels = inQuant;
123  			m_NumOutboundTunnels = outQuant;
124  			return true;
125  		}
126  		return false;
127  	}
128  
129  	void TunnelPool::TunnelCreated (std::shared_ptr<InboundTunnel> createdTunnel)
130  	{
131  		if (!m_IsActive) return;
132  		{
133  			std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
134  			if (createdTunnel->IsRecreated ())
135  			{
136  				// find and mark old tunnel as expired
137  				createdTunnel->SetRecreated (false);
138  				for (auto& it: m_InboundTunnels)
139  					if (it->IsRecreated () && it->GetNextIdentHash () == createdTunnel->GetNextIdentHash ())
140  					{
141  						it->SetState (eTunnelStateExpiring);
142  						break;
143  					}
144  			}
145  			m_InboundTunnels.insert (createdTunnel);
146  		}
147  		if (m_LocalDestination)
148  			m_LocalDestination->SetLeaseSetUpdated (true);
149  	}
150  
151  	void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
152  	{
153  		if (expiredTunnel)
154  		{
155  			expiredTunnel->SetTunnelPool (nullptr);
156  			{
157  				std::unique_lock<std::mutex> l(m_TestsMutex);
158  				for (auto& it: m_Tests)
159  					if (it.second.second == expiredTunnel) it.second.second = nullptr;
160  			}
161  
162  			std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
163  			m_InboundTunnels.erase (expiredTunnel);
164  		}
165  	}
166  
167  	void TunnelPool::TunnelCreated (std::shared_ptr<OutboundTunnel> createdTunnel)
168  	{
169  		if (!m_IsActive) return;
170  		{
171  			std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
172  			m_OutboundTunnels.insert (createdTunnel);
173  		}
174  	}
175  
176  	void TunnelPool::TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel)
177  	{
178  		if (expiredTunnel)
179  		{
180  			expiredTunnel->SetTunnelPool (nullptr);
181  			{
182  				std::unique_lock<std::mutex> l(m_TestsMutex);
183  				for (auto& it: m_Tests)
184  					if (it.second.first == expiredTunnel) it.second.first = nullptr;
185  			}
186  
187  			std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
188  			m_OutboundTunnels.erase (expiredTunnel);
189  		}
190  	}
191  
192  	std::vector<std::shared_ptr<InboundTunnel> > TunnelPool::GetInboundTunnels (int num) const
193  	{
194  		std::vector<std::shared_ptr<InboundTunnel> > v;
195  		int i = 0;
196  		std::shared_ptr<InboundTunnel> slowTunnel;
197  		std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
198  		for (const auto& it : m_InboundTunnels)
199  		{
200  			if (i >= num) break;
201  			if (it->IsEstablished ())
202  			{
203  				if (it->IsSlow () && !slowTunnel)
204  					slowTunnel = it;
205  				else
206  				{
207  					v.push_back (it);
208  					i++;
209  				}
210  			}
211  		}
212  		if (slowTunnel && (int)v.size () < (num/2+1))
213  			v.push_back (slowTunnel);
214  		return v;
215  	}
216  
217  	std::shared_ptr<OutboundTunnel> TunnelPool::GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded,
218  		i2p::data::RouterInfo::CompatibleTransports compatible)
219  	{
220  		std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
221  		return GetNextTunnel (m_OutboundTunnels, excluded, compatible);
222  	}
223  
224  	std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded,
225  		i2p::data::RouterInfo::CompatibleTransports compatible)
226  	{
227  		std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
228  		return GetNextTunnel (m_InboundTunnels, excluded, compatible);
229  	}
230  
231  	template<class TTunnels>
232  	typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels,
233  		typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible)
234  	{
235  		if (tunnels.empty ()) return nullptr;
236  		uint32_t ind = (m_LocalDestination ? m_LocalDestination->GetRng ()() : rand ()) % (tunnels.size ()/2 + 1), i = 0;
237  		bool skipped = false;
238  		typename TTunnels::value_type tunnel = nullptr;
239  		for (const auto& it: tunnels)
240  		{
241  			if (it->IsEstablished () && it != excluded && (compatible & it->GetFarEndTransports ()))
242  			{
243  				if (it->IsSlow () || (HasLatencyRequirement() && it->LatencyIsKnown() &&
244  					!it->LatencyFitsRange(m_MinLatency, m_MaxLatency)))
245  				{
246  					i++; skipped = true;
247  					continue;
248  				}
249  				tunnel = it;
250  				i++;
251  			}
252  			if (i > ind && tunnel) break;
253  		}
254  		if (!tunnel && skipped)
255  		{
256  			ind = (m_LocalDestination ? m_LocalDestination->GetRng ()() : rand ()) % (tunnels.size ()/2 + 1), i = 0;
257  			for (const auto& it: tunnels)
258  			{
259  				if (it->IsEstablished () && it != excluded)
260  				{
261  					tunnel = it;
262  					i++;
263  				}
264  				if (i > ind && tunnel) break;
265  			}
266  		}
267  		if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded;
268  		return tunnel;
269  	}
270  
271  	std::pair<std::shared_ptr<OutboundTunnel>, bool> TunnelPool::GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old)
272  	{
273  		if (old && old->IsEstablished ()) return std::make_pair(old, false);
274  		std::shared_ptr<OutboundTunnel> tunnel;
275  		bool freshTunnel = false;
276  		if (old)
277  		{
278  			std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
279  			for (const auto& it: m_OutboundTunnels)
280  				if (it->IsEstablished () && old->GetEndpointIdentHash () == it->GetEndpointIdentHash ())
281  				{
282  					tunnel = it;
283  					break;
284  				}
285  		}
286  
287  		if (!tunnel)
288  		{
289  			tunnel = GetNextOutboundTunnel ();
290  			freshTunnel = true;
291  		}
292  		return std::make_pair(tunnel, freshTunnel);
293  	}
294  
295  	void TunnelPool::CreateTunnels ()
296  	{
297  		int num = 0;
298  		{
299  			std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
300  			for (const auto& it : m_OutboundTunnels)
301  				if (it->IsEstablished ()) num++;
302  		}
303  		num = m_NumOutboundTunnels - num;
304  		if (num > 0)
305  		{
306  			if (num > TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS) num = TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS;
307  			for (int i = 0; i < num; i++)
308  				CreateOutboundTunnel ();
309  		}
310  
311  		num = 0;
312  		{
313  			std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
314  			for (const auto& it : m_InboundTunnels)
315  				if (it->IsEstablished ()) num++;
316  		}
317  		if (!num && !m_OutboundTunnels.empty () && m_NumOutboundHops > 0 &&
318  		    m_NumInboundHops == m_NumOutboundHops)
319  		{
320  			for (auto it: m_OutboundTunnels)
321  			{
322  				// try to create inbound tunnel through the same path as successive outbound
323  				CreatePairedInboundTunnel (it);
324  				num++;
325  				if (num >= m_NumInboundTunnels) break;
326  			}
327  		}
328  		num = m_NumInboundTunnels - num;
329  		if (num > 0)
330  		{
331  			if (num > TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS) num = TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS;
332  			for (int i = 0; i < num; i++)
333  				CreateInboundTunnel ();
334  		}
335  
336  		if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB
337  			m_LocalDestination->SetLeaseSetUpdated (true); // update LeaseSet immediately
338  	}
339  
340  	void TunnelPool::TestTunnels ()
341  	{
342  		decltype(m_Tests) tests;
343  		{
344  			std::unique_lock<std::mutex> l(m_TestsMutex);
345  			tests.swap(m_Tests);
346  		}
347  
348  		for (auto& it: tests)
349  		{
350  			LogPrint (eLogWarning, "Tunnels: Test of tunnel ", it.first, " failed");
351  			// if test failed again with another tunnel we consider it failed
352  			if (it.second.first)
353  			{
354  				if (it.second.first->GetState () == eTunnelStateTestFailed)
355  				{
356  					it.second.first->SetState (eTunnelStateFailed);
357  					std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
358  					if (m_OutboundTunnels.size () > 1) // don't fail last tunnel
359  						m_OutboundTunnels.erase (it.second.first);
360  					else
361  					{
362  						it.second.first->SetState (eTunnelStateTestFailed);
363  						CreateOutboundTunnel (); // create new tunnel immediately because last one failed
364  					}
365  				}
366  				else if (it.second.first->GetState () != eTunnelStateExpiring)
367  					it.second.first->SetState (eTunnelStateTestFailed);
368  			}
369  			if (it.second.second)
370  			{
371  				if (it.second.second->GetState () == eTunnelStateTestFailed)
372  				{
373  					it.second.second->SetState (eTunnelStateFailed);
374  					{
375  						bool failed = false;
376  						{
377  							std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
378  							if (m_InboundTunnels.size () > 1) // don't fail last tunnel
379  							{
380  								m_InboundTunnels.erase (it.second.second);
381  								failed = true;
382  							}
383  							else
384  							{
385  								it.second.second->SetState (eTunnelStateTestFailed);
386  								CreateInboundTunnel (); // create new tunnel immediately because last one failed
387  							}
388  						}
389  						if (failed && m_LocalDestination)
390  							m_LocalDestination->SetLeaseSetUpdated (true);
391  					}
392  					if (m_LocalDestination)
393  						m_LocalDestination->SetLeaseSetUpdated (true);
394  				}
395  				else if (it.second.second->GetState () != eTunnelStateExpiring)
396  					it.second.second->SetState (eTunnelStateTestFailed);
397  			}
398  		}
399  
400  		// new tests
401  		if (!m_LocalDestination) return;
402  		std::vector<std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > newTests;
403  		std::vector<std::shared_ptr<OutboundTunnel> > outboundTunnels;
404  		{
405  			std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
406  			for (auto& it: m_OutboundTunnels)
407  				if (it->IsEstablished ())
408  					outboundTunnels.push_back (it);
409  		}
410  		std::shuffle (outboundTunnels.begin(), outboundTunnels.end(), tunnels.GetRng ());
411  		std::vector<std::shared_ptr<InboundTunnel> > inboundTunnels;
412  		{
413  			std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
414  			for (auto& it: m_InboundTunnels)
415  				if (it->IsEstablished ())
416  					inboundTunnels.push_back (it);
417  		}
418  		std::shuffle (inboundTunnels.begin(), inboundTunnels.end(), tunnels.GetRng ());
419  		auto it1 = outboundTunnels.begin ();
420  		auto it2 = inboundTunnels.begin ();
421  		while (it1 != outboundTunnels.end () && it2 != inboundTunnels.end ())
422  		{
423  			newTests.push_back(std::make_pair (*it1, *it2));
424  			++it1; ++it2;
425  		}
426  		bool isECIES = m_LocalDestination->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
427  		for (auto& it: newTests)
428  		{
429  			uint32_t msgID;
430  			RAND_bytes ((uint8_t *)&msgID, 4);
431  			{
432  				std::unique_lock<std::mutex> l(m_TestsMutex);
433  				m_Tests[msgID] = it;
434  			}
435  			auto msg = CreateTunnelTestMsg (msgID);
436  			auto outbound = it.first;
437  			auto s = shared_from_this ();
438  			msg->onDrop = [msgID, outbound, s]()
439  				{
440  					// if test msg dropped locally it's outbound tunnel to blame
441  					outbound->SetState (eTunnelStateFailed);
442  					{
443  						std::unique_lock<std::mutex> l(s->m_TestsMutex);
444  						s->m_Tests.erase (msgID);
445  					}
446  					{
447  						std::unique_lock<std::mutex> l(s->m_OutboundTunnelsMutex);
448  						s->m_OutboundTunnels.erase (outbound);
449  					}
450  				};
451  			// encrypt
452  			if (isECIES)
453  			{
454  				uint8_t key[32]; RAND_bytes (key, 32);
455  				uint64_t tag; RAND_bytes ((uint8_t *)&tag, 8);
456  				m_LocalDestination->SubmitECIESx25519Key (key, tag);
457  				msg = i2p::garlic::WrapECIESX25519Message (msg, key, tag);
458  			}
459  			else
460  			{
461  				uint8_t key[32], tag[32];
462  				RAND_bytes (key, 32); RAND_bytes (tag, 32);
463  				m_LocalDestination->SubmitSessionKey (key, tag);
464  				i2p::garlic::ElGamalAESSession garlic (key, tag);
465  				msg = garlic.WrapSingleMessage (msg);
466  			}
467  			outbound->SendTunnelDataMsgTo (it.second->GetNextIdentHash (), it.second->GetNextTunnelID (), msg);
468  		}
469  	}
470  
471  	void TunnelPool::ManageTunnels (uint64_t ts)
472  	{
473  		if (ts > m_NextManageTime || ts + 2*TUNNEL_POOL_MANAGE_INTERVAL < m_NextManageTime) // in case if clock was adjusted
474  		{
475  			CreateTunnels ();
476  			TestTunnels ();
477  			m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (tunnels.GetRng ()() % TUNNEL_POOL_MANAGE_INTERVAL)/2;
478  		}
479  	}
480  
481  	void TunnelPool::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
482  	{
483  		if (m_LocalDestination)
484  			m_LocalDestination->ProcessGarlicMessage (msg);
485  		else
486  			LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped");
487  	}
488  
489  	void TunnelPool::ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg)
490  	{
491  		if (m_LocalDestination)
492  			m_LocalDestination->ProcessDeliveryStatusMessage (msg);
493  		else
494  			LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped");
495  	}
496  
497  	void TunnelPool::ProcessTunnelTest (std::shared_ptr<I2NPMessage> msg)
498  	{
499  		const uint8_t * buf = msg->GetPayload ();
500  		uint32_t msgID = bufbe32toh (buf);
501  		buf += 4;
502  		uint64_t timestamp = bufbe64toh (buf);
503  
504  		ProcessTunnelTest (msgID, timestamp);
505  	}
506  
507  	bool TunnelPool::ProcessTunnelTest (uint32_t msgID, uint64_t timestamp)
508  	{
509  		decltype(m_Tests)::mapped_type test;
510  		bool found = false;
511  		{
512  			std::unique_lock<std::mutex> l(m_TestsMutex);
513  			auto it = m_Tests.find (msgID);
514  			if (it != m_Tests.end ())
515  			{
516  				found = true;
517  				test = it->second;
518  				m_Tests.erase (it);
519  			}
520  		}
521  		if (found)
522  		{
523  			int dlt = (uint64_t)i2p::util::GetMonotonicMicroseconds () - (int64_t)timestamp;
524  			LogPrint (eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " microseconds");
525  			if (dlt < 0) dlt = 0; // should not happen
526  			int numHops = 0;
527  			if (test.first) numHops += test.first->GetNumHops ();
528  			if (test.second) numHops += test.second->GetNumHops ();
529  			// restore from test failed state if any
530  			if (test.first)
531  			{
532  				if (test.first->GetState () != eTunnelStateExpiring)
533  					test.first->SetState (eTunnelStateEstablished);
534  				// update latency
535  				int latency = 0;
536  				if (numHops) latency = dlt*test.first->GetNumHops ()/numHops;
537  				if (!latency) latency = dlt/2;
538  				test.first->AddLatencySample (latency);
539  			}
540  			if (test.second)
541  			{
542  				if (test.second->GetState () != eTunnelStateExpiring)
543  					test.second->SetState (eTunnelStateEstablished);
544  				// update latency
545  				int latency = 0;
546  				if (numHops) latency = dlt*test.second->GetNumHops ()/numHops;
547  				if (!latency) latency = dlt/2;
548  				test.second->AddLatencySample (latency);
549  			}
550  		}
551  		return found;
552  	}
553  
554  	bool TunnelPool::IsExploratory () const
555  	{
556  		return i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ();
557  	}
558  
559  	std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop,
560  		bool reverse, bool endpoint) const
561  	{
562  		bool tryClient = !IsExploratory () && !i2p::context.IsLimitedConnectivity ();
563  		std::shared_ptr<const i2p::data::RouterInfo> hop;
564  		for (int i = 0; i < TUNNEL_POOL_MAX_HOP_SELECTION_ATTEMPTS; i++)
565  		{
566  			hop = tryClient ?
567  				(m_IsHighBandwidth ?
568  				 	i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse, endpoint) :
569  				 	i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint, true)):
570  				i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint, false);
571  			if (hop)
572  			{
573  				if (!hop->HasProfile () || !hop->GetProfile ()->IsBad ())
574  					break;
575  			}
576  			else if (tryClient)
577  				tryClient = false;
578  			else
579  				return nullptr;
580  		}
581  		return hop;
582  	}
583  
584  	bool TunnelPool::StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop)
585  	{
586  		int start = 0;
587  		std::shared_ptr<const i2p::data::RouterInfo> prevHop = i2p::context.GetSharedRouterInfo ();
588  		if(i2p::transport::transports.RoutesRestricted() || !m_TrustedRouters.empty ())
589  		{
590  			/** if routes are restricted or trusted prepend trusted first hop */
591  			auto hop = (!m_TrustedRouters.empty ()) ? SelectTrustedRouter (inbound) :
592  				i2p::transport::transports.GetRestrictedPeer ();
593  			if(!hop) return false;
594  			path.Add (hop);
595  			prevHop = hop;
596  			start++;
597  		}
598  		else if (i2p::transport::transports.GetNumPeers () > 100 ||
599  			(inbound && (i2p::transport::transports.GetNumPeers () > 25 ||
600              (i2p::context.IsLimitedConnectivity () && i2p::transport::transports.GetNumPeers () > 0))))
601  		{
602  			auto r = i2p::transport::transports.GetRandomPeer (m_IsHighBandwidth && !i2p::context.IsLimitedConnectivity ());
603  			if (r && r->IsECIES () && (!r->HasProfile () || !r->GetProfile ()->IsBad ()) &&
604  				(numHops > 1 || (r->IsV4 () && (!inbound || r->IsPublished (true))))) // first inbound must be published ipv4
605  			{
606  				prevHop = r;
607  				path.Add (r);
608  				start++;
609  			}
610  		}
611  
612  		for(int i = start; i < numHops; i++ )
613  		{
614  			auto hop = nextHop (prevHop, inbound, i == numHops - 1);
615  			if (!hop && !i) // if no suitable peer found for first hop, try already connected
616  			{
617  				LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected");
618  				hop = i2p::transport::transports.GetRandomPeer (false);
619  				if (hop && !hop->IsECIES ()) hop = nullptr;
620  			}
621  			if (!hop)
622  			{
623  				LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ());
624  				return false;
625  			}
626  			prevHop = hop;
627  			path.Add (hop);
628  		}
629  		path.farEndTransports = prevHop->GetCompatibleTransports (inbound); // last hop
630  		return true;
631  	}
632  
633  	bool TunnelPool::SelectPeers (Path& path, bool isInbound)
634  	{
635  		// explicit peers in use
636  		if (!m_ExplicitPeers.empty ()) return SelectExplicitPeers (path, isInbound);
637  		// calculate num hops
638  		int numHops;
639  		if (isInbound)
640  		{
641  			numHops = m_NumInboundHops;
642  			if (m_InboundVariance)
643  			{
644  				int offset = tunnels.GetRng ()() % (std::abs (m_InboundVariance) + 1);
645  				if (m_InboundVariance < 0) offset = -offset;
646  				numHops += offset;
647  			}
648  		}
649  		else
650  		{
651  			numHops = m_NumOutboundHops;
652  			if (m_OutboundVariance)
653  			{
654  				int offset = tunnels.GetRng ()() % (std::abs (m_OutboundVariance) + 1);
655  				if (m_OutboundVariance < 0) offset = -offset;
656  				numHops += offset;
657  			}
658  		}
659  		// peers is empty
660  		if (numHops <= 0) return true;
661  		// custom peer selector in use ?
662  		{
663  			std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
664  			if (m_CustomPeerSelector)
665  				return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound);
666  		}
667  		return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this,
668  			std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
669  	}
670  
671  	bool TunnelPool::SelectExplicitPeers (Path& path, bool isInbound)
672  	{
673  		if (m_ExplicitPeers.empty ()) return false;
674  		int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
675  		if (numHops > (int)m_ExplicitPeers.size ()) numHops = m_ExplicitPeers.size ();
676  		for (int i = 0; i < numHops; i++)
677  		{
678  			auto& ident = m_ExplicitPeers[i];
679  			auto r = i2p::data::netdb.FindRouter (ident);
680  			if (r)
681  			{
682  				if (r->IsECIES ())
683  				{
684  					path.Add (r);
685  					if (i == numHops - 1)
686  						path.farEndTransports = r->GetCompatibleTransports (isInbound);
687  				}
688  				else
689  				{
690  					LogPrint (eLogError, "Tunnels: ElGamal router ", ident.ToBase64 (), " is not supported");
691  					return false;
692  				}
693  			}
694  			else
695  			{
696  				LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ());
697  				i2p::data::netdb.RequestDestination (ident);
698  				return false;
699  			}
700  		}
701  		return true;
702  	}
703  
704  	std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectTrustedRouter (bool inbound) const
705  	{
706  		size_t count = m_TrustedRouters.size ();
707  		if (!count) return nullptr;
708  		std::shared_ptr<const i2p::data::RouterInfo> r;
709  		int ind = tunnels.GetRng ()() % count;
710  		for (size_t i = 0; i < count; i++)
711  		{
712  			auto& ident = m_TrustedRouters[(ind + i) % count];
713  			if (inbound)
714  			{
715  				if (i2p::transport::transports.IsConnected (ident))
716  				{
717  					r = i2p::data::netdb.FindRouter (ident);
718  					break;
719  				}
720  			}
721  			else
722  			{
723  				auto r1 = i2p::data::netdb.FindRouter (ident);
724  				if (!r1) i2p::data::netdb.RequestDestination (ident, nullptr); // request one if not in NetDB
725  				if (r1 && !r1->IsUnreachable () && r1->IsReachableFrom (i2p::context.GetRouterInfo ()))
726  				{
727  					r = r1;
728  					break;
729  				}
730  			}
731  		}
732  		return r;
733  	}
734  
735  	void TunnelPool::CreateInboundTunnel ()
736  	{
737  		LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel...");
738  		Path path;
739  		if (SelectPeers (path, true))
740  		{
741  			auto outboundTunnel = GetNextOutboundTunnel (nullptr, path.farEndTransports);
742  			if (!outboundTunnel)
743  				outboundTunnel = tunnels.GetNextOutboundTunnel ();
744  			std::shared_ptr<TunnelConfig> config;
745  			if (m_NumInboundHops > 0)
746  			{
747  				path.Reverse ();
748  				config = std::make_shared<TunnelConfig> (path.peers, path.isShort, path.farEndTransports);
749  			}
750  			auto tunnel = tunnels.CreateInboundTunnel (config, shared_from_this (), outboundTunnel);
751  			if (tunnel->IsEstablished ()) // zero hops
752  				TunnelCreated (tunnel);
753  		}
754  		else
755  			LogPrint (eLogError, "Tunnels: Can't create inbound tunnel, no peers available");
756  	}
757  
758  	void TunnelPool::RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel)
759  	{
760  		if (IsExploratory () || tunnel->IsSlow ()) // always create new exploratory tunnel or if slow
761  		{
762  			CreateInboundTunnel ();
763  			return;
764  		}
765  		auto outboundTunnel = GetNextOutboundTunnel (nullptr, tunnel->GetFarEndTransports ());
766  		if (!outboundTunnel)
767  			outboundTunnel = tunnels.GetNextOutboundTunnel ();
768  		LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound tunnel...");
769  		std::shared_ptr<TunnelConfig> config;
770  		if (m_NumInboundHops > 0)
771  		{
772  			auto peers = tunnel->GetPeers();
773  			if (peers.size ()&& ValidatePeers (peers))
774  				config = std::make_shared<TunnelConfig>(tunnel->GetPeers (),
775  					tunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ());
776  		}
777  		if (!m_NumInboundHops || config)
778  		{
779  			auto newTunnel = tunnels.CreateInboundTunnel (config, shared_from_this(), outboundTunnel);
780  			if (newTunnel->IsEstablished ()) // zero hops
781  				TunnelCreated (newTunnel);
782  			else
783  				newTunnel->SetRecreated (true);
784  		}
785  	}
786  
787  	void TunnelPool::CreateOutboundTunnel ()
788  	{
789  		LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel...");
790  		Path path;
791  		if (SelectPeers (path, false))
792  		{
793  			auto inboundTunnel = GetNextInboundTunnel (nullptr, path.farEndTransports);
794  			if (!inboundTunnel)
795  				inboundTunnel = tunnels.GetNextInboundTunnel ();
796  			if (!inboundTunnel)
797  			{
798  				LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no inbound tunnels found");
799  				return;
800  			}
801  
802  			if (m_LocalDestination && !m_LocalDestination->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
803  				path.isShort = false; // because can't handle ECIES encrypted reply
804  
805  			std::shared_ptr<TunnelConfig> config;
806  			if (m_NumOutboundHops > 0)
807  				config = std::make_shared<TunnelConfig>(path.peers, inboundTunnel->GetNextTunnelID (),
808  					inboundTunnel->GetNextIdentHash (), path.isShort, path.farEndTransports);
809  
810  			std::shared_ptr<OutboundTunnel> tunnel;
811  			if (path.isShort)
812  			{
813  				// TODO: implement it better
814  				tunnel = tunnels.CreateOutboundTunnel (config, inboundTunnel->GetTunnelPool ());
815  				tunnel->SetTunnelPool (shared_from_this ());
816  			}
817  			else
818  				tunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ());
819  			if (tunnel && tunnel->IsEstablished ()) // zero hops
820  				TunnelCreated (tunnel);
821  		}
822  		else
823  			LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available");
824  	}
825  
826  	void TunnelPool::RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel)
827  	{
828  		if (IsExploratory () || tunnel->IsSlow ()) // always create new exploratory tunnel or if slow
829  		{
830  			CreateOutboundTunnel ();
831  			return;
832  		}
833  		auto inboundTunnel = GetNextInboundTunnel (nullptr, tunnel->GetFarEndTransports ());
834  		if (!inboundTunnel)
835  			inboundTunnel = tunnels.GetNextInboundTunnel ();
836  		if (inboundTunnel)
837  		{
838  			LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel...");
839  			std::shared_ptr<TunnelConfig> config;
840  			if (m_NumOutboundHops > 0)
841  			{
842  				auto peers = tunnel->GetPeers();
843  				if (peers.size () && ValidatePeers (peers))
844  					config = std::make_shared<TunnelConfig>(peers, inboundTunnel->GetNextTunnelID (),
845  						inboundTunnel->GetNextIdentHash (), inboundTunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ());
846  			}
847  			if (!m_NumOutboundHops || config)
848  			{
849  				auto newTunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ());
850  				if (newTunnel->IsEstablished ()) // zero hops
851  					TunnelCreated (newTunnel);
852  			}
853  		}
854  		else
855  			LogPrint (eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found");
856  	}
857  
858  	void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel)
859  	{
860  		LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
861  		auto tunnel = tunnels.CreateInboundTunnel (
862  			m_NumOutboundHops > 0 ? std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers (),
863  				outboundTunnel->IsShortBuildMessage ()) : nullptr,
864  				shared_from_this (), outboundTunnel);
865  		if (tunnel->IsEstablished ()) // zero hops
866  			TunnelCreated (tunnel);
867  	}
868  
869  	void TunnelPool::SetCustomPeerSelector(ITunnelPeerSelector * selector)
870  	{
871  		std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
872  		m_CustomPeerSelector = selector;
873  	}
874  
875  	void TunnelPool::UnsetCustomPeerSelector()
876  	{
877  		SetCustomPeerSelector(nullptr);
878  	}
879  
880  	bool TunnelPool::HasCustomPeerSelector()
881  	{
882  		std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
883  		return m_CustomPeerSelector != nullptr;
884  	}
885  
886  	bool TunnelPool::ValidatePeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers) const
887  	{
888  		bool highBandwidth = !IsExploratory ();
889  		for (auto it: peers)
890  		{
891  			auto r = i2p::data::netdb.FindRouter (it->GetIdentHash ());
892  			if (r)
893  			{
894  				if (r->IsHighCongestion (highBandwidth)) return false;
895  				it = r->GetIdentity (); // use identity from updated RouterInfo
896  			}
897  		}
898  		return true;
899  	}
900  
901  	std::shared_ptr<InboundTunnel> TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude) const
902  	{
903  		std::shared_ptr<InboundTunnel> tun = nullptr;
904  		std::unique_lock<std::mutex> lock(m_InboundTunnelsMutex);
905  		int min = 1000000;
906  		for (const auto & itr : m_InboundTunnels) {
907  			if(!itr->LatencyIsKnown()) continue;
908  			auto l = itr->GetMeanLatency();
909  			if (l >= min) continue;
910  			tun = itr;
911  			if(tun == exclude) continue;
912  			min = l;
913  		}
914  		return tun;
915  	}
916  
917  	std::shared_ptr<OutboundTunnel> TunnelPool::GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude) const
918  	{
919  		std::shared_ptr<OutboundTunnel> tun = nullptr;
920  		std::unique_lock<std::mutex> lock(m_OutboundTunnelsMutex);
921  		int min = 1000000;
922  		for (const auto & itr : m_OutboundTunnels) {
923  			if(!itr->LatencyIsKnown()) continue;
924  			auto l = itr->GetMeanLatency();
925  			if (l >= min) continue;
926  			tun = itr;
927  			if(tun == exclude) continue;
928  			min = l;
929  		}
930  		return tun;
931  	}
932  }
933  }