mirror of https://github.com/PurpleI2P/i2pd.git
I2P: End-to-End encrypted and anonymous Internet
https://i2pd.website/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
745 lines
20 KiB
745 lines
20 KiB
#include <string.h> |
|
#include "I2PEndian.h" |
|
#include <thread> |
|
#include <algorithm> |
|
#include <vector> |
|
#include <cryptopp/sha.h> |
|
#include "RouterContext.h" |
|
#include "Log.h" |
|
#include "Timestamp.h" |
|
#include "I2NPProtocol.h" |
|
#include "Transports.h" |
|
#include "NetDb.h" |
|
#include "Tunnel.h" |
|
|
|
namespace i2p |
|
{ |
|
namespace tunnel |
|
{ |
|
|
|
Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config): |
|
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false) |
|
{ |
|
} |
|
|
|
Tunnel::~Tunnel () |
|
{ |
|
} |
|
|
|
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel) |
|
{ |
|
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); |
|
auto numHops = m_Config->GetNumHops (); |
|
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops; |
|
I2NPMessage * msg = NewI2NPShortMessage (); |
|
*msg->GetPayload () = numRecords; |
|
msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1; |
|
|
|
// shuffle records |
|
std::vector<int> recordIndicies; |
|
for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); |
|
std::random_shuffle (recordIndicies.begin(), recordIndicies.end()); |
|
|
|
// create real records |
|
uint8_t * records = msg->GetPayload () + 1; |
|
TunnelHopConfig * hop = m_Config->GetFirstHop (); |
|
int i = 0; |
|
while (hop) |
|
{ |
|
int idx = recordIndicies[i]; |
|
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, |
|
hop->next ? rnd.GenerateWord32 () : replyMsgID); // we set replyMsgID for last hop only |
|
hop->recordIndex = idx; |
|
i++; |
|
hop = hop->next; |
|
} |
|
// fill up fake records with random data |
|
for (int i = numHops; i < numRecords; i++) |
|
{ |
|
int idx = recordIndicies[i]; |
|
rnd.GenerateBlock (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE); |
|
} |
|
|
|
// decrypt real records |
|
i2p::crypto::CBCDecryption decryption; |
|
hop = m_Config->GetLastHop ()->prev; |
|
while (hop) |
|
{ |
|
decryption.SetKey (hop->replyKey); |
|
// decrypt records after current hop |
|
TunnelHopConfig * hop1 = hop->next; |
|
while (hop1) |
|
{ |
|
decryption.SetIV (hop->replyIV); |
|
uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE; |
|
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); |
|
hop1 = hop1->next; |
|
} |
|
hop = hop->prev; |
|
} |
|
FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild); |
|
|
|
// send message |
|
if (outboundTunnel) |
|
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg); |
|
else |
|
i2p::transport::transports.SendMessage (GetNextIdentHash (), ToSharedI2NPMessage (msg)); |
|
} |
|
|
|
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) |
|
{ |
|
LogPrint ("TunnelBuildResponse ", (int)msg[0], " records."); |
|
|
|
i2p::crypto::CBCDecryption decryption; |
|
TunnelHopConfig * hop = m_Config->GetLastHop (); |
|
while (hop) |
|
{ |
|
decryption.SetKey (hop->replyKey); |
|
// decrypt records before and including current hop |
|
TunnelHopConfig * hop1 = hop; |
|
while (hop1) |
|
{ |
|
auto idx = hop1->recordIndex; |
|
if (idx >= 0 && idx < msg[0]) |
|
{ |
|
uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE; |
|
decryption.SetIV (hop->replyIV); |
|
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); |
|
} |
|
else |
|
LogPrint ("Tunnel hop index ", idx, " is out of range"); |
|
hop1 = hop1->prev; |
|
} |
|
hop = hop->prev; |
|
} |
|
|
|
bool established = true; |
|
hop = m_Config->GetFirstHop (); |
|
while (hop) |
|
{ |
|
const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE; |
|
uint8_t ret = record[BUILD_RESPONSE_RECORD_RET_OFFSET]; |
|
LogPrint ("Ret code=", (int)ret); |
|
hop->router->GetProfile ()->TunnelBuildResponse (ret); |
|
if (ret) |
|
// if any of participants declined the tunnel is not established |
|
established = false; |
|
hop = hop->next; |
|
} |
|
if (established) |
|
{ |
|
// change reply keys to layer keys |
|
hop = m_Config->GetFirstHop (); |
|
while (hop) |
|
{ |
|
hop->decryption.SetKeys (hop->layerKey, hop->ivKey); |
|
hop = hop->next; |
|
} |
|
} |
|
if (established) m_State = eTunnelStateEstablished; |
|
return established; |
|
} |
|
|
|
void Tunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg) |
|
{ |
|
uint8_t * payload = tunnelMsg->GetPayload () + 4; |
|
TunnelHopConfig * hop = m_Config->GetLastHop (); |
|
while (hop) |
|
{ |
|
hop->decryption.Decrypt (payload); |
|
hop = hop->prev; |
|
} |
|
} |
|
|
|
void Tunnel::SendTunnelDataMsg (i2p::I2NPMessage * msg) |
|
{ |
|
LogPrint (eLogInfo, "Can't send I2NP messages without delivery instructions"); |
|
DeleteI2NPMessage (msg); |
|
} |
|
|
|
void InboundTunnel::HandleTunnelDataMsg (I2NPMessage * msg) |
|
{ |
|
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive |
|
msg->from = shared_from_this (); |
|
EncryptTunnelMsg (msg); |
|
m_Endpoint.HandleDecryptedTunnelDataMsg (msg); |
|
} |
|
|
|
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg) |
|
{ |
|
TunnelMessageBlock block; |
|
if (gwHash) |
|
{ |
|
block.hash = gwHash; |
|
if (gwTunnel) |
|
{ |
|
block.deliveryType = eDeliveryTypeTunnel; |
|
block.tunnelID = gwTunnel; |
|
} |
|
else |
|
block.deliveryType = eDeliveryTypeRouter; |
|
} |
|
else |
|
block.deliveryType = eDeliveryTypeLocal; |
|
block.data = msg; |
|
|
|
std::unique_lock<std::mutex> l(m_SendMutex); |
|
m_Gateway.SendTunnelDataMsg (block); |
|
} |
|
|
|
void OutboundTunnel::SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs) |
|
{ |
|
std::unique_lock<std::mutex> l(m_SendMutex); |
|
for (auto& it : msgs) |
|
m_Gateway.PutTunnelDataMsg (it); |
|
m_Gateway.SendBuffer (); |
|
} |
|
|
|
void OutboundTunnel::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg) |
|
{ |
|
LogPrint (eLogError, "Incoming message for outbound tunnel ", GetTunnelID ()); |
|
DeleteI2NPMessage (tunnelMsg); |
|
} |
|
|
|
Tunnels tunnels; |
|
|
|
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), |
|
m_NumSuccesiveTunnelCreations (0), m_NumFailedTunnelCreations (0) |
|
{ |
|
} |
|
|
|
Tunnels::~Tunnels () |
|
{ |
|
for (auto& it : m_TransitTunnels) |
|
delete it.second; |
|
m_TransitTunnels.clear (); |
|
} |
|
|
|
std::shared_ptr<InboundTunnel> Tunnels::GetInboundTunnel (uint32_t tunnelID) |
|
{ |
|
auto it = m_InboundTunnels.find(tunnelID); |
|
if (it != m_InboundTunnels.end ()) |
|
return it->second; |
|
return nullptr; |
|
} |
|
|
|
TransitTunnel * Tunnels::GetTransitTunnel (uint32_t tunnelID) |
|
{ |
|
auto it = m_TransitTunnels.find(tunnelID); |
|
if (it != m_TransitTunnels.end ()) |
|
return it->second; |
|
return nullptr; |
|
} |
|
|
|
std::shared_ptr<InboundTunnel> Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID) |
|
{ |
|
return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels); |
|
} |
|
|
|
std::shared_ptr<OutboundTunnel> Tunnels::GetPendingOutboundTunnel (uint32_t replyMsgID) |
|
{ |
|
return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels); |
|
} |
|
|
|
template<class TTunnel> |
|
std::shared_ptr<TTunnel> Tunnels::GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels) |
|
{ |
|
auto it = pendingTunnels.find(replyMsgID); |
|
if (it != pendingTunnels.end () && it->second->GetState () == eTunnelStatePending) |
|
{ |
|
it->second->SetState (eTunnelStateBuildReplyReceived); |
|
return it->second; |
|
} |
|
return nullptr; |
|
} |
|
|
|
std::shared_ptr<InboundTunnel> Tunnels::GetNextInboundTunnel () |
|
{ |
|
std::shared_ptr<InboundTunnel> tunnel; |
|
size_t minReceived = 0; |
|
for (auto it : m_InboundTunnels) |
|
{ |
|
if (!it.second->IsEstablished ()) continue; |
|
if (!tunnel || it.second->GetNumReceivedBytes () < minReceived) |
|
{ |
|
tunnel = it.second; |
|
minReceived = it.second->GetNumReceivedBytes (); |
|
} |
|
} |
|
return tunnel; |
|
} |
|
|
|
std::shared_ptr<OutboundTunnel> Tunnels::GetNextOutboundTunnel () |
|
{ |
|
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); |
|
uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0; |
|
std::shared_ptr<OutboundTunnel> tunnel; |
|
for (auto it: m_OutboundTunnels) |
|
{ |
|
if (it->IsEstablished ()) |
|
{ |
|
tunnel = it; |
|
i++; |
|
} |
|
if (i > ind && tunnel) break; |
|
} |
|
return tunnel; |
|
} |
|
|
|
std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels) |
|
{ |
|
auto pool = std::make_shared<TunnelPool> (localDestination, numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels); |
|
std::unique_lock<std::mutex> l(m_PoolsMutex); |
|
m_Pools.push_back (pool); |
|
return pool; |
|
} |
|
|
|
void Tunnels::DeleteTunnelPool (std::shared_ptr<TunnelPool> pool) |
|
{ |
|
if (pool) |
|
{ |
|
StopTunnelPool (pool); |
|
{ |
|
std::unique_lock<std::mutex> l(m_PoolsMutex); |
|
m_Pools.remove (pool); |
|
} |
|
} |
|
} |
|
|
|
void Tunnels::StopTunnelPool (std::shared_ptr<TunnelPool> pool) |
|
{ |
|
if (pool) |
|
{ |
|
pool->SetActive (false); |
|
pool->DetachTunnels (); |
|
} |
|
} |
|
|
|
void Tunnels::AddTransitTunnel (TransitTunnel * tunnel) |
|
{ |
|
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex); |
|
if (!m_TransitTunnels.insert (std::make_pair (tunnel->GetTunnelID (), tunnel)).second) |
|
{ |
|
LogPrint (eLogError, "Transit tunnel ", tunnel->GetTunnelID (), " already exists"); |
|
delete tunnel; |
|
} |
|
} |
|
|
|
void Tunnels::Start () |
|
{ |
|
m_IsRunning = true; |
|
m_Thread = new std::thread (std::bind (&Tunnels::Run, this)); |
|
} |
|
|
|
void Tunnels::Stop () |
|
{ |
|
m_IsRunning = false; |
|
m_Queue.WakeUp (); |
|
if (m_Thread) |
|
{ |
|
m_Thread->join (); |
|
delete m_Thread; |
|
m_Thread = 0; |
|
} |
|
} |
|
|
|
void Tunnels::Run () |
|
{ |
|
std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready |
|
|
|
uint64_t lastTs = 0; |
|
while (m_IsRunning) |
|
{ |
|
try |
|
{ |
|
I2NPMessage * msg = m_Queue.GetNextWithTimeout (1000); // 1 sec |
|
if (msg) |
|
{ |
|
uint32_t prevTunnelID = 0, tunnelID = 0; |
|
TunnelBase * prevTunnel = nullptr; |
|
do |
|
{ |
|
TunnelBase * tunnel = nullptr; |
|
uint8_t typeID = msg->GetTypeID (); |
|
switch (typeID) |
|
{ |
|
case eI2NPTunnelData: |
|
case eI2NPTunnelGateway: |
|
{ |
|
tunnelID = bufbe32toh (msg->GetPayload ()); |
|
if (tunnelID == prevTunnelID) |
|
tunnel = prevTunnel; |
|
else if (prevTunnel) |
|
prevTunnel->FlushTunnelDataMsgs (); |
|
|
|
if (!tunnel && typeID == eI2NPTunnelData) |
|
tunnel = GetInboundTunnel (tunnelID).get (); |
|
if (!tunnel) |
|
tunnel = GetTransitTunnel (tunnelID); |
|
if (tunnel) |
|
{ |
|
if (typeID == eI2NPTunnelData) |
|
tunnel->HandleTunnelDataMsg (msg); |
|
else // tunnel gateway assumed |
|
HandleTunnelGatewayMsg (tunnel, msg); |
|
} |
|
else |
|
{ |
|
LogPrint (eLogWarning, "Tunnel ", tunnelID, " not found"); |
|
DeleteI2NPMessage (msg); |
|
} |
|
break; |
|
} |
|
case eI2NPVariableTunnelBuild: |
|
case eI2NPVariableTunnelBuildReply: |
|
case eI2NPTunnelBuild: |
|
case eI2NPTunnelBuildReply: |
|
{ |
|
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); |
|
DeleteI2NPMessage (msg); |
|
break; |
|
} |
|
default: |
|
{ |
|
LogPrint (eLogError, "Unexpected messsage type ", (int)typeID); |
|
DeleteI2NPMessage (msg); |
|
} |
|
} |
|
|
|
msg = m_Queue.Get (); |
|
if (msg) |
|
{ |
|
prevTunnelID = tunnelID; |
|
prevTunnel = tunnel; |
|
} |
|
else if (tunnel) |
|
tunnel->FlushTunnelDataMsgs (); |
|
} |
|
while (msg); |
|
} |
|
|
|
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); |
|
if (ts - lastTs >= 15) // manage tunnels every 15 seconds |
|
{ |
|
ManageTunnels (); |
|
lastTs = ts; |
|
} |
|
} |
|
catch (std::exception& ex) |
|
{ |
|
LogPrint ("Tunnels: ", ex.what ()); |
|
} |
|
} |
|
} |
|
|
|
void Tunnels::HandleTunnelGatewayMsg (TunnelBase * tunnel, I2NPMessage * msg) |
|
{ |
|
if (!tunnel) |
|
{ |
|
LogPrint (eLogError, "Missing tunnel for TunnelGateway"); |
|
i2p::DeleteI2NPMessage (msg); |
|
return; |
|
} |
|
const uint8_t * payload = msg->GetPayload (); |
|
uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET); |
|
// we make payload as new I2NP message to send |
|
msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; |
|
msg->len = msg->offset + len; |
|
auto typeID = msg->GetTypeID (); |
|
LogPrint (eLogDebug, "TunnelGateway of ", (int)len, " bytes for tunnel ", tunnel->GetTunnelID (), ". Msg type ", (int)typeID); |
|
|
|
if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply) |
|
{ |
|
// transit DatabaseStore my contain new/updated RI |
|
// or DatabaseSearchReply with new routers |
|
auto ds = NewI2NPMessage (); |
|
*ds = *msg; // TODO: don't copy once msg is shared_ptr |
|
i2p::data::netdb.PostI2NPMsg (ToSharedI2NPMessage (ds)); |
|
} |
|
tunnel->SendTunnelDataMsg (msg); |
|
} |
|
|
|
void Tunnels::ManageTunnels () |
|
{ |
|
ManagePendingTunnels (); |
|
ManageInboundTunnels (); |
|
ManageOutboundTunnels (); |
|
ManageTransitTunnels (); |
|
ManageTunnelPools (); |
|
} |
|
|
|
void Tunnels::ManagePendingTunnels () |
|
{ |
|
ManagePendingTunnels (m_PendingInboundTunnels); |
|
ManagePendingTunnels (m_PendingOutboundTunnels); |
|
} |
|
|
|
template<class PendingTunnels> |
|
void Tunnels::ManagePendingTunnels (PendingTunnels& pendingTunnels) |
|
{ |
|
// check pending tunnel. delete failed or timeout |
|
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); |
|
for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();) |
|
{ |
|
auto tunnel = it->second; |
|
switch (tunnel->GetState ()) |
|
{ |
|
case eTunnelStatePending: |
|
if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT) |
|
{ |
|
LogPrint ("Pending tunnel build request ", it->first, " timeout. Deleted"); |
|
// update stats |
|
auto config = tunnel->GetTunnelConfig (); |
|
if (config) |
|
{ |
|
auto hop = config->GetFirstHop (); |
|
while (hop) |
|
{ |
|
if (hop->router) |
|
hop->router->GetProfile ()->TunnelNonReplied (); |
|
hop = hop->next; |
|
} |
|
} |
|
// delete |
|
it = pendingTunnels.erase (it); |
|
m_NumFailedTunnelCreations++; |
|
} |
|
else |
|
it++; |
|
break; |
|
case eTunnelStateBuildFailed: |
|
LogPrint ("Pending tunnel build request ", it->first, " failed. Deleted"); |
|
it = pendingTunnels.erase (it); |
|
m_NumFailedTunnelCreations++; |
|
break; |
|
case eTunnelStateBuildReplyReceived: |
|
// intermediate state, will be either established of build failed |
|
it++; |
|
break; |
|
default: |
|
// success |
|
it = pendingTunnels.erase (it); |
|
m_NumSuccesiveTunnelCreations++; |
|
} |
|
} |
|
} |
|
|
|
void Tunnels::ManageOutboundTunnels () |
|
{ |
|
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); |
|
{ |
|
for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();) |
|
{ |
|
auto tunnel = *it; |
|
if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) |
|
{ |
|
LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired"); |
|
auto pool = tunnel->GetTunnelPool (); |
|
if (pool) |
|
pool->TunnelExpired (tunnel); |
|
it = m_OutboundTunnels.erase (it); |
|
} |
|
else |
|
{ |
|
if (tunnel->IsEstablished ()) |
|
{ |
|
if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) |
|
{ |
|
tunnel->SetIsRecreated (); |
|
auto pool = tunnel->GetTunnelPool (); |
|
if (pool) |
|
pool->RecreateOutboundTunnel (tunnel); |
|
} |
|
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) |
|
tunnel->SetState (eTunnelStateExpiring); |
|
} |
|
it++; |
|
} |
|
} |
|
} |
|
|
|
if (m_OutboundTunnels.size () < 5) |
|
{ |
|
// trying to create one more oubound tunnel |
|
auto inboundTunnel = GetNextInboundTunnel (); |
|
auto router = i2p::data::netdb.GetRandomRouter (); |
|
if (!inboundTunnel || !router) return; |
|
LogPrint ("Creating one hop outbound tunnel..."); |
|
CreateTunnel<OutboundTunnel> ( |
|
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > { router }, |
|
inboundTunnel->GetTunnelConfig ()) |
|
); |
|
} |
|
} |
|
|
|
void Tunnels::ManageInboundTunnels () |
|
{ |
|
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); |
|
{ |
|
for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();) |
|
{ |
|
auto tunnel = it->second; |
|
if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) |
|
{ |
|
LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired"); |
|
auto pool = tunnel->GetTunnelPool (); |
|
if (pool) |
|
pool->TunnelExpired (tunnel); |
|
it = m_InboundTunnels.erase (it); |
|
} |
|
else |
|
{ |
|
if (tunnel->IsEstablished ()) |
|
{ |
|
if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) |
|
{ |
|
tunnel->SetIsRecreated (); |
|
auto pool = tunnel->GetTunnelPool (); |
|
if (pool) |
|
pool->RecreateInboundTunnel (tunnel); |
|
} |
|
|
|
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) |
|
tunnel->SetState (eTunnelStateExpiring); |
|
} |
|
it++; |
|
} |
|
} |
|
} |
|
|
|
if (m_InboundTunnels.empty ()) |
|
{ |
|
LogPrint ("Creating zero hops inbound tunnel..."); |
|
CreateZeroHopsInboundTunnel (); |
|
if (!m_ExploratoryPool) |
|
m_ExploratoryPool = CreateTunnelPool (&i2p::context, 2, 2, 5, 5); // 2-hop exploratory, 5 tunnels |
|
return; |
|
} |
|
|
|
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 5) |
|
{ |
|
// trying to create one more inbound tunnel |
|
auto router = i2p::data::netdb.GetRandomRouter (); |
|
LogPrint ("Creating one hop inbound tunnel..."); |
|
CreateTunnel<InboundTunnel> ( |
|
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > { router }) |
|
); |
|
} |
|
} |
|
|
|
void Tunnels::ManageTransitTunnels () |
|
{ |
|
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); |
|
for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();) |
|
{ |
|
if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) |
|
{ |
|
auto tmp = it->second; |
|
LogPrint ("Transit tunnel ", tmp->GetTunnelID (), " expired"); |
|
{ |
|
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex); |
|
it = m_TransitTunnels.erase (it); |
|
} |
|
delete tmp; |
|
} |
|
else |
|
it++; |
|
} |
|
} |
|
|
|
void Tunnels::ManageTunnelPools () |
|
{ |
|
std::unique_lock<std::mutex> l(m_PoolsMutex); |
|
for (auto it: m_Pools) |
|
{ |
|
auto pool = it; |
|
if (pool && pool->IsActive ()) |
|
{ |
|
pool->CreateTunnels (); |
|
pool->TestTunnels (); |
|
} |
|
} |
|
} |
|
|
|
void Tunnels::PostTunnelData (I2NPMessage * msg) |
|
{ |
|
if (msg) m_Queue.Put (msg); |
|
} |
|
|
|
void Tunnels::PostTunnelData (const std::vector<I2NPMessage *>& msgs) |
|
{ |
|
m_Queue.Put (msgs); |
|
} |
|
|
|
template<class TTunnel> |
|
std::shared_ptr<TTunnel> Tunnels::CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel) |
|
{ |
|
auto newTunnel = std::make_shared<TTunnel> (config); |
|
uint32_t replyMsgID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); |
|
AddPendingTunnel (replyMsgID, newTunnel); |
|
newTunnel->Build (replyMsgID, outboundTunnel); |
|
return newTunnel; |
|
} |
|
|
|
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel) |
|
{ |
|
m_PendingInboundTunnels[replyMsgID] = tunnel; |
|
} |
|
|
|
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel) |
|
{ |
|
m_PendingOutboundTunnels[replyMsgID] = tunnel; |
|
} |
|
|
|
void Tunnels::AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel) |
|
{ |
|
m_OutboundTunnels.push_back (newTunnel); |
|
auto pool = newTunnel->GetTunnelPool (); |
|
if (pool && pool->IsActive ()) |
|
pool->TunnelCreated (newTunnel); |
|
else |
|
newTunnel->SetTunnelPool (nullptr); |
|
} |
|
|
|
void Tunnels::AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel) |
|
{ |
|
m_InboundTunnels[newTunnel->GetTunnelID ()] = newTunnel; |
|
auto pool = newTunnel->GetTunnelPool (); |
|
if (!pool) |
|
{ |
|
// build symmetric outbound tunnel |
|
CreateTunnel<OutboundTunnel> (newTunnel->GetTunnelConfig ()->Invert (), GetNextOutboundTunnel ()); |
|
} |
|
else |
|
{ |
|
if (pool->IsActive ()) |
|
pool->TunnelCreated (newTunnel); |
|
else |
|
newTunnel->SetTunnelPool (nullptr); |
|
} |
|
} |
|
|
|
|
|
void Tunnels::CreateZeroHopsInboundTunnel () |
|
{ |
|
CreateTunnel<InboundTunnel> ( |
|
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > |
|
{ |
|
i2p::context.GetSharedRouterInfo () |
|
})); |
|
} |
|
|
|
int Tunnels::GetTransitTunnelsExpirationTimeout () |
|
{ |
|
int timeout = 0; |
|
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); |
|
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex); |
|
for (auto it: m_TransitTunnels) |
|
{ |
|
int t = it.second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts; |
|
if (t > timeout) timeout = t; |
|
} |
|
return timeout; |
|
} |
|
} |
|
}
|
|
|