Tunnel added

This commit is contained in:
orignal 2013-12-06 19:02:49 -05:00
parent 885e996a8a
commit d07f5d0310
3 changed files with 854 additions and 0 deletions

487
Tunnel.cpp Normal file
View File

@ -0,0 +1,487 @@
#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 (TunnelConfig * config): m_Config (config),
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_IsEstablished (false)
{
}
Tunnel::~Tunnel ()
{
delete m_Config;
}
void Tunnel::Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel)
{
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
size_t numRecords = m_Config->GetNumHops ();
I2NPMessage * msg = NewI2NPMessage ();
*msg->GetPayload () = numRecords;
msg->len += numRecords*sizeof (I2NPBuildRequestRecordElGamalEncrypted) + 1;
I2NPBuildRequestRecordElGamalEncrypted * records = (I2NPBuildRequestRecordElGamalEncrypted *)(msg->GetPayload () + 1);
TunnelHopConfig * hop = m_Config->GetFirstHop ();
int i = 0;
while (hop)
{
EncryptBuildRequestRecord (*hop->router,
CreateBuildRequestRecord (hop->router->GetIdentHash (),
hop->tunnelID,
hop->nextRouter->GetIdentHash (),
hop->nextTunnelID,
hop->layerKey, hop->ivKey,
hop->replyKey, hop->replyIV,
hop->next ? rnd.GenerateWord32 () : replyMsgID, // we set replyMsgID for last hop only
hop->isGateway, hop->isEndpoint),
records[i]);
i++;
hop = hop->next;
}
hop = m_Config->GetLastHop ()->prev;
size_t ind = numRecords - 1;
while (hop)
{
for (size_t i = ind; i < numRecords; i++)
{
m_CBCDecryption.SetKeyWithIV (hop->replyKey, 32, hop->replyIV);
m_CBCDecryption.ProcessData((uint8_t *)&records[i], (uint8_t *)&records[i], sizeof (I2NPBuildRequestRecordElGamalEncrypted));
}
hop = hop->prev;
ind--;
}
FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild);
if (outboundTunnel)
{
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
DeleteI2NPMessage (msg);
}
else
{
i2p::transports.SendMessage (GetNextIdentHash (), msg);
}
}
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
{
LogPrint ("TunnelBuildResponse ", (int)msg[0], " records.");
TunnelHopConfig * hop = m_Config->GetLastHop ();
int num = msg[0];
while (hop)
{
for (int i = 0; i < num; i++)
{
uint8_t * record = msg + 1 + i*sizeof (I2NPBuildResponseRecord);
m_CBCDecryption.SetKeyWithIV(hop->replyKey, 32, hop->replyIV);
m_CBCDecryption.ProcessData(record, record, sizeof (I2NPBuildResponseRecord));
}
hop = hop->prev;
num--;
}
m_IsEstablished = true;
for (int i = 0; i < msg[0]; i++)
{
I2NPBuildResponseRecord * record = (I2NPBuildResponseRecord *)(msg + 1 + i*sizeof (I2NPBuildResponseRecord));
LogPrint ("Ret code=", (int)record->ret);
if (record->ret)
// if any of participants declined the tunnel is not established
m_IsEstablished = false;
}
return m_IsEstablished;
}
void Tunnel::LayerDecrypt (const uint8_t * in, size_t len, const uint8_t * layerKey,
const uint8_t * iv, uint8_t * out)
{
m_CBCDecryption.SetKeyWithIV (layerKey, 32, iv);
m_CBCDecryption.ProcessData(out, in, len);
}
void Tunnel::IVDecrypt (const uint8_t * in, const uint8_t * ivKey, uint8_t * out)
{
m_ECBDecryption.SetKey (ivKey, 32);
m_ECBDecryption.ProcessData(out, in, 16);
}
void Tunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg)
{
uint8_t * payload = tunnelMsg->GetPayload () + 4;
TunnelHopConfig * hop = m_Config->GetLastHop ();
while (hop)
{
// iv + data
IVDecrypt (payload, hop->ivKey, payload);
LayerDecrypt (payload + 16, TUNNEL_DATA_ENCRYPTED_SIZE, hop->layerKey, payload, payload+16);
IVDecrypt (payload, hop->ivKey, payload);
hop = hop->prev;
}
}
void InboundTunnel::HandleTunnelDataMsg (I2NPMessage * msg)
{
EncryptTunnelMsg (msg);
m_Endpoint.HandleDecryptedTunnelDataMsg (msg);
}
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
{
m_Gateway.SendTunnelDataMsg (gwHash, gwTunnel, msg);
}
void OutboundTunnel::SendTunnelDataMsg (i2p::I2NPMessage * msg)
{
SendTunnelDataMsg (nullptr, 0, msg);
}
Tunnels tunnels;
Tunnels::Tunnels (): m_IsRunning (false), m_IsTunnelCreated (false), m_NextReplyMsgID (555),
m_ZeroHopsInboundTunnel (nullptr), m_ZeroHopsOutboundTunnel (nullptr), m_Thread (0)
{
}
Tunnels::~Tunnels ()
{
for (auto& it : m_OutboundTunnels)
delete it;
m_OutboundTunnels.clear ();
for (auto& it : m_InboundTunnels)
delete it.second;
m_InboundTunnels.clear ();
for (auto& it : m_TransitTunnels)
delete it.second;
m_TransitTunnels.clear ();
for (auto& it : m_PendingTunnels)
delete it.second;
m_PendingTunnels.clear ();
delete m_ZeroHopsInboundTunnel;
delete m_ZeroHopsOutboundTunnel;
}
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;
}
Tunnel * Tunnels::GetPendingTunnel (uint32_t replyMsgID)
{
auto it = m_PendingTunnels.find(replyMsgID);
if (it != m_PendingTunnels.end ())
{
Tunnel * t = it->second;
m_PendingTunnels.erase (it);
return t;
}
return nullptr;
}
InboundTunnel * Tunnels::GetNextInboundTunnel ()
{
InboundTunnel * tunnel = nullptr;
size_t minReceived = 0;
for (auto it : m_InboundTunnels)
if (!tunnel || it.second->GetNumReceivedBytes () < minReceived)
{
tunnel = it.second;
minReceived = it.second->GetNumReceivedBytes ();
}
return tunnel;
}
OutboundTunnel * Tunnels::GetNextOutboundTunnel ()
{
OutboundTunnel * tunnel = nullptr;
size_t minSent = 0;
for (auto it : m_OutboundTunnels)
if (!tunnel || it->GetNumSentBytes () < minSent)
{
tunnel = it;
minSent = it->GetNumSentBytes ();
}
return tunnel;
}
void Tunnels::AddTransitTunnel (TransitTunnel * tunnel)
{
m_TransitTunnels[tunnel->GetTunnelID ()] = 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 ()
{
sleep (1); // wait for other parts are ready
// we must start with zero hops tunnels
CreateZeroHopsInboundTunnel ();
CreateZeroHopsOutboundTunnel ();
uint32_t lastTs = 0;
while (m_IsRunning)
{
try
{
I2NPMessage * msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
while (msg)
{
uint32_t tunnelID = be32toh (*(uint32_t *)msg->GetPayload ());
InboundTunnel * tunnel = GetInboundTunnel (tunnelID);
if (tunnel)
tunnel->HandleTunnelDataMsg (msg);
else
{
TransitTunnel * transitTunnel = GetTransitTunnel (tunnelID);
if (transitTunnel)
transitTunnel->HandleTunnelDataMsg (msg);
else
{
LogPrint ("Tunnel ", tunnelID, " not found");
i2p::DeleteI2NPMessage (msg);
}
}
msg = m_Queue.Get ();
}
uint32_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::ManageTunnels ()
{
// check pending tunnel. if something is still there, wipe it out
// because it wouldn't be reponded anyway
for (auto& it : m_PendingTunnels)
{
LogPrint ("Pending tunnel build request ", it.first, " has not been responded. Deleted");
delete it.second;
}
m_PendingTunnels.clear ();
ManageOutboundTunnels ();
ManageInboundTunnels ();
/* if (!m_IsTunnelCreated)
{
InboundTunnel * inboundTunnel = CreateOneHopInboundTestTunnel ();
if (inboundTunnel)
CreateOneHopOutboundTestTunnel (inboundTunnel);
inboundTunnel = CreateTwoHopsInboundTestTunnel ();
if (inboundTunnel)
CreateTwoHopsOutboundTestTunnel (inboundTunnel);
m_IsTunnelCreated = true;
}*/
}
void Tunnels::ManageOutboundTunnels ()
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();)
{
if (ts > (*it)->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
LogPrint ("Tunnel ", (*it)->GetTunnelID (), " expired");
it = m_OutboundTunnels.erase (it);
}
else
it++;
}
if (m_OutboundTunnels.size () < 10)
{
// trying to create one more oubound tunnel
InboundTunnel * inboundTunnel = m_ZeroHopsInboundTunnel;
if (!m_InboundTunnels.empty ())
inboundTunnel = m_InboundTunnels.rbegin ()->second;
if (m_OutboundTunnels.empty () || m_OutboundTunnels.size () < 3)
{
LogPrint ("Creating one hop outbound tunnel...");
CreateTunnel<OutboundTunnel> (
new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),
inboundTunnel->GetTunnelConfig ()));
}
else
{
OutboundTunnel * outboundTunnel = *m_OutboundTunnels.begin ();
LogPrint ("Creating two hops outbound tunnel...");
CreateTunnel<OutboundTunnel> (
new TunnelConfig (inboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router,
i2p::data::netdb.GetRandomNTCPRouter (),
inboundTunnel->GetTunnelConfig ()),
outboundTunnel);
}
}
}
void Tunnels::ManageInboundTunnels ()
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();)
{
if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
LogPrint ("Tunnel ", it->second->GetTunnelID (), " expired");
it = m_InboundTunnels.erase (it);
}
else
it++;
}
if (m_InboundTunnels.size () < 10)
{
// trying to create one more inbound tunnel
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 3)
{
LogPrint ("Creating one hop inbound tunnel...");
CreateTunnel<InboundTunnel> (new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter ()));
}
else
{
OutboundTunnel * outboundTunnel = *m_OutboundTunnels.rbegin ();
InboundTunnel * inboundTunnel = m_InboundTunnels.rbegin ()->second;
LogPrint ("Creating two hops inbound tunnel...");
CreateTunnel<InboundTunnel> (
new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),
inboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router),
outboundTunnel);
}
}
}
void Tunnels::PostTunnelData (I2NPMessage * msg)
{
if (msg) m_Queue.Put (msg);
}
template<class TTunnel>
TTunnel * Tunnels::CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel)
{
TTunnel * newTunnel = new TTunnel (config);
m_PendingTunnels[m_NextReplyMsgID] = newTunnel;
newTunnel->Build (m_NextReplyMsgID, outboundTunnel);
m_NextReplyMsgID++; // TODO: should be atomic
return newTunnel;
}
void Tunnels::AddOutboundTunnel (OutboundTunnel * newTunnel)
{
if (newTunnel != m_ZeroHopsOutboundTunnel)
m_OutboundTunnels.push_back (newTunnel);
}
void Tunnels::AddInboundTunnel (InboundTunnel * newTunnel)
{
if (newTunnel != m_ZeroHopsInboundTunnel)
m_InboundTunnels[newTunnel->GetTunnelID ()] = newTunnel;
}
void Tunnels::CreateZeroHopsOutboundTunnel ()
{
m_ZeroHopsOutboundTunnel = CreateTunnel<OutboundTunnel> (
new TunnelConfig (&i2p::context.GetRouterInfo (),
m_ZeroHopsInboundTunnel->GetTunnelConfig ()));
}
void Tunnels::CreateZeroHopsInboundTunnel ()
{
m_ZeroHopsInboundTunnel = CreateTunnel<InboundTunnel> (
new TunnelConfig (&i2p::context.GetRouterInfo ()));
}
OutboundTunnel * Tunnels::CreateOneHopOutboundTestTunnel (InboundTunnel * replyTunnel)
{
return CreateTunnel<OutboundTunnel> (replyTunnel->GetTunnelConfig ()->Invert ());
}
InboundTunnel * Tunnels::CreateOneHopInboundTestTunnel (OutboundTunnel * outboundTunnel)
{
i2p::ntcp::NTCPSession * peer = i2p::transports.GetNextNTCPSession ();
if (peer)
{
const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo ();
return CreateTunnel<InboundTunnel> (new TunnelConfig (&router), outboundTunnel);
}
else
LogPrint ("No established peers");
return 0;
}
OutboundTunnel * Tunnels::CreateTwoHopsOutboundTestTunnel (InboundTunnel * replyTunnel)
{
return CreateTunnel<OutboundTunnel> (replyTunnel->GetTunnelConfig ()->Invert ());
}
InboundTunnel * Tunnels::CreateTwoHopsInboundTestTunnel (OutboundTunnel * outboundTunnel)
{
i2p::ntcp::NTCPSession * peer = i2p::transports.GetNextNTCPSession ();
if (peer)
{
const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo ();
return CreateTunnel<InboundTunnel> (
new TunnelConfig (&router, &i2p::context.GetRouterInfo ()),
outboundTunnel);
}
else
LogPrint ("No established peers");
return 0;
}
}
}

