mirror of https://github.com/PurpleI2P/i2pd.git
orignal
11 years ago
3 changed files with 854 additions and 0 deletions
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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 |
@ -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 |
Loading…
Reference in new issue