mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-09 20:51:04 +00:00
commit
653a2be378
131
Garlic.cpp
131
Garlic.cpp
@ -4,6 +4,8 @@
|
||||
#include <string>
|
||||
#include "ElGamal.h"
|
||||
#include "RouterContext.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Tunnel.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Streaming.h"
|
||||
#include "Garlic.h"
|
||||
@ -29,7 +31,7 @@ namespace garlic
|
||||
delete[] m_SessionTags;
|
||||
}
|
||||
|
||||
I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg)
|
||||
I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet)
|
||||
{
|
||||
I2NPMessage * m = NewI2NPMessage ();
|
||||
size_t len = 0;
|
||||
@ -46,7 +48,7 @@ namespace garlic
|
||||
buf += 514;
|
||||
// AES block
|
||||
m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv);
|
||||
len += 514 + CreateAESBlock (buf, msg);
|
||||
len += 514 + CreateAESBlock (buf, msg, leaseSet);
|
||||
}
|
||||
else // existing session
|
||||
{
|
||||
@ -57,17 +59,18 @@ namespace garlic
|
||||
CryptoPP::SHA256().CalculateDigest(iv, m_SessionTags + m_NextTag*32, 32);
|
||||
m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv);
|
||||
// AES block
|
||||
len += 32 + CreateAESBlock (buf, msg);
|
||||
len += 32 + CreateAESBlock (buf, msg, leaseSet);
|
||||
}
|
||||
m_NextTag++;
|
||||
*(uint32_t *)(m->GetPayload ()) = htobe32 (len);
|
||||
m->len += len + 4;
|
||||
FillI2NPMessageHeader (m, eI2NPGarlic);
|
||||
DeleteI2NPMessage (msg);
|
||||
if (msg)
|
||||
DeleteI2NPMessage (msg);
|
||||
return m;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, I2NPMessage * msg)
|
||||
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, I2NPMessage * msg, I2NPMessage * leaseSet)
|
||||
{
|
||||
size_t blockSize = 0;
|
||||
*(uint16_t *)buf = htobe16 (m_NumTags); // tag count
|
||||
@ -80,7 +83,7 @@ namespace garlic
|
||||
blockSize += 32;
|
||||
buf[blockSize] = 0; // flag
|
||||
blockSize++;
|
||||
size_t len = CreateGarlicPayload (buf + blockSize, msg);
|
||||
size_t len = CreateGarlicPayload (buf + blockSize, msg, leaseSet);
|
||||
*payloadSize = htobe32 (len);
|
||||
CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len);
|
||||
blockSize += len;
|
||||
@ -91,41 +94,104 @@ namespace garlic
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg)
|
||||
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg, I2NPMessage * leaseSet)
|
||||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
|
||||
uint32_t msgID = m_Rnd.GenerateWord32 ();
|
||||
size_t size = 0;
|
||||
payload[size] = 1; // 1 clove
|
||||
uint8_t * numCloves = payload + size;
|
||||
*numCloves = 0;
|
||||
size++;
|
||||
if (m_Destination->IsDestination ())
|
||||
|
||||
if (leaseSet)
|
||||
{
|
||||
payload[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination
|
||||
size++;
|
||||
memcpy (payload + size, m_Destination->GetIdentHash (), 32);
|
||||
size += 32;
|
||||
// clove is DeliveryStatus is LeaseSet is presented
|
||||
size += CreateDeliveryStatusClove (payload + size, msgID);
|
||||
(*numCloves)++;
|
||||
|
||||
// clove is our leaseSet if presented
|
||||
size += CreateGarlicClove (payload + size, leaseSet, false);
|
||||
(*numCloves)++;
|
||||
}
|
||||
else
|
||||
if (msg) // clove message ifself if presented
|
||||
{
|
||||
payload[size] = 0;// delivery instructions flag local
|
||||
size++;
|
||||
}
|
||||
memcpy (payload + size, msg->GetBuffer (), msg->GetLength ());
|
||||
size += msg->GetLength ();
|
||||
*(uint32_t *)(payload + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
|
||||
size += 4;
|
||||
*(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of clove
|
||||
size += 8;
|
||||
memset (payload + size, 0, 3); // certificate of clove
|
||||
size += 3;
|
||||
size += CreateGarlicClove (payload + size, msg, m_Destination->IsDestination ());
|
||||
(*numCloves)++;
|
||||
}
|
||||
|
||||
memset (payload + size, 0, 3); // certificate of message
|
||||
size += 3;
|
||||
*(uint32_t *)(payload + size) = htobe32 (m_Rnd.GenerateWord32 ()); // MessageID
|
||||
*(uint32_t *)(payload + size) = htobe32 (msgID); // MessageID
|
||||
size += 4;
|
||||
*(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of message
|
||||
size += 8;
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, I2NPMessage * msg, bool isDestination)
|
||||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
|
||||
size_t size = 0;
|
||||
if (isDestination)
|
||||
{
|
||||
buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination
|
||||
size++;
|
||||
memcpy (buf + size, m_Destination->GetIdentHash (), 32);
|
||||
size += 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[size] = 0;// delivery instructions flag local
|
||||
size++;
|
||||
}
|
||||
|
||||
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
|
||||
size += msg->GetLength ();
|
||||
*(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
|
||||
size += 4;
|
||||
*(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove
|
||||
size += 8;
|
||||
memset (buf + size, 0, 3); // certificate of clove
|
||||
size += 3;
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID)
|
||||
{
|
||||
size_t size = 0;
|
||||
auto tunnel = i2p::tunnel::tunnels.GetNextInboundTunnel ();
|
||||
if (tunnel)
|
||||
{
|
||||
buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel
|
||||
size++;
|
||||
*(uint32_t *)(buf + size) = htobe32 (tunnel->GetNextTunnelID ()); // tunnelID
|
||||
size += 4;
|
||||
memcpy (buf + size, tunnel->GetNextIdentHash (), 32); // To Hash
|
||||
size += 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("No reply tunnels for garlic DeliveryStatus found");
|
||||
buf[size] = 0;// delivery instructions flag local
|
||||
size++;
|
||||
}
|
||||
|
||||
|
||||
I2NPMessage * msg = CreateDeliveryStatusMsg (msgID);
|
||||
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
|
||||
size += msg->GetLength ();
|
||||
DeleteI2NPMessage (msg);
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
|
||||
*(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
|
||||
size += 4;
|
||||
*(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove
|
||||
size += 8;
|
||||
memset (buf + size, 0, 3); // certificate of clove
|
||||
size += 3;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
GarlicRouting routing;
|
||||
GarlicRouting::GarlicRouting ()
|
||||
{
|
||||
@ -138,7 +204,8 @@ namespace garlic
|
||||
m_Sessions.clear ();
|
||||
}
|
||||
|
||||
I2NPMessage * GarlicRouting::WrapSingleMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * msg)
|
||||
I2NPMessage * GarlicRouting::WrapSingleMessage (const i2p::data::RoutingDestination * destination,
|
||||
I2NPMessage * msg, I2NPMessage * leaseSet)
|
||||
{
|
||||
if (!destination) return nullptr;
|
||||
auto it = m_Sessions.find (destination->GetIdentHash ());
|
||||
@ -151,7 +218,7 @@ namespace garlic
|
||||
m_Sessions[destination->GetIdentHash ()] = session;
|
||||
}
|
||||
|
||||
I2NPMessage * ret = session->WrapSingleMessage (msg);
|
||||
I2NPMessage * ret = session->WrapSingleMessage (msg, leaseSet);
|
||||
if (session->GetNumRemainingSessionTags () <= 0)
|
||||
{
|
||||
m_Sessions.erase (destination->GetIdentHash ());
|
||||
@ -160,7 +227,7 @@ namespace garlic
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GarlicRouting::HandleGarlicMessage (uint8_t * buf, size_t len)
|
||||
void GarlicRouting::HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel)
|
||||
{
|
||||
uint32_t length = be32toh (*(uint32_t *)buf);
|
||||
buf += 4;
|
||||
@ -178,7 +245,9 @@ namespace garlic
|
||||
{
|
||||
// new session
|
||||
ElGamalBlock elGamal;
|
||||
i2p::crypto::ElGamalDecrypt (i2p::context.GetLeaseSetPrivateKey (), buf, (uint8_t *)&elGamal, true);
|
||||
i2p::crypto::ElGamalDecrypt (
|
||||
isFromTunnel ? i2p::context.GetLeaseSetPrivateKey () : i2p::context.GetPrivateKey (),
|
||||
buf, (uint8_t *)&elGamal, true);
|
||||
memcpy (m_SessionKey, elGamal.sessionKey, 32);
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
|
||||
@ -227,7 +296,7 @@ namespace garlic
|
||||
{
|
||||
case eGarlicDeliveryTypeLocal:
|
||||
LogPrint ("Garlic type local");
|
||||
i2p::HandleI2NPMessage (buf, len);
|
||||
i2p::HandleI2NPMessage (buf, len, false);
|
||||
break;
|
||||
case eGarlicDeliveryTypeDestination:
|
||||
{
|
||||
|
13
Garlic.h
13
Garlic.h
@ -40,13 +40,15 @@ namespace garlic
|
||||
|
||||
GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags);
|
||||
~GarlicRoutingSession ();
|
||||
I2NPMessage * WrapSingleMessage (I2NPMessage * msg);
|
||||
I2NPMessage * WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet);
|
||||
int GetNumRemainingSessionTags () const { return m_NumTags - m_NextTag; };
|
||||
|
||||
private:
|
||||
|
||||
size_t CreateAESBlock (uint8_t * buf, I2NPMessage * msg);
|
||||
size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg);
|
||||
size_t CreateAESBlock (uint8_t * buf, I2NPMessage * msg, I2NPMessage * leaseSet);
|
||||
size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg, I2NPMessage * leaseSet);
|
||||
size_t CreateGarlicClove (uint8_t * buf, I2NPMessage * msg, bool isDestination);
|
||||
size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID);
|
||||
|
||||
private:
|
||||
|
||||
@ -66,9 +68,10 @@ namespace garlic
|
||||
GarlicRouting ();
|
||||
~GarlicRouting ();
|
||||
|
||||
void HandleGarlicMessage (uint8_t * buf, size_t len);
|
||||
void HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel);
|
||||
|
||||
I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * msg);
|
||||
I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination,
|
||||
I2NPMessage * msg, I2NPMessage * leaseSet = nullptr);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -104,15 +104,22 @@ namespace util
|
||||
s << "-->" << it.second->GetTunnelID ();
|
||||
else
|
||||
s << "-->" << it.second->GetTunnelID () << "-->";
|
||||
s << "<BR>";
|
||||
s << " " << it.second->GetNumTransmittedBytes () << "<BR>";
|
||||
}
|
||||
|
||||
s << "<P>Transports</P>";
|
||||
for (auto it: i2p::transports.GetNTCPSessions ())
|
||||
{
|
||||
// RouterInfo of incoming connection doesn't have address
|
||||
bool outgoing = it.second->GetRemoteRouterInfo ().GetNTCPAddress ();
|
||||
if (it.second->IsEstablished ())
|
||||
{
|
||||
if (outgoing) s << "-->";
|
||||
s << it.second->GetRemoteRouterInfo ().GetIdentHashAbbreviation () << ": "
|
||||
<< it.second->GetSocket ().remote_endpoint().address ().to_string () << "<BR>";
|
||||
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
|
||||
if (!outgoing) s << "-->";
|
||||
s << "<BR>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ namespace i2p
|
||||
return msg;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDeliveryStatusMsg ()
|
||||
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
|
||||
{
|
||||
#pragma pack(1)
|
||||
struct
|
||||
@ -77,13 +77,13 @@ namespace i2p
|
||||
} msg;
|
||||
#pragma pack ()
|
||||
|
||||
msg.msgID = 0;
|
||||
msg.msgID = htobe32 (msgID);
|
||||
msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ());
|
||||
return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg));
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
|
||||
uint32_t replyTunnelID, bool exploratory)
|
||||
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
|
||||
{
|
||||
I2NPMessage * m = NewI2NPMessage ();
|
||||
uint8_t * buf = m->GetPayload ();
|
||||
@ -113,15 +113,59 @@ namespace i2p
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing to exclude
|
||||
*(uint16_t *)buf = htobe16 (0);
|
||||
buf += 2;
|
||||
if (excludedPeers)
|
||||
{
|
||||
int cnt = excludedPeers->size ();
|
||||
*(uint16_t *)buf = htobe16 (cnt);
|
||||
buf += 2;
|
||||
for (auto& it: *excludedPeers)
|
||||
{
|
||||
memcpy (buf, it, 32);
|
||||
buf += 32;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing to exclude
|
||||
*(uint16_t *)buf = htobe16 (0);
|
||||
buf += 2;
|
||||
}
|
||||
}
|
||||
m->len += (buf - m->GetPayload ());
|
||||
FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
|
||||
return m;
|
||||
}
|
||||
|
||||
void HandleDatabaseLookupMsg (uint8_t * buf, size_t len)
|
||||
{
|
||||
char key[48];
|
||||
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
|
||||
key[l] = 0;
|
||||
LogPrint ("DatabaseLookup for ", key, " recieved");
|
||||
uint8_t flag = buf[64];
|
||||
uint32_t replyTunnelID = 0;
|
||||
if (flag & 0x01) //reply to yunnel
|
||||
replyTunnelID = be32toh (*(uint32_t *)(buf + 64));
|
||||
// TODO: implement search. We send non-found for now
|
||||
I2NPMessage * replyMsg = CreateDatabaseSearchReply (buf);
|
||||
if (replyTunnelID)
|
||||
i2p::tunnel::tunnels.GetNextOutboundTunnel ()->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg);
|
||||
else
|
||||
i2p::transports.SendMessage (buf, replyMsg);
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident)
|
||||
{
|
||||
I2NPMessage * m = NewI2NPMessage ();
|
||||
uint8_t * buf = m->GetPayload ();
|
||||
memcpy (buf, ident, 32);
|
||||
buf[32] = 0; // TODO:
|
||||
memcpy (buf + 33, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
|
||||
m->len += 65;
|
||||
FillI2NPMessageHeader (m, eI2NPDatabaseSearchReply);
|
||||
return m;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDatabaseStoreMsg ()
|
||||
{
|
||||
I2NPMessage * m = NewI2NPMessage ();
|
||||
@ -370,7 +414,7 @@ namespace i2p
|
||||
return be16toh (header->size) + sizeof (I2NPHeader);
|
||||
}
|
||||
|
||||
void HandleI2NPMessage (uint8_t * msg, size_t len)
|
||||
void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel)
|
||||
{
|
||||
I2NPHeader * header = (I2NPHeader *)msg;
|
||||
uint32_t msgID = be32toh (header->msgID);
|
||||
@ -382,7 +426,7 @@ namespace i2p
|
||||
{
|
||||
case eI2NPGarlic:
|
||||
LogPrint ("Garlic");
|
||||
i2p::garlic::routing.HandleGarlicMessage (buf, size);
|
||||
i2p::garlic::routing.HandleGarlicMessage (buf, size, isFromTunnel);
|
||||
break;
|
||||
break;
|
||||
case eI2NPDeliveryStatus:
|
||||
@ -395,13 +439,17 @@ namespace i2p
|
||||
case eI2NPVariableTunnelBuildReply:
|
||||
LogPrint ("VariableTunnelBuildReply");
|
||||
HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
|
||||
break;
|
||||
break;
|
||||
case eI2NPDatabaseLookup:
|
||||
LogPrint ("DatabaseLookup");
|
||||
HandleDatabaseLookupMsg (buf, size);
|
||||
break;
|
||||
default:
|
||||
LogPrint ("Unexpected message ", (int)header->typeID);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleI2NPMessage (I2NPMessage * msg)
|
||||
void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel)
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
@ -424,7 +472,7 @@ namespace i2p
|
||||
i2p::data::netdb.PostI2NPMsg (msg);
|
||||
break;
|
||||
default:
|
||||
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
|
||||
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength (), isFromTunnel);
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
#define I2NP_PROTOCOL_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <string.h>
|
||||
#include "RouterInfo.h"
|
||||
|
||||
@ -103,10 +103,13 @@ namespace i2p
|
||||
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0);
|
||||
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len);
|
||||
|
||||
I2NPMessage * CreateDeliveryStatusMsg ();
|
||||
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID);
|
||||
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
|
||||
uint32_t replyTunnelID, bool exploratory = false);
|
||||
|
||||
uint32_t replyTunnelID, bool exploratory = false,
|
||||
std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
|
||||
void HandleDatabaseLookupMsg (uint8_t * buf, size_t len);
|
||||
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident);
|
||||
|
||||
I2NPMessage * CreateDatabaseStoreMsg ();
|
||||
|
||||
I2NPBuildRequestRecordClearText CreateBuildRequestRecord (
|
||||
@ -132,8 +135,8 @@ namespace i2p
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg);
|
||||
|
||||
size_t GetI2NPMessageLength (uint8_t * msg);
|
||||
void HandleI2NPMessage (uint8_t * msg, size_t len);
|
||||
void HandleI2NPMessage (I2NPMessage * msg);
|
||||
void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel);
|
||||
void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
35
Identity.cpp
35
Identity.cpp
@ -1,3 +1,5 @@
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <cryptopp/sha.h>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <cryptopp/dh.h>
|
||||
@ -9,6 +11,14 @@ namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
Identity& Identity::operator=(const Keys& keys)
|
||||
{
|
||||
// copy public and signing keys together
|
||||
memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey));
|
||||
memset (certificate, 0, sizeof (certificate));
|
||||
return *this;
|
||||
}
|
||||
|
||||
IdentHash CalculateIdentHash (const Identity& identity)
|
||||
{
|
||||
IdentHash hash;
|
||||
@ -35,5 +45,30 @@ namespace data
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
RoutingKey CreateRoutingKey (const IdentHash& ident)
|
||||
{
|
||||
uint8_t buf[41]; // ident + yyyymmdd
|
||||
memcpy (buf, (const uint8_t *)ident, 32);
|
||||
time_t t = time (nullptr);
|
||||
struct tm tm;
|
||||
gmtime_r (&t, &tm);
|
||||
sprintf ((char *)(buf + 32),"%4i%2i%2i", tm.tm_year, tm.tm_mon, tm.tm_mday);
|
||||
|
||||
RoutingKey key;
|
||||
CryptoPP::SHA256().CalculateDigest(key.hash, buf, 40);
|
||||
return key;
|
||||
}
|
||||
|
||||
XORMetric operator^(const RoutingKey& key1, const RoutingKey& key2)
|
||||
{
|
||||
// TODO: implementation depends on CPU
|
||||
XORMetric m;
|
||||
((uint64_t *)m.metric)[0] = ((uint64_t *)key1.hash)[0] ^ ((uint64_t *)key2.hash)[0];
|
||||
((uint64_t *)m.metric)[1] = ((uint64_t *)key1.hash)[1] ^ ((uint64_t *)key2.hash)[1];
|
||||
((uint64_t *)m.metric)[2] = ((uint64_t *)key1.hash)[2] ^ ((uint64_t *)key2.hash)[2];
|
||||
((uint64_t *)m.metric)[3] = ((uint64_t *)key1.hash)[3] ^ ((uint64_t *)key2.hash)[3];
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
21
Identity.h
21
Identity.h
@ -23,6 +23,8 @@ namespace data
|
||||
uint8_t publicKey[256];
|
||||
uint8_t signingKey[128];
|
||||
uint8_t certificate[3];
|
||||
|
||||
Identity& operator=(const Keys& keys);
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
@ -59,7 +61,26 @@ namespace data
|
||||
|
||||
IdentHash CalculateIdentHash (const Identity& identity);
|
||||
Keys CreateRandomKeys ();
|
||||
|
||||
// kademlia
|
||||
struct RoutingKey
|
||||
{
|
||||
uint8_t hash[32];
|
||||
};
|
||||
|
||||
struct XORMetric
|
||||
{
|
||||
uint8_t metric[32];
|
||||
|
||||
void SetMin () { memset (metric, 0, 32); };
|
||||
void SetMax () { memset (metric, 0xFF, 32); };
|
||||
bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; };
|
||||
};
|
||||
|
||||
RoutingKey CreateRoutingKey (const IdentHash& ident);
|
||||
XORMetric operator^(const RoutingKey& key1, const RoutingKey& key2);
|
||||
|
||||
// destination for delivery instuctions
|
||||
class RoutingDestination
|
||||
{
|
||||
public:
|
||||
|
16
LeaseSet.cpp
16
LeaseSet.cpp
@ -1,3 +1,5 @@
|
||||
#include <cryptopp/dsa.h>
|
||||
#include "CryptoConst.h"
|
||||
#include "Log.h"
|
||||
#include "LeaseSet.h"
|
||||
|
||||
@ -24,10 +26,22 @@ namespace data
|
||||
memcpy (m_EncryptionKey, header->encryptionKey, 256);
|
||||
LogPrint ("LeaseSet num=", (int)header->num);
|
||||
|
||||
const uint8_t * leases = buf + sizeof (H);
|
||||
for (int i = 0; i < header->num; i++)
|
||||
{
|
||||
m_Leases.push_back (*(Lease *)(buf + sizeof (H)));
|
||||
Lease lease = *(Lease *)leases;
|
||||
lease.tunnelID = be32toh (lease.tunnelID);
|
||||
m_Leases.push_back (lease);
|
||||
leases += sizeof (Lease);
|
||||
}
|
||||
|
||||
// verify
|
||||
CryptoPP::DSA::PublicKey pubKey;
|
||||
pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
|
||||
CryptoPP::Integer (m_Identity.signingKey, 128));
|
||||
CryptoPP::DSA::Verifier verifier (pubKey);
|
||||
if (!verifier.VerifyMessage (buf, leases - buf, leases, 40))
|
||||
LogPrint ("LeaseSet verification failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
@ -31,12 +31,13 @@ namespace data
|
||||
// implements RoutingDestination
|
||||
const Identity& GetIdentity () const { return m_Identity; };
|
||||
const IdentHash& GetIdentHash () const { return m_IdentHash; };
|
||||
const std::vector<Lease>& GetLeases () const { return m_Leases; };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; };
|
||||
bool IsDestination () const { return true; };
|
||||
|
||||
private:
|
||||
|
||||
std::list<Lease> m_Leases;
|
||||
std::vector<Lease> m_Leases;
|
||||
Identity m_Identity;
|
||||
IdentHash m_IdentHash;
|
||||
uint8_t m_EncryptionKey[256];
|
||||
|
@ -20,12 +20,10 @@ namespace i2p
|
||||
{
|
||||
namespace ntcp
|
||||
{
|
||||
NTCPSession::NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo * in_RemoteRouterInfo):
|
||||
NTCPSession::NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo& in_RemoteRouterInfo):
|
||||
m_Socket (service), m_TerminationTimer (service), m_IsEstablished (false),
|
||||
m_ReceiveBufferOffset (0), m_NextMessage (nullptr)
|
||||
m_RemoteRouterInfo (in_RemoteRouterInfo), m_ReceiveBufferOffset (0), m_NextMessage (nullptr)
|
||||
{
|
||||
if (in_RemoteRouterInfo)
|
||||
m_RemoteRouterInfo = *in_RemoteRouterInfo;
|
||||
}
|
||||
|
||||
void NTCPSession::CreateAESKey (uint8_t * pubKey, uint8_t * aesKey)
|
||||
@ -186,7 +184,8 @@ namespace ntcp
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("Phase 2 read error: ", ecode.message ());
|
||||
LogPrint ("Phase 2 read error: ", ecode.message (), ". Wrong ident assumed");
|
||||
GetRemoteRouterInfo ().SetUnreachable (true); // this RouterInfo is not valid
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
@ -385,7 +384,8 @@ namespace ntcp
|
||||
if (m_ReceiveBufferOffset > 0)
|
||||
memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset);
|
||||
}
|
||||
|
||||
|
||||
ScheduleTermination (); // reset termination timer
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
@ -424,7 +424,7 @@ namespace ntcp
|
||||
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
|
||||
{
|
||||
// we have a complete I2NP message
|
||||
i2p::HandleI2NPMessage (m_NextMessage);
|
||||
i2p::HandleI2NPMessage (m_NextMessage, false);
|
||||
m_NextMessage = nullptr;
|
||||
}
|
||||
}
|
||||
@ -461,14 +461,10 @@ namespace ntcp
|
||||
m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding);
|
||||
|
||||
int l = len + padding + 6;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (m_EncryptionMutex);
|
||||
m_Encryption.ProcessData(sendBuffer, sendBuffer, l);
|
||||
}
|
||||
m_Encryption.ProcessData(sendBuffer, sendBuffer, l);
|
||||
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (sendBuffer, l), boost::asio::transfer_all (),
|
||||
boost::bind(&NTCPSession::HandleSent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, msg));
|
||||
ScheduleTermination (); // reset termination timer
|
||||
}
|
||||
|
||||
void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, i2p::I2NPMessage * msg)
|
||||
@ -483,6 +479,7 @@ namespace ntcp
|
||||
else
|
||||
{
|
||||
LogPrint ("Msg sent: ", bytes_transferred);
|
||||
ScheduleTermination (); // reset termination timer
|
||||
}
|
||||
}
|
||||
|
||||
@ -521,7 +518,8 @@ namespace ntcp
|
||||
|
||||
|
||||
NTCPClient::NTCPClient (boost::asio::io_service& service, const char * address,
|
||||
int port, i2p::data::RouterInfo& in_RouterInfo): NTCPSession (service, &in_RouterInfo),
|
||||
int port, i2p::data::RouterInfo& in_RouterInfo):
|
||||
NTCPSession (service, in_RouterInfo),
|
||||
m_Endpoint (boost::asio::ip::address::from_string (address), port)
|
||||
{
|
||||
Connect ();
|
||||
|
@ -2,7 +2,6 @@
|
||||
#define NTCP_SESSION_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <mutex>
|
||||
#include <list>
|
||||
#include <boost/asio.hpp>
|
||||
#include <cryptopp/modes.h>
|
||||
@ -66,7 +65,7 @@ namespace ntcp
|
||||
{
|
||||
public:
|
||||
|
||||
NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo * in_RemoteRouterInfo = 0);
|
||||
NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo& in_RemoteRouterInfo);
|
||||
virtual ~NTCPSession () {};
|
||||
|
||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
||||
@ -126,7 +125,7 @@ namespace ntcp
|
||||
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption;
|
||||
CryptoPP::Adler32 m_Adler;
|
||||
|
||||
i2p::data::RouterInfo m_RemoteRouterInfo;
|
||||
i2p::data::RouterInfo& m_RemoteRouterInfo;
|
||||
|
||||
NTCPPhase1 m_Phase1;
|
||||
NTCPPhase2 m_Phase2;
|
||||
@ -139,8 +138,6 @@ namespace ntcp
|
||||
i2p::I2NPMessage * m_NextMessage;
|
||||
std::list<i2p::I2NPMessage *> m_DelayedMessages;
|
||||
size_t m_NextMessageOffset;
|
||||
|
||||
std::mutex m_EncryptionMutex;
|
||||
};
|
||||
|
||||
class NTCPClient: public NTCPSession
|
||||
@ -163,11 +160,16 @@ namespace ntcp
|
||||
{
|
||||
public:
|
||||
|
||||
NTCPServerConnection (boost::asio::io_service& service): NTCPSession (service) {};
|
||||
NTCPServerConnection (boost::asio::io_service& service):
|
||||
NTCPSession (service, m_DummyRemoteRouterInfo) {};
|
||||
|
||||
protected:
|
||||
|
||||
virtual void Connected ();
|
||||
|
||||
private:
|
||||
|
||||
i2p::data::RouterInfo m_DummyRemoteRouterInfo;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
262
NetDb.cpp
262
NetDb.cpp
@ -8,16 +8,30 @@
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Tunnel.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Garlic.h"
|
||||
#include "NetDb.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
|
||||
I2NPMessage * RequestedDestination::CreateRequestMessage (const RouterInfo * router,
|
||||
const i2p::tunnel::InboundTunnel * replyTunnel)
|
||||
{
|
||||
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination,
|
||||
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, &m_ExcludedPeers);
|
||||
if (m_IsLeaseSet) // wrap lookup message into garlic
|
||||
msg = i2p::garlic::routing.WrapSingleMessage (router, msg);
|
||||
m_ExcludedPeers.insert (router->GetIdentHash ());
|
||||
m_LastRouter = router;
|
||||
m_LastReplyTunnel = replyTunnel;
|
||||
return msg;
|
||||
}
|
||||
|
||||
NetDb netdb;
|
||||
|
||||
NetDb::NetDb (): m_IsRunning (false), m_Thread (0), m_LastFloodfill (0)
|
||||
NetDb::NetDb (): m_IsRunning (false), m_Thread (0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -28,6 +42,8 @@ namespace data
|
||||
delete l.second;
|
||||
for (auto r:m_RouterInfos)
|
||||
delete r.second;
|
||||
for (auto r:m_RequestedDestinations)
|
||||
delete r.second;
|
||||
}
|
||||
|
||||
void NetDb::Start ()
|
||||
@ -70,7 +86,7 @@ namespace data
|
||||
else // WTF?
|
||||
{
|
||||
LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID);
|
||||
i2p::HandleI2NPMessage (msg);
|
||||
i2p::HandleI2NPMessage (msg, false);
|
||||
}
|
||||
msg = m_Queue.Get ();
|
||||
}
|
||||
@ -96,6 +112,7 @@ namespace data
|
||||
void NetDb::AddRouterInfo (uint8_t * buf, int len)
|
||||
{
|
||||
RouterInfo * r = new RouterInfo (buf, len);
|
||||
DeleteRequestedDestination (r->GetIdentHash ());
|
||||
auto it = m_RouterInfos.find(r->GetIdentHash ());
|
||||
if (it != m_RouterInfos.end ())
|
||||
{
|
||||
@ -117,6 +134,7 @@ namespace data
|
||||
void NetDb::AddLeaseSet (uint8_t * buf, int len)
|
||||
{
|
||||
LeaseSet * l = new LeaseSet (buf, len);
|
||||
DeleteRequestedDestination (l->GetIdentHash ());
|
||||
m_LeaseSets[l->GetIdentHash ()] = l;
|
||||
}
|
||||
|
||||
@ -128,6 +146,15 @@ namespace data
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LeaseSet * NetDb::FindLeaseSet (const IdentHash& destination) const
|
||||
{
|
||||
auto it = m_LeaseSets.find (destination);
|
||||
if (it != m_LeaseSets.end ())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NetDb::Load (const char * directory)
|
||||
{
|
||||
@ -156,39 +183,67 @@ namespace data
|
||||
|
||||
void NetDb::SaveUpdated (const char * directory)
|
||||
{
|
||||
int count = 0;
|
||||
auto GetFilePath = [](const char * directory, const RouterInfo * routerInfo)
|
||||
{
|
||||
return std::string (directory) + "/r" +
|
||||
routerInfo->GetIdentHashBase64 ()[0] + "/routerInfo-" +
|
||||
routerInfo->GetIdentHashBase64 () + ".dat";
|
||||
};
|
||||
|
||||
int count = 0, deletedCount = 0;
|
||||
for (auto it: m_RouterInfos)
|
||||
{
|
||||
if (it.second->IsUpdated ())
|
||||
{
|
||||
std::ofstream r (std::string (directory) + "/r" +
|
||||
it.second->GetIdentHashBase64 ()[0] + "/routerInfo-" +
|
||||
it.second->GetIdentHashBase64 () + ".dat");
|
||||
std::ofstream r (GetFilePath(directory, it.second));
|
||||
r.write ((char *)it.second->GetBuffer (), it.second->GetBufferLen ());
|
||||
it.second->SetUpdated (false);
|
||||
count++;
|
||||
}
|
||||
else if (it.second->IsUnreachable ())
|
||||
{
|
||||
if (boost::filesystem::exists (GetFilePath (directory, it.second)))
|
||||
{
|
||||
boost::filesystem::remove (GetFilePath (directory, it.second));
|
||||
deletedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count > 0)
|
||||
LogPrint (count," new/updated routers saved");
|
||||
if (deletedCount > 0)
|
||||
LogPrint (deletedCount," routers deleted");
|
||||
}
|
||||
|
||||
void NetDb::RequestDestination (const char * b32, const uint8_t * router)
|
||||
void NetDb::RequestDestination (const char * b32)
|
||||
{
|
||||
uint8_t destination[32];
|
||||
Base32ToByteStream (b32, strlen(b32), destination, 32);
|
||||
RequestDestination (destination, router);
|
||||
RequestDestination (destination, true);
|
||||
}
|
||||
|
||||
void NetDb::RequestDestination (const IdentHash& destination, bool isLeaseSet)
|
||||
{
|
||||
auto floodfill= GetRandomNTCPRouter (true);
|
||||
if (floodfill)
|
||||
RequestDestination (destination, floodfill, isLeaseSet);
|
||||
else
|
||||
LogPrint ("No floodfill routers found");
|
||||
}
|
||||
|
||||
void NetDb::RequestDestination (const uint8_t * destination, const uint8_t * router)
|
||||
void NetDb::RequestDestination (const IdentHash& destination, const RouterInfo * floodfill, bool isLeaseSet)
|
||||
{
|
||||
if (!floodfill) return;
|
||||
i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
|
||||
if (outbound)
|
||||
{
|
||||
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
|
||||
if (inbound)
|
||||
{
|
||||
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (destination, inbound->GetNextIdentHash (),
|
||||
inbound->GetNextTunnelID ());
|
||||
outbound->SendTunnelDataMsg (router, 0, msg);
|
||||
RequestedDestination * dest = CreateRequestedDestination (destination, isLeaseSet);
|
||||
dest->SetLastOutboundTunnel (outbound);
|
||||
auto msg = dest->CreateRequestMessage (floodfill, inbound);
|
||||
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
|
||||
}
|
||||
else
|
||||
LogPrint ("No inbound tunnels found");
|
||||
@ -196,7 +251,7 @@ namespace data
|
||||
else
|
||||
LogPrint ("No outbound tunnels found");
|
||||
}
|
||||
|
||||
|
||||
void NetDb::HandleDatabaseStoreMsg (uint8_t * buf, size_t len)
|
||||
{
|
||||
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)buf;
|
||||
@ -236,83 +291,133 @@ namespace data
|
||||
key[l] = 0;
|
||||
int num = buf[32]; // num
|
||||
LogPrint ("DatabaseSearchReply for ", key, " num=", num);
|
||||
if (num > 0)
|
||||
{
|
||||
bool isExploratory = !memcmp (m_Exploratory, buf, 32) && m_LastFloodfill;
|
||||
i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
|
||||
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
|
||||
auto it = m_RequestedDestinations.find (IdentHash (buf));
|
||||
if (it != m_RequestedDestinations.end ())
|
||||
{
|
||||
RequestedDestination * dest = it->second;
|
||||
if (num > 0)
|
||||
{
|
||||
i2p::tunnel::OutboundTunnel * outbound = dest->GetLastOutboundTunnel ();
|
||||
const i2p::tunnel::InboundTunnel * inbound = dest->GetLastReplyTunnel ();
|
||||
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
uint8_t * router = buf + 33 + i*32;
|
||||
char peerHash[48];
|
||||
int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48);
|
||||
peerHash[l1] = 0;
|
||||
LogPrint (i,": ", peerHash);
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
uint8_t * router = buf + 33 + i*32;
|
||||
char peerHash[48];
|
||||
int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48);
|
||||
peerHash[l1] = 0;
|
||||
LogPrint (i,": ", peerHash);
|
||||
|
||||
if (isExploratory)
|
||||
{
|
||||
if (m_RouterInfos.find (IdentHash(router)) == m_RouterInfos.end ())
|
||||
if (dest->IsExploratory ())
|
||||
{
|
||||
LogPrint ("Found new router. Requesting RouterInfo ...");
|
||||
if (!FindRouter (router)) // router with ident not found
|
||||
{
|
||||
LogPrint ("Found new router. Requesting RouterInfo ...");
|
||||
if (outbound && inbound)
|
||||
{
|
||||
RequestedDestination * d1 = CreateRequestedDestination (router, false, false);
|
||||
d1->SetLastOutboundTunnel (outbound);
|
||||
auto msg = d1->CreateRequestMessage (dest->GetLastRouter (), dest->GetLastReplyTunnel ());
|
||||
outbound->GetTunnelGateway ().PutTunnelDataMsg (dest->GetLastRouter ()->GetIdentHash (), 0, msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint ("Bayan");
|
||||
}
|
||||
else
|
||||
{
|
||||
// reply to our destination. Try other floodfills
|
||||
if (outbound && inbound)
|
||||
{
|
||||
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (router, inbound->GetNextIdentHash (),
|
||||
inbound->GetNextTunnelID ());
|
||||
outbound->GetTunnelGateway ().PutTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0, msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint ("Bayan");
|
||||
}
|
||||
else
|
||||
{
|
||||
// reply to our destination. Try other floodfills
|
||||
if (outbound && inbound)
|
||||
{
|
||||
// do we have that floodfill router in our database?
|
||||
if (!FindRouter (router))
|
||||
{
|
||||
// request router
|
||||
LogPrint ("Found new floodfill. Request it");
|
||||
msg = i2p::CreateDatabaseLookupMsg (router, inbound->GetNextIdentHash (),
|
||||
inbound->GetNextTunnelID ());
|
||||
outbound->GetTunnelGateway ().PutTunnelDataMsg (
|
||||
GetRandomNTCPRouter (true)->GetIdentHash (), 0, msg);
|
||||
// request destination
|
||||
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (buf, inbound->GetNextIdentHash (),
|
||||
inbound->GetNextTunnelID ());
|
||||
outbound->GetTunnelGateway ().PutTunnelDataMsg (router, 0, msg);
|
||||
auto r = FindRouter (router);
|
||||
// do we have that floodfill router in our database?
|
||||
if (r)
|
||||
{
|
||||
if (!dest->IsExcluded (r->GetIdentHash ()) && dest->GetNumExcludedPeers () < 10) // TODO: fix TunnelGateway first
|
||||
{
|
||||
// request destination
|
||||
auto msg = dest->CreateRequestMessage (r, dest->GetLastReplyTunnel ());
|
||||
outbound->GetTunnelGateway ().PutTunnelDataMsg (r->GetIdentHash (), 0, msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// request router
|
||||
LogPrint ("Found new floodfill. Request it");
|
||||
RequestedDestination * d2 = CreateRequestedDestination (router, false, false);
|
||||
d2->SetLastOutboundTunnel (outbound);
|
||||
I2NPMessage * msg = d2->CreateRequestMessage (dest->GetLastRouter (), inbound);
|
||||
outbound->GetTunnelGateway ().PutTunnelDataMsg (
|
||||
dest->GetLastRouter ()->GetIdentHash (), 0, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (outbound)
|
||||
outbound->GetTunnelGateway ().SendBuffer ();
|
||||
}
|
||||
if (outbound)
|
||||
outbound->GetTunnelGateway ().SendBuffer ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// no more requests for detination possible. delete it
|
||||
m_RequestedDestinations.erase (it);
|
||||
delete it->second;
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint ("Requested destination for ", key, " not found");
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
}
|
||||
|
||||
void NetDb::Explore ()
|
||||
{
|
||||
i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
|
||||
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
|
||||
auto outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
|
||||
auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
|
||||
if (outbound && inbound)
|
||||
{
|
||||
m_LastFloodfill = GetRandomNTCPRouter (true);
|
||||
if (m_LastFloodfill)
|
||||
auto floodfill = GetRandomNTCPRouter (true);
|
||||
if (floodfill)
|
||||
{
|
||||
LogPrint ("Exploring new routers ...");
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
rnd.GenerateBlock (m_Exploratory, 32);
|
||||
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Exploratory, inbound->GetNextIdentHash (),
|
||||
inbound->GetNextTunnelID (), true);
|
||||
outbound->SendTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0, msg);
|
||||
uint8_t randomHash[32];
|
||||
rnd.GenerateBlock (randomHash, 32);
|
||||
RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true);
|
||||
dest->SetLastOutboundTunnel (outbound);
|
||||
|
||||
outbound->GetTunnelGateway ().PutTunnelDataMsg (floodfill->GetIdentHash (), 0,
|
||||
CreateDatabaseStoreMsg ()); // tell floodfill about us
|
||||
outbound->GetTunnelGateway ().PutTunnelDataMsg (floodfill->GetIdentHash (), 0,
|
||||
dest->CreateRequestMessage (floodfill, inbound)); // explore
|
||||
outbound->GetTunnelGateway ().SendBuffer ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest,
|
||||
bool isLeaseSet, bool isExploratory)
|
||||
{
|
||||
auto it = m_RequestedDestinations.find (dest);
|
||||
if (it == m_RequestedDestinations.end ()) // not exist yet
|
||||
{
|
||||
RequestedDestination * d = new RequestedDestination (dest, isLeaseSet, isExploratory);
|
||||
m_RequestedDestinations[dest] = d;
|
||||
return d;
|
||||
}
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void NetDb::DeleteRequestedDestination (const IdentHash& dest)
|
||||
{
|
||||
auto it = m_RequestedDestinations.find (dest);
|
||||
if (it != m_RequestedDestinations.end ())
|
||||
{
|
||||
m_RequestedDestinations.erase (it);
|
||||
delete it->second;
|
||||
}
|
||||
}
|
||||
|
||||
const RouterInfo * NetDb::GetRandomNTCPRouter (bool floodfillOnly) const
|
||||
{
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
@ -345,5 +450,26 @@ namespace data
|
||||
{
|
||||
if (msg) m_Queue.Put (msg);
|
||||
}
|
||||
|
||||
const RouterInfo * NetDb::GetClosestFloodfill (const IdentHash& destination) const
|
||||
{
|
||||
RouterInfo * r = nullptr;
|
||||
XORMetric minMetric;
|
||||
RoutingKey destKey = CreateRoutingKey (destination);
|
||||
minMetric.SetMax ();
|
||||
for (auto it: m_RouterInfos)
|
||||
{
|
||||
if (it.second->IsFloodfill () &&! it.second->IsUnreachable ())
|
||||
{
|
||||
XORMetric m = destKey ^ it.second->GetRoutingKey ();
|
||||
if (m < minMetric)
|
||||
{
|
||||
minMetric = m;
|
||||
r = it.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
50
NetDb.h
50
NetDb.h
@ -2,6 +2,7 @@
|
||||
#define NETDB_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@ -9,11 +10,41 @@
|
||||
#include "I2NPProtocol.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "Tunnel.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
class RequestedDestination
|
||||
{
|
||||
public:
|
||||
|
||||
RequestedDestination (const IdentHash& destination, bool isLeaseSet, bool isExploratory = false):
|
||||
m_Destination (destination), m_IsLeaseSet (isLeaseSet), m_IsExploratory (isExploratory),
|
||||
m_LastRouter (nullptr), m_LastReplyTunnel (nullptr), m_LastOutboundTunnel (nullptr) {};
|
||||
|
||||
const IdentHash& GetDestination () const { return m_Destination; };
|
||||
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
|
||||
const RouterInfo * GetLastRouter () const { return m_LastRouter; };
|
||||
const i2p::tunnel::InboundTunnel * GetLastReplyTunnel () const { return m_LastReplyTunnel; };
|
||||
bool IsExploratory () const { return m_IsExploratory; };
|
||||
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
|
||||
I2NPMessage * CreateRequestMessage (const RouterInfo * router, const i2p::tunnel::InboundTunnel * replyTunnel);
|
||||
|
||||
i2p::tunnel::OutboundTunnel * GetLastOutboundTunnel () const { return m_LastOutboundTunnel; };
|
||||
void SetLastOutboundTunnel (i2p::tunnel::OutboundTunnel * tunnel) { m_LastOutboundTunnel = tunnel; };
|
||||
|
||||
private:
|
||||
|
||||
IdentHash m_Destination;
|
||||
bool m_IsLeaseSet, m_IsExploratory;
|
||||
std::set<IdentHash> m_ExcludedPeers;
|
||||
const RouterInfo * m_LastRouter;
|
||||
const i2p::tunnel::InboundTunnel * m_LastReplyTunnel;
|
||||
i2p::tunnel::OutboundTunnel * m_LastOutboundTunnel;
|
||||
};
|
||||
|
||||
class NetDb
|
||||
{
|
||||
public:
|
||||
@ -27,9 +58,12 @@ namespace data
|
||||
void AddRouterInfo (uint8_t * buf, int len);
|
||||
void AddLeaseSet (uint8_t * buf, int len);
|
||||
RouterInfo * FindRouter (const IdentHash& ident) const;
|
||||
|
||||
void RequestDestination (const char * b32, const uint8_t * router); // in base32
|
||||
void RequestDestination (const uint8_t * destination, const uint8_t * router);
|
||||
LeaseSet * FindLeaseSet (const IdentHash& destination) const;
|
||||
|
||||
void RequestDestination (const char * b32); // in base32
|
||||
void RequestDestination (const IdentHash& destination, bool isLeaseSet = false);
|
||||
void RequestDestination (const IdentHash& destination, const RouterInfo * floodfill, bool isLeaseSet = false);
|
||||
|
||||
void HandleDatabaseStoreMsg (uint8_t * buf, size_t len);
|
||||
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
|
||||
|
||||
@ -44,16 +78,20 @@ namespace data
|
||||
void SaveUpdated (const char * directory);
|
||||
void Run (); // exploratory thread
|
||||
void Explore ();
|
||||
const RouterInfo * GetClosestFloodfill (const IdentHash& destination) const;
|
||||
|
||||
RequestedDestination * CreateRequestedDestination (const IdentHash& dest,
|
||||
bool isLeaseSet, bool isExploratory = false);
|
||||
void DeleteRequestedDestination (const IdentHash& dest);
|
||||
|
||||
private:
|
||||
|
||||
std::map<IdentHash, LeaseSet *> m_LeaseSets;
|
||||
std::map<IdentHash, RouterInfo *> m_RouterInfos;
|
||||
|
||||
std::map<IdentHash, RequestedDestination *> m_RequestedDestinations;
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
uint8_t m_Exploratory[32];
|
||||
const RouterInfo * m_LastFloodfill;
|
||||
i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
|
||||
};
|
||||
|
||||
|
@ -26,9 +26,7 @@ namespace i2p
|
||||
CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
|
||||
|
||||
i2p::data::Identity ident;
|
||||
// copy public and signing keys together
|
||||
memcpy (ident.publicKey, m_Keys.publicKey, sizeof (ident.publicKey) + sizeof (ident.signingKey));
|
||||
memset (ident.certificate, 0, sizeof (ident.certificate));
|
||||
ident = m_Keys;
|
||||
m_RouterInfo.SetRouterIdentity (ident);
|
||||
|
||||
m_RouterInfo.AddNTCPAddress ("127.0.0.1", 17007); // TODO:
|
||||
|
@ -34,6 +34,8 @@ namespace data
|
||||
{
|
||||
m_RouterIdentity = identity;
|
||||
m_IdentHash = CalculateIdentHash (m_RouterIdentity);
|
||||
UpdateIdentHashBase64 ();
|
||||
UpdateRoutingKey ();
|
||||
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
@ -124,12 +126,23 @@ namespace data
|
||||
}
|
||||
|
||||
CryptoPP::SHA256().CalculateDigest(m_IdentHash, (uint8_t *)&m_RouterIdentity, sizeof (m_RouterIdentity));
|
||||
UpdateIdentHashBase64 ();
|
||||
UpdateRoutingKey ();
|
||||
}
|
||||
|
||||
void RouterInfo::UpdateIdentHashBase64 ()
|
||||
{
|
||||
size_t l = i2p::data::ByteStreamToBase64 (m_IdentHash, 32, m_IdentHashBase64, 48);
|
||||
m_IdentHashBase64[l] = 0;
|
||||
memcpy (m_IdentHashAbbreviation, m_IdentHashBase64, 4);
|
||||
m_IdentHashAbbreviation[4] = 0;
|
||||
}
|
||||
|
||||
void RouterInfo::UpdateRoutingKey ()
|
||||
{
|
||||
m_RoutingKey = CreateRoutingKey (m_IdentHash);
|
||||
}
|
||||
|
||||
void RouterInfo::WriteToStream (std::ostream& s)
|
||||
{
|
||||
s.write ((char *)&m_RouterIdentity, sizeof (m_RouterIdentity));
|
||||
|
@ -36,6 +36,7 @@ namespace data
|
||||
RouterInfo (const char * filename);
|
||||
RouterInfo () = default;
|
||||
RouterInfo (const RouterInfo& ) = default;
|
||||
RouterInfo& operator=(const RouterInfo& ) = default;
|
||||
RouterInfo (const uint8_t * buf, int len);
|
||||
|
||||
const Identity& GetRouterIdentity () const { return m_RouterIdentity; };
|
||||
@ -45,6 +46,7 @@ namespace data
|
||||
uint64_t GetTimestamp () const { return m_Timestamp; };
|
||||
const std::vector<Address>& GetAddresses () const { return m_Addresses; };
|
||||
Address * GetNTCPAddress ();
|
||||
const RoutingKey& GetRoutingKey () const { return m_RoutingKey; };
|
||||
|
||||
void AddNTCPAddress (const char * host, int port);
|
||||
void SetProperty (const char * key, const char * value);
|
||||
@ -55,6 +57,7 @@ namespace data
|
||||
bool IsUnreachable () const { return m_IsUnreachable; };
|
||||
|
||||
void CreateBuffer ();
|
||||
void UpdateRoutingKey ();
|
||||
const char * GetBuffer () const { return m_Buffer; };
|
||||
int GetBufferLen () const { return m_BufferLen; };
|
||||
|
||||
@ -74,11 +77,13 @@ namespace data
|
||||
void WriteToStream (std::ostream& s);
|
||||
size_t ReadString (char * str, std::istream& s);
|
||||
void WriteString (const std::string& str, std::ostream& s);
|
||||
void UpdateIdentHashBase64 ();
|
||||
|
||||
private:
|
||||
|
||||
Identity m_RouterIdentity;
|
||||
IdentHash m_IdentHash;
|
||||
RoutingKey m_RoutingKey;
|
||||
char m_IdentHashBase64[48], m_IdentHashAbbreviation[5];
|
||||
char m_Buffer[2048];
|
||||
int m_BufferLen;
|
||||
|
174
Streaming.cpp
174
Streaming.cpp
@ -4,14 +4,18 @@
|
||||
#include "Log.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Tunnel.h"
|
||||
#include "Timestamp.h"
|
||||
#include "CryptoConst.h"
|
||||
#include "Garlic.h"
|
||||
#include "Streaming.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace stream
|
||||
{
|
||||
Stream::Stream (const i2p::data::IdentHash& destination):
|
||||
m_SendStreamID (0)
|
||||
Stream::Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote):
|
||||
m_SendStreamID (0), m_SequenceNumber (0), m_LocalDestination (local), m_RemoteLeaseSet (remote)
|
||||
{
|
||||
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
|
||||
}
|
||||
@ -56,9 +60,68 @@ namespace stream
|
||||
std::string str((const char *)buf, end-buf);
|
||||
LogPrint ("Payload: ", str);
|
||||
}
|
||||
|
||||
size_t Stream::Send (uint8_t * buf, size_t len, int timeout)
|
||||
{
|
||||
uint8_t packet[STREAMING_MTU];
|
||||
size_t size = 0;
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID);
|
||||
size += 4; // sendStreamID
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID);
|
||||
size += 4; // receiveStreamID
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber);
|
||||
size += 4; // sequenceNum
|
||||
*(uint32_t *)(packet + size) = 0; // TODO
|
||||
size += 4; // ack Through
|
||||
packet[size] = 0;
|
||||
size++; // NACK count
|
||||
size++; // resend delay
|
||||
// TODO: for initial packet only, following packets have different falgs
|
||||
*(uint16_t *)(packet + size) = htobe16 (PACKET_FLAG_SYNCHRONIZE |
|
||||
PACKET_FLAG_FROM_INCLUDED | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_NO_ACK);
|
||||
size += 2; // flags
|
||||
*(uint16_t *)(packet + size) = htobe16 (sizeof (i2p::data::Identity) + 40); // identity + signature
|
||||
size += 2; // options size
|
||||
memcpy (packet + size, &m_LocalDestination->GetIdentity (), sizeof (i2p::data::Identity));
|
||||
size += sizeof (i2p::data::Identity); // from
|
||||
uint8_t * signature = packet + size; // set it later
|
||||
memset (signature, 0, 40); // zeroes for now
|
||||
size += 40; // signature
|
||||
memcpy (packet + size, buf, len);
|
||||
size += len; // payload
|
||||
m_LocalDestination->Sign (packet, size, signature);
|
||||
I2NPMessage * msg = i2p::garlic::routing.WrapSingleMessage (m_RemoteLeaseSet,
|
||||
CreateDataMessage (this, packet, size), m_LocalDestination->GetLeaseSet ());
|
||||
|
||||
auto outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
|
||||
if (outbound)
|
||||
{
|
||||
auto& lease = m_RemoteLeaseSet->GetLeases ()[0]; // TODO:
|
||||
outbound->SendTunnelDataMsg (lease.tunnelGateway, lease.tunnelID, msg);
|
||||
}
|
||||
else
|
||||
DeleteI2NPMessage (msg);
|
||||
return len;
|
||||
}
|
||||
|
||||
StreamingDestination * sharedLocalDestination = nullptr;
|
||||
|
||||
StreamingDestination::StreamingDestination (): m_LeaseSet (nullptr)
|
||||
{
|
||||
// TODO: read from file later
|
||||
m_Keys = i2p::data::CreateRandomKeys ();
|
||||
m_Identity = m_Keys;
|
||||
m_IdentHash = i2p::data::CalculateIdentHash (m_Identity);
|
||||
m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
|
||||
CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
|
||||
}
|
||||
|
||||
StreamingDestination::~StreamingDestination ()
|
||||
{
|
||||
if (m_LeaseSet)
|
||||
DeleteI2NPMessage (m_LeaseSet);
|
||||
}
|
||||
|
||||
StreamingDestination m_SharedLocalDestination;
|
||||
|
||||
void StreamingDestination::HandleNextPacket (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint32_t sendStreamID = *(uint32_t *)(buf);
|
||||
@ -69,25 +132,93 @@ namespace stream
|
||||
LogPrint ("Unknown stream ", sendStreamID);
|
||||
}
|
||||
|
||||
Stream * StreamingDestination::CreateNewStream (const i2p::data::IdentHash& destination)
|
||||
Stream * StreamingDestination::CreateNewStream (const i2p::data::LeaseSet * remote)
|
||||
{
|
||||
/*i2p::data::LeaseSet * leaseSet = i2p::data::netdb.FindLeaseSet (destination);
|
||||
if (!leaseSet)
|
||||
{
|
||||
i2p::data::netdb.RequestDestination (destination);
|
||||
sleep (5); // wait for 5 seconds
|
||||
leaseSet = i2p::data::netdb.FindLeaseSet (destination);
|
||||
if (!leaseSet)
|
||||
{
|
||||
LogPrint ("Couldn't find LeaseSet");
|
||||
return nullptr;
|
||||
}
|
||||
} */
|
||||
Stream * s = new Stream (destination);
|
||||
Stream * s = new Stream (this, remote);
|
||||
m_Streams[s->GetRecvStreamID ()] = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
void StreamingDestination::DeleteStream (Stream * stream)
|
||||
{
|
||||
if (stream)
|
||||
{
|
||||
m_Streams.erase (stream->GetRecvStreamID ());
|
||||
delete stream;
|
||||
}
|
||||
}
|
||||
|
||||
I2NPMessage * StreamingDestination::GetLeaseSet ()
|
||||
{
|
||||
if (!m_LeaseSet)
|
||||
m_LeaseSet = CreateLeaseSet ();
|
||||
else
|
||||
FillI2NPMessageHeader (m_LeaseSet, eI2NPDatabaseStore); // refresh msgID
|
||||
return m_LeaseSet;
|
||||
}
|
||||
|
||||
I2NPMessage * StreamingDestination::CreateLeaseSet () const
|
||||
{
|
||||
I2NPMessage * m = NewI2NPMessage ();
|
||||
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)m->GetPayload ();
|
||||
memcpy (msg->key, (const uint8_t *)m_IdentHash, 32);
|
||||
msg->type = 1; // LeaseSet
|
||||
msg->replyToken = 0;
|
||||
|
||||
uint8_t * buf = m->GetPayload () + sizeof (I2NPDatabaseStoreMsg);
|
||||
size_t size = 0;
|
||||
memcpy (buf + size, &m_Identity, sizeof (m_Identity));
|
||||
size += sizeof (m_Identity); // destination
|
||||
memcpy (buf + size, i2p::context.GetLeaseSetPublicKey (), 256);
|
||||
size += 256; // encryption key
|
||||
memset (buf + size, 0, 128);
|
||||
size += 128; // signing key
|
||||
auto tunnel = i2p::tunnel::tunnels.GetNextInboundTunnel ();
|
||||
if (tunnel)
|
||||
{
|
||||
buf[size] = 1; // 1 lease
|
||||
size++; // num
|
||||
memcpy (buf + size, (const uint8_t *)tunnel->GetNextIdentHash (), 32);
|
||||
size += 32; // tunnel_gw
|
||||
*(uint32_t *)(buf + size) = htobe32 (tunnel->GetNextTunnelID ());
|
||||
size += 4; // tunnel_id
|
||||
uint64_t ts = tunnel->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT;
|
||||
ts *= 1000; // in milliseconds
|
||||
*(uint64_t *)(buf + size) = htobe64 (ts);
|
||||
size += 8; // end_date
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[size] = 0; // zero leases
|
||||
size++; // num
|
||||
}
|
||||
Sign (buf, size, buf+ size);
|
||||
size += 40; // signature
|
||||
|
||||
m->len += size + sizeof (I2NPDatabaseStoreMsg);
|
||||
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
|
||||
return m;
|
||||
}
|
||||
|
||||
void StreamingDestination::Sign (uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
CryptoPP::DSA::Signer signer (m_SigningPrivateKey);
|
||||
signer.SignMessage (i2p::context.GetRandomNumberGenerator (), buf, len, signature);
|
||||
}
|
||||
|
||||
Stream * CreateStream (const i2p::data::LeaseSet * remote)
|
||||
{
|
||||
if (!sharedLocalDestination)
|
||||
sharedLocalDestination = new StreamingDestination ();
|
||||
return sharedLocalDestination->CreateNewStream (remote);
|
||||
}
|
||||
|
||||
void CloseStream (Stream * stream)
|
||||
{
|
||||
if (sharedLocalDestination)
|
||||
sharedLocalDestination->DeleteStream (stream);
|
||||
}
|
||||
|
||||
void HandleDataMessage (i2p::data::IdentHash * destination, const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint32_t length = be32toh (*(uint32_t *)buf);
|
||||
@ -104,7 +235,8 @@ namespace stream
|
||||
decompressor.Get (uncompressed, uncompressedSize);
|
||||
// then forward to streaming engine
|
||||
// TODO: we have onle one destination, might be more
|
||||
m_SharedLocalDestination.HandleNextPacket (uncompressed, uncompressedSize);
|
||||
if (sharedLocalDestination)
|
||||
sharedLocalDestination->HandleNextPacket (uncompressed, uncompressedSize);
|
||||
}
|
||||
else
|
||||
LogPrint ("Data: protocol ", buf[9], " is not supported");
|
||||
@ -118,7 +250,7 @@ namespace stream
|
||||
compressor.MessageEnd();
|
||||
int size = compressor.MaxRetrievable ();
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
*(uint16_t *)buf = htobe32 (size); // length
|
||||
*(uint32_t *)buf = htobe32 (size); // length
|
||||
buf += 4;
|
||||
compressor.Get (buf, size);
|
||||
buf[9] = 6; // streaming protocol
|
||||
|
41
Streaming.h
41
Streaming.h
@ -3,6 +3,8 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <map>
|
||||
#include <cryptopp/dsa.h>
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "I2NPProtocol.h"
|
||||
|
||||
@ -22,32 +24,63 @@ namespace stream
|
||||
const uint16_t PACKET_FLAG_ECHO = 0x0200;
|
||||
const uint16_t PACKET_FLAG_NO_ACK = 0x0400;
|
||||
|
||||
const size_t STREAMING_MTU = 1730;
|
||||
|
||||
class StreamingDestination;
|
||||
class Stream
|
||||
{
|
||||
public:
|
||||
|
||||
Stream (const i2p::data::IdentHash& destination);
|
||||
Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote);
|
||||
uint32_t GetSendStreamID () const { return m_SendStreamID; };
|
||||
uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
|
||||
|
||||
const i2p::data::LeaseSet * GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
|
||||
bool IsEstablished () const { return !m_SendStreamID; };
|
||||
|
||||
void HandleNextPacket (const uint8_t * buf, size_t len);
|
||||
size_t Send (uint8_t * buf, size_t len, int timeout); // timeout in seconds
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_SendStreamID, m_RecvStreamID;
|
||||
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
|
||||
StreamingDestination * m_LocalDestination;
|
||||
const i2p::data::LeaseSet * m_RemoteLeaseSet;
|
||||
};
|
||||
|
||||
class StreamingDestination
|
||||
{
|
||||
public:
|
||||
|
||||
Stream * CreateNewStream (const i2p::data::IdentHash& destination);
|
||||
StreamingDestination ();
|
||||
~StreamingDestination ();
|
||||
|
||||
const i2p::data::Keys& GetKeys () const { return m_Keys; };
|
||||
const i2p::data::Identity& GetIdentity () const { return m_Identity; };
|
||||
I2NPMessage * GetLeaseSet ();
|
||||
void Sign (uint8_t * buf, int len, uint8_t * signature) const;
|
||||
|
||||
Stream * CreateNewStream (const i2p::data::LeaseSet * remote);
|
||||
void DeleteStream (Stream * stream);
|
||||
void HandleNextPacket (const uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
I2NPMessage * CreateLeaseSet () const;
|
||||
|
||||
private:
|
||||
|
||||
std::map<uint32_t, Stream *> m_Streams;
|
||||
i2p::data::Keys m_Keys;
|
||||
i2p::data::Identity m_Identity;
|
||||
i2p::data::IdentHash m_IdentHash;
|
||||
|
||||
I2NPMessage * m_LeaseSet;
|
||||
|
||||
CryptoPP::DSA::PrivateKey m_SigningPrivateKey;
|
||||
};
|
||||
|
||||
Stream * CreateStream (const i2p::data::LeaseSet * remote);
|
||||
void CloseStream (Stream * stream);
|
||||
|
||||
// assuming data is I2CP message
|
||||
void HandleDataMessage (i2p::data::IdentHash * destination, const uint8_t * buf, size_t len);
|
||||
|
@ -14,7 +14,8 @@ namespace tunnel
|
||||
TransitTunnel::TransitTunnel (uint32_t receiveTunnelID,
|
||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
||||
const uint8_t * layerKey,const uint8_t * ivKey):
|
||||
m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent)
|
||||
m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID),
|
||||
m_NextIdent (nextIdent), m_NumTransmittedBytes (0)
|
||||
{
|
||||
memcpy (m_LayerKey, layerKey, 32);
|
||||
memcpy (m_IVKey, ivKey, 32);
|
||||
@ -43,6 +44,7 @@ namespace tunnel
|
||||
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
|
||||
|
||||
i2p::transports.SendMessage (m_NextIdent, tunnelMsg);
|
||||
m_NumTransmittedBytes += tunnelMsg->GetLength ();
|
||||
}
|
||||
|
||||
void TransitTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
|
||||
|
@ -23,6 +23,7 @@ namespace tunnel
|
||||
|
||||
virtual void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
|
||||
virtual void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
|
||||
virtual size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
|
||||
|
||||
uint32_t GetTunnelID () const { return m_TunnelID; };
|
||||
|
||||
@ -37,6 +38,7 @@ namespace tunnel
|
||||
i2p::data::IdentHash m_NextIdent;
|
||||
uint8_t m_LayerKey[32];
|
||||
uint8_t m_IVKey[32];
|
||||
size_t m_NumTransmittedBytes;
|
||||
|
||||
CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_ECBEncryption;
|
||||
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_CBCEncryption;
|
||||
@ -53,6 +55,7 @@ namespace tunnel
|
||||
layerKey, ivKey), m_Gateway(this) {};
|
||||
|
||||
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
|
||||
size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); };
|
||||
|
||||
private:
|
||||
|
||||
@ -69,6 +72,7 @@ namespace tunnel
|
||||
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey) {};
|
||||
|
||||
void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
|
||||
size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }
|
||||
|
||||
private:
|
||||
|
||||
|
@ -23,6 +23,7 @@ namespace i2p
|
||||
|
||||
void Transports::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&Transports::Run, this));
|
||||
// create acceptors
|
||||
auto addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
@ -48,6 +49,7 @@ namespace i2p
|
||||
m_NTCPSessions.clear ();
|
||||
delete m_NTCPAcceptor;
|
||||
|
||||
m_IsRunning = false;
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
@ -59,13 +61,16 @@ namespace i2p
|
||||
|
||||
void Transports::Run ()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
while (m_IsRunning)
|
||||
{
|
||||
LogPrint ("Transports: ", ex.what ());
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint ("Transports: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,27 +123,30 @@ namespace i2p
|
||||
{
|
||||
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
|
||||
// we send it to ourself
|
||||
i2p::HandleI2NPMessage (msg);
|
||||
i2p::HandleI2NPMessage (msg, false);
|
||||
else
|
||||
{
|
||||
auto session = FindNTCPSession (ident);
|
||||
if (!session)
|
||||
{
|
||||
RouterInfo * r = netdb.FindRouter (ident);
|
||||
if (r)
|
||||
m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg));
|
||||
}
|
||||
|
||||
void Transports::PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
|
||||
{
|
||||
auto session = FindNTCPSession (ident);
|
||||
if (!session)
|
||||
{
|
||||
RouterInfo * r = netdb.FindRouter (ident);
|
||||
if (r)
|
||||
{
|
||||
auto address = r->GetNTCPAddress ();
|
||||
if (address)
|
||||
{
|
||||
auto address = r->GetNTCPAddress ();
|
||||
if (address)
|
||||
{
|
||||
session = new i2p::ntcp::NTCPClient (m_Service, address->host.c_str (), address->port, *r);
|
||||
AddNTCPSession (session);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (session)
|
||||
session->SendI2NPMessage (msg);
|
||||
else
|
||||
LogPrint ("Session not found");
|
||||
session = new i2p::ntcp::NTCPClient (m_Service, address->host.c_str (), address->port, *r);
|
||||
AddNTCPSession (session);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (session)
|
||||
session->SendI2NPMessage (msg);
|
||||
else
|
||||
LogPrint ("Session not found");
|
||||
}
|
||||
}
|
||||
|
@ -36,9 +36,11 @@ namespace i2p
|
||||
|
||||
void Run ();
|
||||
void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error);
|
||||
void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
|
91
Tunnel.cpp
91
Tunnel.cpp
@ -14,8 +14,7 @@ namespace i2p
|
||||
namespace tunnel
|
||||
{
|
||||
|
||||
Tunnel::Tunnel (TunnelConfig * config): m_Config (config),
|
||||
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_IsEstablished (false)
|
||||
Tunnel::Tunnel (TunnelConfig * config): m_Config (config), m_IsEstablished (false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -67,14 +66,9 @@ namespace tunnel
|
||||
FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild);
|
||||
|
||||
if (outboundTunnel)
|
||||
{
|
||||
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
|
||||
else
|
||||
{
|
||||
i2p::transports.SendMessage (GetNextIdentHash (), msg);
|
||||
}
|
||||
}
|
||||
|
||||
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
|
||||
@ -220,7 +214,17 @@ namespace tunnel
|
||||
|
||||
OutboundTunnel * Tunnels::GetNextOutboundTunnel ()
|
||||
{
|
||||
OutboundTunnel * tunnel = nullptr;
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0;
|
||||
for (auto it: m_OutboundTunnels)
|
||||
{
|
||||
if (i >= ind) return it;
|
||||
else i++;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
// TODO: implement it base on profiling
|
||||
/*OutboundTunnel * tunnel = nullptr;
|
||||
size_t minSent = 0;
|
||||
for (auto it : m_OutboundTunnels)
|
||||
if (!tunnel || it->GetNumSentBytes () < minSent)
|
||||
@ -228,7 +232,7 @@ namespace tunnel
|
||||
tunnel = it;
|
||||
minSent = it->GetNumSentBytes ();
|
||||
}
|
||||
return tunnel;
|
||||
return tunnel;*/
|
||||
}
|
||||
|
||||
void Tunnels::AddTransitTunnel (TransitTunnel * tunnel)
|
||||
@ -311,6 +315,7 @@ namespace tunnel
|
||||
|
||||
ManageInboundTunnels ();
|
||||
ManageOutboundTunnels ();
|
||||
ManageTransitTunnels ();
|
||||
|
||||
/* if (!m_IsTunnelCreated)
|
||||
{
|
||||
@ -349,18 +354,24 @@ namespace tunnel
|
||||
{
|
||||
LogPrint ("Creating one hop outbound tunnel...");
|
||||
CreateTunnel<OutboundTunnel> (
|
||||
new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),
|
||||
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
|
||||
{
|
||||
i2p::data::netdb.GetRandomNTCPRouter ()
|
||||
},
|
||||
inboundTunnel->GetTunnelConfig ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
|
||||
//OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
|
||||
LogPrint ("Creating two hops outbound tunnel...");
|
||||
CreateTunnel<OutboundTunnel> (
|
||||
new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),
|
||||
i2p::data::netdb.GetRandomNTCPRouter (),
|
||||
inboundTunnel->GetTunnelConfig ()),
|
||||
outboundTunnel);
|
||||
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
|
||||
{
|
||||
i2p::data::netdb.GetRandomNTCPRouter (),
|
||||
i2p::data::netdb.GetRandomNTCPRouter ()
|
||||
},
|
||||
inboundTunnel->GetTunnelConfig ())/*,
|
||||
outboundTunnel*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -392,19 +403,42 @@ namespace tunnel
|
||||
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 3)
|
||||
{
|
||||
LogPrint ("Creating one hop inbound tunnel...");
|
||||
CreateTunnel<InboundTunnel> (new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter ()));
|
||||
CreateTunnel<InboundTunnel> (
|
||||
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
|
||||
{
|
||||
i2p::data::netdb.GetRandomNTCPRouter ()
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
|
||||
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
|
||||
LogPrint ("Creating two hops inbound tunnel...");
|
||||
auto router = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router;
|
||||
CreateTunnel<InboundTunnel> (
|
||||
new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),
|
||||
outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router),
|
||||
outboundTunnel);
|
||||
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
|
||||
{
|
||||
i2p::data::netdb.GetRandomNTCPRouter (),
|
||||
router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomNTCPRouter ()
|
||||
}),
|
||||
outboundTunnel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
LogPrint ("Transit tunnel ", it->second->GetTunnelID (), " expired");
|
||||
it = m_TransitTunnels.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::PostTunnelData (I2NPMessage * msg)
|
||||
{
|
||||
@ -437,7 +471,10 @@ namespace tunnel
|
||||
void Tunnels::CreateZeroHopsInboundTunnel ()
|
||||
{
|
||||
CreateTunnel<InboundTunnel> (
|
||||
new TunnelConfig (&i2p::context.GetRouterInfo ()));
|
||||
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
|
||||
{
|
||||
&i2p::context.GetRouterInfo ()
|
||||
}));
|
||||
}
|
||||
|
||||
OutboundTunnel * Tunnels::CreateOneHopOutboundTestTunnel (InboundTunnel * replyTunnel)
|
||||
@ -451,7 +488,9 @@ namespace tunnel
|
||||
if (peer)
|
||||
{
|
||||
const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo ();
|
||||
return CreateTunnel<InboundTunnel> (new TunnelConfig (&router), outboundTunnel);
|
||||
return CreateTunnel<InboundTunnel> (
|
||||
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>{&router}),
|
||||
outboundTunnel);
|
||||
}
|
||||
else
|
||||
LogPrint ("No established peers");
|
||||
@ -470,7 +509,11 @@ namespace tunnel
|
||||
{
|
||||
const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo ();
|
||||
return CreateTunnel<InboundTunnel> (
|
||||
new TunnelConfig (&router, &i2p::context.GetRouterInfo ()),
|
||||
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
|
||||
{
|
||||
&router,
|
||||
&i2p::context.GetRouterInfo ()
|
||||
}),
|
||||
outboundTunnel);
|
||||
}
|
||||
else
|
||||
|
14
Tunnel.h
14
Tunnel.h
@ -33,9 +33,7 @@ namespace 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);
|
||||
@ -54,7 +52,6 @@ namespace tunnel
|
||||
private:
|
||||
|
||||
TunnelConfig * m_Config;
|
||||
uint32_t m_CreationTime; // seconds since epoch
|
||||
bool m_IsEstablished;
|
||||
|
||||
CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption m_ECBDecryption;
|
||||
@ -70,9 +67,11 @@ namespace tunnel
|
||||
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 (); };
|
||||
|
||||
// implements TunnelBase
|
||||
uint32_t GetTunnelID () const { return GetNextTunnelID (); };
|
||||
|
||||
private:
|
||||
|
||||
@ -85,10 +84,10 @@ namespace tunnel
|
||||
|
||||
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 (); };
|
||||
|
||||
|
||||
// implements TunnelBase
|
||||
uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; };
|
||||
private:
|
||||
|
||||
TunnelEndpoint m_Endpoint;
|
||||
@ -128,6 +127,7 @@ namespace tunnel
|
||||
void ManageTunnels ();
|
||||
void ManageOutboundTunnels ();
|
||||
void ManageInboundTunnels ();
|
||||
void ManageTransitTunnels ();
|
||||
|
||||
void CreateZeroHopsInboundTunnel ();
|
||||
|
||||
|
10
TunnelBase.h
10
TunnelBase.h
@ -2,6 +2,7 @@
|
||||
#define TUNNEL_BASE_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "Timestamp.h"
|
||||
#include "I2NPProtocol.h"
|
||||
|
||||
namespace i2p
|
||||
@ -30,11 +31,20 @@ namespace tunnel
|
||||
{
|
||||
public:
|
||||
|
||||
TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
|
||||
virtual ~TunnelBase () {};
|
||||
|
||||
virtual void EncryptTunnelMsg (I2NPMessage * tunnelMsg) = 0;
|
||||
virtual uint32_t GetNextTunnelID () const = 0;
|
||||
virtual const i2p::data::IdentHash& GetNextIdentHash () const = 0;
|
||||
virtual uint32_t GetTunnelID () const = 0; // as known at our side
|
||||
|
||||
uint32_t GetCreationTime () const { return m_CreationTime; };
|
||||
void SetCreationTime (uint32_t t) { m_CreationTime = t; };
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_CreationTime; // seconds since epoch
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
|
||||
@ -83,34 +84,30 @@ namespace tunnel
|
||||
{
|
||||
public:
|
||||
|
||||
TunnelConfig (const i2p::data::RouterInfo * peer, TunnelConfig * replyTunnelConfig = 0) // one hop
|
||||
|
||||
TunnelConfig (std::vector<const i2p::data::RouterInfo *> peers,
|
||||
TunnelConfig * replyTunnelConfig = 0) // replyTunnelConfig=0 means inbound
|
||||
{
|
||||
m_FirstHop = new TunnelHopConfig (peer);
|
||||
m_LastHop = m_FirstHop;
|
||||
TunnelHopConfig * prev = nullptr;
|
||||
for (auto it: peers)
|
||||
{
|
||||
auto hop = new TunnelHopConfig (it);
|
||||
if (prev)
|
||||
prev->SetNext (hop);
|
||||
else
|
||||
m_FirstHop = hop;
|
||||
prev = hop;
|
||||
}
|
||||
m_LastHop = prev;
|
||||
|
||||
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
|
||||
else // inbound
|
||||
m_LastHop->SetNextRouter (&i2p::context.GetRouterInfo ());
|
||||
}
|
||||
}
|
||||
|
||||
~TunnelConfig ()
|
||||
{
|
||||
|
@ -147,7 +147,7 @@ namespace tunnel
|
||||
switch (msg.deliveryType)
|
||||
{
|
||||
case eDeliveryTypeLocal:
|
||||
i2p::HandleI2NPMessage (msg.data);
|
||||
i2p::HandleI2NPMessage (msg.data, true);
|
||||
break;
|
||||
case eDeliveryTypeTunnel:
|
||||
i2p::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
|
||||
|
@ -12,198 +12,144 @@ namespace tunnel
|
||||
{
|
||||
void TunnelGatewayBuffer::PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg)
|
||||
{
|
||||
TunnelMessageBlockExt * block = new TunnelMessageBlockExt;
|
||||
block->deliveryInstructionsLen = 1; // flag
|
||||
if (!m_CurrentTunnelDataMsg)
|
||||
CreateCurrentTunnelDataMessage ();
|
||||
|
||||
// create delivery instructions
|
||||
uint8_t di[43]; // max delivery instruction length is 43 for tunnel
|
||||
size_t diLen = 1;// flag
|
||||
TunnelDeliveryType dt = eDeliveryTypeLocal;
|
||||
if (gwHash)
|
||||
{
|
||||
block->deliveryInstructionsLen += 32; // hash
|
||||
memcpy (block->hash, gwHash, 32);
|
||||
if (gwTunnel)
|
||||
{
|
||||
block->deliveryType = eDeliveryTypeTunnel;
|
||||
block->deliveryInstructionsLen += 4; // tunnelID
|
||||
block->tunnelID = gwTunnel;
|
||||
}
|
||||
else
|
||||
block->deliveryType = eDeliveryTypeRouter;
|
||||
}
|
||||
else
|
||||
block->deliveryType = eDeliveryTypeLocal;
|
||||
block->deliveryInstructionsLen += 2; // size
|
||||
// we don't reserve 4 bytes for msgID yet
|
||||
block->totalLen = block->deliveryInstructionsLen + msg->GetLength ();
|
||||
block->data = msg;
|
||||
m_I2NPMsgs.push_back (block);
|
||||
|
||||
if (!m_Remaining) m_Remaining = TUNNEL_DATA_MAX_PAYLOAD_SIZE;
|
||||
if (block->totalLen <= m_Remaining) // message fits
|
||||
{
|
||||
block->isFragmented = false;
|
||||
m_Remaining -= block->totalLen;
|
||||
}
|
||||
else // message doesn't fit
|
||||
{
|
||||
if (block->deliveryInstructionsLen + 4 <= m_Remaining)
|
||||
{
|
||||
// delivery instructions of first fragment fits
|
||||
block->isFragmented = true;
|
||||
block->deliveryInstructionsLen += 4;
|
||||
block->totalLen += 4;
|
||||
m_Remaining = m_Remaining + TUNNEL_DATA_MAX_PAYLOAD_SIZE - block->totalLen - 7; // TODO: handle case if more than two fragments
|
||||
}
|
||||
else
|
||||
{
|
||||
// delivery instructions of first fragment don't fit
|
||||
block->isFragmented = false;
|
||||
m_Remaining = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<I2NPMessage *> TunnelGatewayBuffer::GetTunnelDataMsgs ()
|
||||
{
|
||||
m_Remaining = 0;
|
||||
m_NextOffset = 0;
|
||||
std::vector<I2NPMessage *> res;
|
||||
int cnt = m_I2NPMsgs.size ();
|
||||
if (cnt > 0)
|
||||
{
|
||||
int ind = 0;
|
||||
while (ind < cnt)
|
||||
{
|
||||
auto tunnelMsg = CreateNextTunnelMessage (ind);
|
||||
if (!tunnelMsg) break;
|
||||
res.push_back (tunnelMsg);
|
||||
*(uint32_t *)(di + diLen) = htobe32 (gwTunnel);
|
||||
diLen += 4; // tunnelID
|
||||
dt = eDeliveryTypeTunnel;
|
||||
}
|
||||
for (auto msg: m_I2NPMsgs)
|
||||
delete msg;
|
||||
m_I2NPMsgs.clear ();
|
||||
else
|
||||
dt = eDeliveryTypeRouter;
|
||||
|
||||
memcpy (di + diLen, gwHash, 32);
|
||||
diLen += 32; //len
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
di[0] = dt << 5; // set delivery type
|
||||
|
||||
size_t TunnelGatewayBuffer::CreateFirstFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len)
|
||||
{
|
||||
size_t ret = 1;
|
||||
buf[0] = block->deliveryType << 5; // flag
|
||||
if (block->deliveryType == eDeliveryTypeTunnel)
|
||||
// create fragments
|
||||
if (diLen + msg->GetLength () + 2<= m_RemainingSize)
|
||||
{
|
||||
*(uint32_t *)(buf + ret) = htobe32 (block->tunnelID);
|
||||
ret += 4;
|
||||
}
|
||||
if (block->deliveryType == eDeliveryTypeTunnel || block->deliveryType == eDeliveryTypeRouter)
|
||||
{
|
||||
memcpy (buf + ret, block->hash, 32);
|
||||
ret += 32;
|
||||
// message fits. First and last fragment
|
||||
*(uint16_t *)(di + diLen) = htobe16 (msg->GetLength ());
|
||||
diLen += 2; // size
|
||||
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen);
|
||||
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), msg->GetLength ());
|
||||
m_CurrentTunnelDataMsg->len += diLen + msg->GetLength ();
|
||||
m_RemainingSize -= diLen + msg->GetLength ();
|
||||
if (!m_RemainingSize)
|
||||
CompleteCurrentTunnelDataMessage ();
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
size_t size = block->data->GetLength ();
|
||||
if (block->totalLen > len) // entire message doesn't fit
|
||||
{
|
||||
buf[0] |= 0x08; // set fragmented bit
|
||||
m_NextMsgID = block->data->GetHeader ()->msgID;
|
||||
*(uint32_t *)(buf + ret) = m_NextMsgID;
|
||||
ret += 4; // msgID
|
||||
m_NextSeqn = 1;
|
||||
size = len - ret - 2; // 2 bytes for size field
|
||||
m_NextOffset = size;
|
||||
}
|
||||
*(uint16_t *)(buf + ret) = htobe16 (size); // size
|
||||
ret += 2;
|
||||
memcpy (buf + ret, block->data->GetBuffer (), size);
|
||||
ret += size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t TunnelGatewayBuffer::CreateFollowOnFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len)
|
||||
{
|
||||
int ret = 0;
|
||||
buf[0] = 0x80 | (m_NextSeqn << 1);// follow-on flag and seqn
|
||||
size_t fragmentLen = len - 7; // 7 bytes of header
|
||||
if (fragmentLen >= block->data->GetLength () - m_NextOffset)
|
||||
{
|
||||
// fragment fits
|
||||
fragmentLen = block->data->GetLength () - m_NextOffset;
|
||||
buf[0] |= 0x01; // last fragment
|
||||
}
|
||||
else
|
||||
m_NextSeqn++;
|
||||
{
|
||||
if (diLen + 6 <= m_RemainingSize)
|
||||
{
|
||||
// delivery instructions fit
|
||||
uint32_t msgID = msg->GetHeader ()->msgID;
|
||||
size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size)
|
||||
|
||||
*(uint32_t *)(buf + 1) = m_NextMsgID; // msgID
|
||||
*(uint16_t *)(buf + 5) = htobe16 (fragmentLen); // size
|
||||
memcpy (buf + 7, block->data->GetBuffer () + m_NextOffset, fragmentLen);
|
||||
|
||||
m_NextOffset += fragmentLen;
|
||||
ret += fragmentLen + 7;
|
||||
|
||||
// first fragment
|
||||
di[0] |= 0x08; // fragmented
|
||||
*(uint32_t *)(di + diLen) = htobe32 (msgID);
|
||||
diLen += 4; // Message ID
|
||||
*(uint16_t *)(di + diLen) = htobe16 (size);
|
||||
diLen += 2; // size
|
||||
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen);
|
||||
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), size);
|
||||
m_CurrentTunnelDataMsg->len += diLen + size;
|
||||
CompleteCurrentTunnelDataMessage ();
|
||||
// follow on fragments
|
||||
int fragmentNumber = 1;
|
||||
while (size < msg->GetLength ())
|
||||
{
|
||||
CreateCurrentTunnelDataMessage ();
|
||||
uint8_t * buf = m_CurrentTunnelDataMsg->GetBuffer ();
|
||||
buf[0] = 0x80 | (fragmentNumber << 1); // frag
|
||||
bool isLastFragment = false;
|
||||
size_t s = msg->GetLength () - size;
|
||||
if (s > TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7) // 7 follow on instructions
|
||||
s = TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7;
|
||||
else // last fragment
|
||||
{
|
||||
buf[0] |= 0x01;
|
||||
isLastFragment = true;
|
||||
}
|
||||
*(uint32_t *)(buf + 1) = htobe32 (msgID); //Message ID
|
||||
*(uint16_t *)(buf + 5) = htobe16 (s); // size
|
||||
memcpy (buf + 7, msg->GetBuffer () + size, s);
|
||||
m_CurrentTunnelDataMsg->len += s+7;
|
||||
if (isLastFragment)
|
||||
{
|
||||
m_RemainingSize -= s+7;
|
||||
if (!m_RemainingSize)
|
||||
CompleteCurrentTunnelDataMessage ();
|
||||
}
|
||||
else
|
||||
CompleteCurrentTunnelDataMessage ();
|
||||
size += s;
|
||||
fragmentNumber++;
|
||||
}
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// delivery instructions don't fit. Create new message
|
||||
CompleteCurrentTunnelDataMessage ();
|
||||
PutI2NPMsg (gwHash, gwTunnel, msg);
|
||||
// don't delete msg because it's taken care inside
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<I2NPMessage *> TunnelGatewayBuffer::GetTunnelDataMsgs ()
|
||||
{
|
||||
CompleteCurrentTunnelDataMessage ();
|
||||
std::vector<I2NPMessage *> ret = m_TunnelDataMsgs; // TODO: implement it better
|
||||
m_TunnelDataMsgs.clear ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
I2NPMessage * TunnelGatewayBuffer::CreateNextTunnelMessage (int& ind)
|
||||
void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage ()
|
||||
{
|
||||
int cnt = m_I2NPMsgs.size ();
|
||||
if (ind > cnt - 1) return nullptr; // no more messages
|
||||
// calculate payload size
|
||||
size_t size = 0;
|
||||
int i = ind;
|
||||
if (m_NextOffset)
|
||||
{
|
||||
size = m_I2NPMsgs[i]->data->GetLength () - m_NextOffset + 7; // including follow-on header
|
||||
i++;
|
||||
}
|
||||
while (i < cnt)
|
||||
{
|
||||
auto msg = m_I2NPMsgs[i];
|
||||
size += msg->totalLen;
|
||||
if (size >= TUNNEL_DATA_MAX_PAYLOAD_SIZE)
|
||||
{
|
||||
size = TUNNEL_DATA_MAX_PAYLOAD_SIZE;
|
||||
break;
|
||||
}
|
||||
if (msg->isFragmented) break;
|
||||
i++;
|
||||
}
|
||||
m_CurrentTunnelDataMsg = NewI2NPMessage ();
|
||||
// we reserve space for padding
|
||||
m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + sizeof (I2NPHeader);
|
||||
m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset;
|
||||
m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE;
|
||||
}
|
||||
|
||||
void TunnelGatewayBuffer::CompleteCurrentTunnelDataMessage ()
|
||||
{
|
||||
if (!m_CurrentTunnelDataMsg) return;
|
||||
uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer ();
|
||||
size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset;
|
||||
|
||||
I2NPMessage * tunnelMsg = NewI2NPMessage ();
|
||||
uint8_t * buf = tunnelMsg->GetPayload ();
|
||||
m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - sizeof (I2NPHeader);
|
||||
uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload ();
|
||||
*(uint32_t *)(buf) = htobe32 (m_TunnelID);
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
rnd.GenerateBlock (buf + 4, 16); // original IV
|
||||
memcpy (buf + TUNNEL_DATA_MSG_SIZE, buf + 4, 16); // copy IV for checksum
|
||||
size_t zero = TUNNEL_DATA_MSG_SIZE - size -1;
|
||||
buf[zero] = 0; // zero
|
||||
size_t s = 0;
|
||||
while (ind < cnt)
|
||||
{
|
||||
auto msg = m_I2NPMsgs[ind];
|
||||
if (m_NextOffset)
|
||||
{
|
||||
s += CreateFollowOnFragment (msg, buf + zero + 1 + s, size - s);
|
||||
m_NextOffset = 0; // TODO:
|
||||
}
|
||||
else
|
||||
{
|
||||
s += CreateFirstFragment (msg, buf + zero + 1 + s, size - s);
|
||||
if (msg->isFragmented) break; // payload is full, but we stay at the same message
|
||||
}
|
||||
ind++;
|
||||
if (s >= size) break; // payload is full but we moved to next message
|
||||
}
|
||||
|
||||
if (s != size)
|
||||
{
|
||||
LogPrint ("TunnelData payload size mismatch ", s, "!=", size);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memcpy (payload + size, buf + 4, 16); // copy IV for checksum
|
||||
uint8_t hash[32];
|
||||
CryptoPP::SHA256().CalculateDigest(hash, buf+zero+1, size+16);
|
||||
memcpy (buf+20, hash, 4); // checksum
|
||||
if (zero > 24)
|
||||
memset (buf+24, 1, zero-24); // padding TODO: fill with random data
|
||||
tunnelMsg->len += TUNNEL_DATA_MSG_SIZE;
|
||||
CryptoPP::SHA256().CalculateDigest (hash, payload, size+16);
|
||||
memcpy (buf+20, hash, 4); // checksum
|
||||
payload[-1] = 0; // zero
|
||||
ssize_t paddingSize = payload - buf - 25; // 25 = 24 + 1
|
||||
if (paddingSize > 0)
|
||||
memset (buf + 24, 1, paddingSize); // padding TODO: fill with random data
|
||||
|
||||
// we can't fill message header yet because encryption is required
|
||||
return tunnelMsg;
|
||||
m_TunnelDataMsgs.push_back (m_CurrentTunnelDataMsg);
|
||||
m_CurrentTunnelDataMsg = nullptr;
|
||||
}
|
||||
|
||||
void TunnelGateway::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
|
||||
|
@ -12,32 +12,23 @@ namespace tunnel
|
||||
{
|
||||
class TunnelGatewayBuffer
|
||||
{
|
||||
struct TunnelMessageBlockExt: public TunnelMessageBlock
|
||||
{
|
||||
size_t deliveryInstructionsLen, totalLen;
|
||||
bool isFragmented;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID), m_Remaining (0) {};
|
||||
|
||||
TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID),
|
||||
m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) {};
|
||||
void PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg);
|
||||
std::vector<I2NPMessage *> GetTunnelDataMsgs ();
|
||||
|
||||
private:
|
||||
|
||||
size_t CreateFirstFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len);
|
||||
size_t CreateFollowOnFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len);
|
||||
I2NPMessage * CreateNextTunnelMessage (int& ind);
|
||||
void CreateCurrentTunnelDataMessage ();
|
||||
void CompleteCurrentTunnelDataMessage ();
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_TunnelID;
|
||||
std::vector<TunnelMessageBlockExt *> m_I2NPMsgs;
|
||||
// for fragmented messages
|
||||
size_t m_NextOffset, m_NextSeqn, m_Remaining;
|
||||
uint32_t m_NextMsgID;
|
||||
std::vector<I2NPMessage *> m_TunnelDataMsgs;
|
||||
I2NPMessage * m_CurrentTunnelDataMsg;
|
||||
size_t m_RemainingSize;
|
||||
};
|
||||
|
||||
class TunnelGateway
|
||||
|
Loading…
x
Reference in New Issue
Block a user