160
Tunnel.h Normal file
View File

@ -0,0 +1,160 @@
#ifndef TUNNEL_H__
#define TUNNEL_H__
#include <inttypes.h>
#include <map>
#include <list>
#include <string>
#include <thread>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
#include "Queue.h"
#include "TunnelConfig.h"
#include "TransitTunnel.h"
#include "TunnelEndpoint.h"
#include "TunnelGateway.h"
#include "TunnelBase.h"
#include "I2NPProtocol.h"
namespace i2p
{
namespace tunnel
{
const int TUNNEL_EXPIRATION_TIMEOUT = 600; // 10 minutes
class OutboundTunnel;
class InboundTunnel;
class Tunnel: public TunnelBase
{
public:
Tunnel (TunnelConfig * config);
~Tunnel ();
void Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel = 0);
virtual uint32_t GetTunnelID () const = 0; // as known at our side
TunnelConfig * GetTunnelConfig () const { return m_Config; }
uint32_t GetCreationTime () const { return m_CreationTime; };
bool IsEstablished () const { return m_IsEstablished; };
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
// implements TunnelBase
void EncryptTunnelMsg (I2NPMessage * tunnelMsg);
uint32_t GetNextTunnelID () const { return m_Config->GetFirstHop ()->tunnelID; };
const i2p::data::IdentHash& GetNextIdentHash () const { return m_Config->GetFirstHop ()->router->GetIdentHash (); };
private:
void LayerDecrypt (const uint8_t * in, size_t len, const uint8_t * layerKey,
const uint8_t * iv, uint8_t * out);
void IVDecrypt (const uint8_t * in, const uint8_t * ivKey, uint8_t * out);
private:
TunnelConfig * m_Config;
uint32_t m_CreationTime; // seconds since epoch
bool m_IsEstablished;
CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption m_ECBDecryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_CBCDecryption;
};
class OutboundTunnel: public Tunnel
{
public:
OutboundTunnel (TunnelConfig * config): Tunnel (config), m_Gateway (this) {};
void SendTunnelDataMsg (i2p::I2NPMessage * msg); //local
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
uint32_t GetTunnelID () const { return GetNextTunnelID (); };
TunnelGateway& GetTunnelGateway () { return m_Gateway; };
size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
private:
TunnelGateway m_Gateway;
};
class InboundTunnel: public Tunnel
{
public:
InboundTunnel (TunnelConfig * config): Tunnel (config) {};
void HandleTunnelDataMsg (I2NPMessage * msg);
uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; };
size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
private:
TunnelEndpoint m_Endpoint;
};
class Tunnels
{
public:
Tunnels ();
~Tunnels ();
void Start ();
void Stop ();
InboundTunnel * GetInboundTunnel (uint32_t tunnelID);
Tunnel * GetPendingTunnel (uint32_t replyMsgID);
InboundTunnel * GetNextInboundTunnel ();
OutboundTunnel * GetNextOutboundTunnel ();
TransitTunnel * GetTransitTunnel (uint32_t tunnelID);
void AddTransitTunnel (TransitTunnel * tunnel);
void AddOutboundTunnel (OutboundTunnel * newTunnel);
void AddInboundTunnel (InboundTunnel * newTunnel);
void PostTunnelData (I2NPMessage * msg);
template<class TTunnel>
TTunnel * CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel = 0);
OutboundTunnel * CreateOneHopOutboundTestTunnel (InboundTunnel * replyTunnel);
InboundTunnel * CreateOneHopInboundTestTunnel (OutboundTunnel * outboundTunnel = 0);
OutboundTunnel * CreateTwoHopsOutboundTestTunnel (InboundTunnel * replyTunnel);
InboundTunnel * CreateTwoHopsInboundTestTunnel (OutboundTunnel * outboundTunnel = 0);
private:
void Run ();
void ManageTunnels ();
void ManageOutboundTunnels ();
void ManageInboundTunnels ();
void CreateZeroHopsOutboundTunnel ();
void CreateZeroHopsInboundTunnel ();
private:
bool m_IsRunning;
bool m_IsTunnelCreated; // TODO: temporary
uint32_t m_NextReplyMsgID; // TODO: make it random later
InboundTunnel * m_ZeroHopsInboundTunnel;
OutboundTunnel * m_ZeroHopsOutboundTunnel;
std::thread * m_Thread;
std::map<uint32_t, Tunnel *> m_PendingTunnels; // by replyMsgID
std::map<uint32_t, InboundTunnel *> m_InboundTunnels;
std::list<OutboundTunnel *> m_OutboundTunnels;
std::map<uint32_t, TransitTunnel *> m_TransitTunnels;
i2p::util::Queue<I2NPMessage> m_Queue;
public:
// for HTTP only
const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; };
const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; };
};
extern Tunnels tunnels;
}
}
#endif

207
TunnelConfig.h Normal file
View File

@ -0,0 +1,207 @@
#ifndef TUNNEL_CONFIG_H__
#define TUNNEL_CONFIG_H__
#include <inttypes.h>
#include <sstream>
#include "RouterInfo.h"
#include "RouterContext.h"
namespace i2p
{
namespace tunnel
{
struct TunnelHopConfig
{
const i2p::data::RouterInfo * router, * nextRouter;
uint32_t tunnelID, nextTunnelID;
uint8_t layerKey[32];
uint8_t ivKey[32];
uint8_t replyKey[32];
uint8_t replyIV[16];
bool isGateway, isEndpoint;
TunnelHopConfig * next, * prev;
TunnelHopConfig (const i2p::data::RouterInfo * r)
{
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (layerKey, 32);
rnd.GenerateBlock (ivKey, 32);
rnd.GenerateBlock (replyIV, 16);
tunnelID = rnd.GenerateWord32 ();
isGateway = true;
isEndpoint = true;
router = r;
nextRouter = 0;
nextTunnelID = 0;
next = 0;
prev = 0;
}
void SetNextRouter (const i2p::data::RouterInfo * r)
{
nextRouter = r;
isEndpoint = false;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
nextTunnelID = rnd.GenerateWord32 ();
}
void SetReplyHop (TunnelHopConfig * replyFirstHop)
{
nextRouter = replyFirstHop->router;
nextTunnelID = replyFirstHop->tunnelID;
isEndpoint = true;
}
void SetNext (TunnelHopConfig * n)
{
next = n;
if (next)
{
next->prev = this;
next->isGateway = false;
isEndpoint = false;
nextRouter = next->router;
nextTunnelID = next->tunnelID;
}
}
void SetPrev (TunnelHopConfig * p)
{
prev = p;
if (prev)
{
prev->next = this;
prev->isEndpoint = false;
isGateway = false;
}
}
};
class TunnelConfig
{
public:
TunnelConfig (const i2p::data::RouterInfo * peer, TunnelConfig * replyTunnelConfig = 0) // one hop
{
m_FirstHop = new TunnelHopConfig (peer);
m_LastHop = m_FirstHop;
if (replyTunnelConfig) // outbound
{
m_FirstHop->isGateway = false;
m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ());
}
else
m_FirstHop->SetNextRouter (&i2p::context.GetRouterInfo ());
}
TunnelConfig (const i2p::data::RouterInfo * peer1, const i2p::data::RouterInfo * peer2, TunnelConfig * replyTunnelConfig = 0) // two hops
{
m_FirstHop = new TunnelHopConfig (peer1);
m_LastHop = new TunnelHopConfig (peer2);
m_FirstHop->SetNext (m_LastHop);
if (replyTunnelConfig)
{
m_FirstHop->isGateway = false;
m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ());
}
else
m_LastHop->SetNextRouter (&i2p::context.GetRouterInfo ());
}
~TunnelConfig ()
{
TunnelHopConfig * hop = m_FirstHop;
while (hop)
{
delete hop;
hop = hop->next;
}
}
TunnelHopConfig * GetFirstHop () const
{
return m_FirstHop;
}
TunnelHopConfig * GetLastHop () const
{
return m_LastHop;
}
size_t GetNumHops () const
{
size_t num = 0;
TunnelHopConfig * hop = m_FirstHop;
while (hop)
{
num++;
hop = hop->next;
}
return num;
}
void Print (std::stringstream& s) const
{
TunnelHopConfig * hop = m_FirstHop;
if (!m_FirstHop->isGateway)
s << "me";
s << "-->" << m_FirstHop->tunnelID;
while (hop)
{
s << ":" << hop->router->GetIdentHashAbbreviation () << "-->";
if (!hop->isEndpoint)
s << hop->nextTunnelID;
else
return;
hop = hop->next;
}
// we didn't reach enpoint that mean we are last hop
s << ":me";
}
TunnelConfig * Invert () const
{
TunnelConfig * newConfig = new TunnelConfig ();
TunnelHopConfig * hop = m_FirstHop, * nextNewHop = nullptr;
while (hop)
{
TunnelHopConfig * newHop = new TunnelHopConfig (hop->router);
if (nextNewHop)
newHop->SetNext (nextNewHop);
nextNewHop = newHop;
newHop->isEndpoint = hop->isGateway;
newHop->isGateway = hop->isEndpoint;
if (!hop->prev) // first hop
{
newConfig->m_LastHop = newHop;
if (hop->isGateway) // inbound tunnel
newHop->SetReplyHop (m_FirstHop); // use it as reply tunnel
}
if (!hop->next) newConfig->m_FirstHop = newHop; // last hop
hop = hop->next;
}
return newConfig;
}
private:
// this constructor can't be called from outside
TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr)
{
}
private:
TunnelHopConfig * m_FirstHop, * m_LastHop;
};
}
}
#endif