Merge pull request #1 from orignal/master

merge with master
pull/7/head
mikhail4021 11 years ago
commit 653a2be378

@ -4,6 +4,8 @@
#include <string> #include <string>
#include "ElGamal.h" #include "ElGamal.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "I2NPProtocol.h"
#include "Tunnel.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "Streaming.h" #include "Streaming.h"
#include "Garlic.h" #include "Garlic.h"
@ -29,7 +31,7 @@ namespace garlic
delete[] m_SessionTags; delete[] m_SessionTags;
} }
I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg) I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet)
{ {
I2NPMessage * m = NewI2NPMessage (); I2NPMessage * m = NewI2NPMessage ();
size_t len = 0; size_t len = 0;
@ -46,7 +48,7 @@ namespace garlic
buf += 514; buf += 514;
// AES block // AES block
m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv);
len += 514 + CreateAESBlock (buf, msg); len += 514 + CreateAESBlock (buf, msg, leaseSet);
} }
else // existing session else // existing session
{ {
@ -57,17 +59,18 @@ namespace garlic
CryptoPP::SHA256().CalculateDigest(iv, m_SessionTags + m_NextTag*32, 32); CryptoPP::SHA256().CalculateDigest(iv, m_SessionTags + m_NextTag*32, 32);
m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv);
// AES block // AES block
len += 32 + CreateAESBlock (buf, msg); len += 32 + CreateAESBlock (buf, msg, leaseSet);
} }
m_NextTag++; m_NextTag++;
*(uint32_t *)(m->GetPayload ()) = htobe32 (len); *(uint32_t *)(m->GetPayload ()) = htobe32 (len);
m->len += len + 4; m->len += len + 4;
FillI2NPMessageHeader (m, eI2NPGarlic); FillI2NPMessageHeader (m, eI2NPGarlic);
DeleteI2NPMessage (msg); if (msg)
DeleteI2NPMessage (msg);
return m; 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; size_t blockSize = 0;
*(uint16_t *)buf = htobe16 (m_NumTags); // tag count *(uint16_t *)buf = htobe16 (m_NumTags); // tag count
@ -80,7 +83,7 @@ namespace garlic
blockSize += 32; blockSize += 32;
buf[blockSize] = 0; // flag buf[blockSize] = 0; // flag
blockSize++; blockSize++;
size_t len = CreateGarlicPayload (buf + blockSize, msg); size_t len = CreateGarlicPayload (buf + blockSize, msg, leaseSet);
*payloadSize = htobe32 (len); *payloadSize = htobe32 (len);
CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len); CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len);
blockSize += len; blockSize += len;
@ -91,41 +94,104 @@ namespace garlic
return blockSize; 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 uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
uint32_t msgID = m_Rnd.GenerateWord32 ();
size_t size = 0; size_t size = 0;
payload[size] = 1; // 1 clove uint8_t * numCloves = payload + size;
*numCloves = 0;
size++; size++;
if (m_Destination->IsDestination ())
if (leaseSet)
{ {
payload[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination // 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)++;
}
if (msg) // clove message ifself if presented
{
size += CreateGarlicClove (payload + size, msg, m_Destination->IsDestination ());
(*numCloves)++;
}
memset (payload + size, 0, 3); // certificate of message
size += 3;
*(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++; size++;
memcpy (payload + size, m_Destination->GetIdentHash (), 32); memcpy (buf + size, m_Destination->GetIdentHash (), 32);
size += 32; size += 32;
} }
else else
{ {
payload[size] = 0;// delivery instructions flag local buf[size] = 0;// delivery instructions flag local
size++; size++;
} }
memcpy (payload + size, msg->GetBuffer (), msg->GetLength ());
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength (); size += msg->GetLength ();
*(uint32_t *)(payload + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID *(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
size += 4; size += 4;
*(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of clove *(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove
size += 8; size += 8;
memset (payload + size, 0, 3); // certificate of clove memset (buf + size, 0, 3); // certificate of clove
size += 3; size += 3;
memset (payload + size, 0, 3); // certificate of message
size += 3;
*(uint32_t *)(payload + size) = htobe32 (m_Rnd.GenerateWord32 ()); // MessageID
size += 4;
*(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of message
size += 8;
return size; 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 routing;
GarlicRouting::GarlicRouting () GarlicRouting::GarlicRouting ()
{ {
@ -138,7 +204,8 @@ namespace garlic
m_Sessions.clear (); 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; if (!destination) return nullptr;
auto it = m_Sessions.find (destination->GetIdentHash ()); auto it = m_Sessions.find (destination->GetIdentHash ());
@ -151,7 +218,7 @@ namespace garlic
m_Sessions[destination->GetIdentHash ()] = session; m_Sessions[destination->GetIdentHash ()] = session;
} }
I2NPMessage * ret = session->WrapSingleMessage (msg); I2NPMessage * ret = session->WrapSingleMessage (msg, leaseSet);
if (session->GetNumRemainingSessionTags () <= 0) if (session->GetNumRemainingSessionTags () <= 0)
{ {
m_Sessions.erase (destination->GetIdentHash ()); m_Sessions.erase (destination->GetIdentHash ());
@ -160,7 +227,7 @@ namespace garlic
return ret; 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); uint32_t length = be32toh (*(uint32_t *)buf);
buf += 4; buf += 4;
@ -178,7 +245,9 @@ namespace garlic
{ {
// new session // new session
ElGamalBlock elGamal; 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); memcpy (m_SessionKey, elGamal.sessionKey, 32);
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
@ -227,7 +296,7 @@ namespace garlic
{ {
case eGarlicDeliveryTypeLocal: case eGarlicDeliveryTypeLocal:
LogPrint ("Garlic type local"); LogPrint ("Garlic type local");
i2p::HandleI2NPMessage (buf, len); i2p::HandleI2NPMessage (buf, len, false);
break; break;
case eGarlicDeliveryTypeDestination: case eGarlicDeliveryTypeDestination:
{ {

@ -40,13 +40,15 @@ namespace garlic
GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags); GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags);
~GarlicRoutingSession (); ~GarlicRoutingSession ();
I2NPMessage * WrapSingleMessage (I2NPMessage * msg); I2NPMessage * WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet);
int GetNumRemainingSessionTags () const { return m_NumTags - m_NextTag; }; int GetNumRemainingSessionTags () const { return m_NumTags - m_NextTag; };
private: private:
size_t CreateAESBlock (uint8_t * buf, I2NPMessage * msg); size_t CreateAESBlock (uint8_t * buf, I2NPMessage * msg, I2NPMessage * leaseSet);
size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg); 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: private:
@ -66,9 +68,10 @@ namespace garlic
GarlicRouting (); GarlicRouting ();
~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: private:

@ -104,15 +104,22 @@ namespace util
s << "-->" << it.second->GetTunnelID (); s << "-->" << it.second->GetTunnelID ();
else else
s << "-->" << it.second->GetTunnelID () << "-->"; s << "-->" << it.second->GetTunnelID () << "-->";
s << "<BR>"; s << " " << it.second->GetNumTransmittedBytes () << "<BR>";
} }
s << "<P>Transports</P>"; s << "<P>Transports</P>";
for (auto it: i2p::transports.GetNTCPSessions ()) 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 (it.second->IsEstablished ())
{
if (outgoing) s << "-->";
s << it.second->GetRemoteRouterInfo ().GetIdentHashAbbreviation () << ": " 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; return msg;
} }
I2NPMessage * CreateDeliveryStatusMsg () I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
{ {
#pragma pack(1) #pragma pack(1)
struct struct
@ -77,13 +77,13 @@ namespace i2p
} msg; } msg;
#pragma pack () #pragma pack ()
msg.msgID = 0; msg.msgID = htobe32 (msgID);
msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ()); msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ());
return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg)); return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg));
} }
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, 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 (); I2NPMessage * m = NewI2NPMessage ();
uint8_t * buf = m->GetPayload (); uint8_t * buf = m->GetPayload ();
@ -113,15 +113,59 @@ namespace i2p
} }
else else
{ {
// nothing to exclude if (excludedPeers)
*(uint16_t *)buf = htobe16 (0); {
buf += 2; 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 ()); m->len += (buf - m->GetPayload ());
FillI2NPMessageHeader (m, eI2NPDatabaseLookup); FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
return m; 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 * CreateDatabaseStoreMsg ()
{ {
I2NPMessage * m = NewI2NPMessage (); I2NPMessage * m = NewI2NPMessage ();
@ -370,7 +414,7 @@ namespace i2p
return be16toh (header->size) + sizeof (I2NPHeader); 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; I2NPHeader * header = (I2NPHeader *)msg;
uint32_t msgID = be32toh (header->msgID); uint32_t msgID = be32toh (header->msgID);
@ -382,7 +426,7 @@ namespace i2p
{ {
case eI2NPGarlic: case eI2NPGarlic:
LogPrint ("Garlic"); LogPrint ("Garlic");
i2p::garlic::routing.HandleGarlicMessage (buf, size); i2p::garlic::routing.HandleGarlicMessage (buf, size, isFromTunnel);
break; break;
break; break;
case eI2NPDeliveryStatus: case eI2NPDeliveryStatus:
@ -395,13 +439,17 @@ namespace i2p
case eI2NPVariableTunnelBuildReply: case eI2NPVariableTunnelBuildReply:
LogPrint ("VariableTunnelBuildReply"); LogPrint ("VariableTunnelBuildReply");
HandleVariableTunnelBuildReplyMsg (msgID, buf, size); HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
break; break;
case eI2NPDatabaseLookup:
LogPrint ("DatabaseLookup");
HandleDatabaseLookupMsg (buf, size);
break;
default: default:
LogPrint ("Unexpected message ", (int)header->typeID); LogPrint ("Unexpected message ", (int)header->typeID);
} }
} }
void HandleI2NPMessage (I2NPMessage * msg) void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel)
{ {
if (msg) if (msg)
{ {
@ -424,7 +472,7 @@ namespace i2p
i2p::data::netdb.PostI2NPMsg (msg); i2p::data::netdb.PostI2NPMsg (msg);
break; break;
default: default:
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); HandleI2NPMessage (msg->GetBuffer (), msg->GetLength (), isFromTunnel);
DeleteI2NPMessage (msg); DeleteI2NPMessage (msg);
} }
} }

@ -2,7 +2,7 @@
#define I2NP_PROTOCOL_H__ #define I2NP_PROTOCOL_H__
#include <inttypes.h> #include <inttypes.h>
#include <vector> #include <set>
#include <string.h> #include <string.h>
#include "RouterInfo.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 (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0);
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len); 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, 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 (); I2NPMessage * CreateDatabaseStoreMsg ();
I2NPBuildRequestRecordClearText CreateBuildRequestRecord ( I2NPBuildRequestRecordClearText CreateBuildRequestRecord (
@ -132,8 +135,8 @@ namespace i2p
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg); I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg);
size_t GetI2NPMessageLength (uint8_t * msg); size_t GetI2NPMessageLength (uint8_t * msg);
void HandleI2NPMessage (uint8_t * msg, size_t len); void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel);
void HandleI2NPMessage (I2NPMessage * msg); void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel);
} }
#endif #endif

@ -1,3 +1,5 @@
#include <time.h>
#include <stdio.h>
#include <cryptopp/sha.h> #include <cryptopp/sha.h>
#include <cryptopp/osrng.h> #include <cryptopp/osrng.h>
#include <cryptopp/dh.h> #include <cryptopp/dh.h>
@ -9,6 +11,14 @@ namespace i2p
{ {
namespace data 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 CalculateIdentHash (const Identity& identity)
{ {
IdentHash hash; IdentHash hash;
@ -35,5 +45,30 @@ namespace data
return keys; 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;
}
} }
} }

@ -23,6 +23,8 @@ namespace data
uint8_t publicKey[256]; uint8_t publicKey[256];
uint8_t signingKey[128]; uint8_t signingKey[128];
uint8_t certificate[3]; uint8_t certificate[3];
Identity& operator=(const Keys& keys);
}; };
#pragma pack() #pragma pack()
@ -59,7 +61,26 @@ namespace data
IdentHash CalculateIdentHash (const Identity& identity); IdentHash CalculateIdentHash (const Identity& identity);
Keys CreateRandomKeys (); 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 class RoutingDestination
{ {
public: public:

@ -1,3 +1,5 @@
#include <cryptopp/dsa.h>
#include "CryptoConst.h"
#include "Log.h" #include "Log.h"
#include "LeaseSet.h" #include "LeaseSet.h"
@ -24,10 +26,22 @@ namespace data
memcpy (m_EncryptionKey, header->encryptionKey, 256); memcpy (m_EncryptionKey, header->encryptionKey, 256);
LogPrint ("LeaseSet num=", (int)header->num); LogPrint ("LeaseSet num=", (int)header->num);
const uint8_t * leases = buf + sizeof (H);
for (int i = 0; i < header->num; i++) 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 <inttypes.h>
#include <string.h> #include <string.h>
#include <list> #include <vector>
#include "Identity.h" #include "Identity.h"
namespace i2p namespace i2p
@ -31,12 +31,13 @@ namespace data
// implements RoutingDestination // implements RoutingDestination
const Identity& GetIdentity () const { return m_Identity; }; const Identity& GetIdentity () const { return m_Identity; };
const IdentHash& GetIdentHash () const { return m_IdentHash; }; const IdentHash& GetIdentHash () const { return m_IdentHash; };
const std::vector<Lease>& GetLeases () const { return m_Leases; };
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; }; const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; };
bool IsDestination () const { return true; }; bool IsDestination () const { return true; };
private: private:
std::list<Lease> m_Leases; std::vector<Lease> m_Leases;
Identity m_Identity; Identity m_Identity;
IdentHash m_IdentHash; IdentHash m_IdentHash;
uint8_t m_EncryptionKey[256]; uint8_t m_EncryptionKey[256];

@ -20,12 +20,10 @@ namespace i2p
{ {
namespace ntcp 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_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) void NTCPSession::CreateAESKey (uint8_t * pubKey, uint8_t * aesKey)
@ -186,7 +184,8 @@ namespace ntcp
{ {
if (ecode) 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 (); Terminate ();
} }
else else
@ -385,7 +384,8 @@ namespace ntcp
if (m_ReceiveBufferOffset > 0) if (m_ReceiveBufferOffset > 0)
memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset); memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset);
} }
ScheduleTermination (); // reset termination timer
Receive (); Receive ();
} }
} }
@ -424,7 +424,7 @@ namespace ntcp
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
{ {
// we have a complete I2NP message // we have a complete I2NP message
i2p::HandleI2NPMessage (m_NextMessage); i2p::HandleI2NPMessage (m_NextMessage, false);
m_NextMessage = nullptr; m_NextMessage = nullptr;
} }
} }
@ -461,14 +461,10 @@ namespace ntcp
m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding); m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding);
int l = len + padding + 6; int l = len + padding + 6;
{ m_Encryption.ProcessData(sendBuffer, sendBuffer, l);
std::lock_guard<std::mutex> lock (m_EncryptionMutex);
m_Encryption.ProcessData(sendBuffer, sendBuffer, l);
}
boost::asio::async_write (m_Socket, boost::asio::buffer (sendBuffer, l), boost::asio::transfer_all (), 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)); 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) void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, i2p::I2NPMessage * msg)
@ -483,6 +479,7 @@ namespace ntcp
else else
{ {
LogPrint ("Msg sent: ", bytes_transferred); 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, 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) m_Endpoint (boost::asio::ip::address::from_string (address), port)
{ {
Connect (); Connect ();

@ -2,7 +2,6 @@
#define NTCP_SESSION_H__ #define NTCP_SESSION_H__
#include <inttypes.h> #include <inttypes.h>
#include <mutex>
#include <list> #include <list>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <cryptopp/modes.h> #include <cryptopp/modes.h>
@ -66,7 +65,7 @@ namespace ntcp
{ {
public: 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 () {}; virtual ~NTCPSession () {};
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
@ -126,7 +125,7 @@ namespace ntcp
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption; CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption;
CryptoPP::Adler32 m_Adler; CryptoPP::Adler32 m_Adler;
i2p::data::RouterInfo m_RemoteRouterInfo; i2p::data::RouterInfo& m_RemoteRouterInfo;
NTCPPhase1 m_Phase1; NTCPPhase1 m_Phase1;
NTCPPhase2 m_Phase2; NTCPPhase2 m_Phase2;
@ -139,8 +138,6 @@ namespace ntcp
i2p::I2NPMessage * m_NextMessage; i2p::I2NPMessage * m_NextMessage;
std::list<i2p::I2NPMessage *> m_DelayedMessages; std::list<i2p::I2NPMessage *> m_DelayedMessages;
size_t m_NextMessageOffset; size_t m_NextMessageOffset;
std::mutex m_EncryptionMutex;
}; };
class NTCPClient: public NTCPSession class NTCPClient: public NTCPSession
@ -163,11 +160,16 @@ namespace ntcp
{ {
public: public:
NTCPServerConnection (boost::asio::io_service& service): NTCPSession (service) {}; NTCPServerConnection (boost::asio::io_service& service):
NTCPSession (service, m_DummyRemoteRouterInfo) {};
protected: protected:
virtual void Connected (); virtual void Connected ();
private:
i2p::data::RouterInfo m_DummyRemoteRouterInfo;
}; };
} }
} }

@ -8,16 +8,30 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Tunnel.h" #include "Tunnel.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "Garlic.h"
#include "NetDb.h" #include "NetDb.h"
namespace i2p namespace i2p
{ {
namespace data 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;
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; delete l.second;
for (auto r:m_RouterInfos) for (auto r:m_RouterInfos)
delete r.second; delete r.second;
for (auto r:m_RequestedDestinations)
delete r.second;
} }
void NetDb::Start () void NetDb::Start ()
@ -70,7 +86,7 @@ namespace data
else // WTF? else // WTF?
{ {
LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID); LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID);
i2p::HandleI2NPMessage (msg); i2p::HandleI2NPMessage (msg, false);
} }
msg = m_Queue.Get (); msg = m_Queue.Get ();
} }
@ -96,6 +112,7 @@ namespace data
void NetDb::AddRouterInfo (uint8_t * buf, int len) void NetDb::AddRouterInfo (uint8_t * buf, int len)
{ {
RouterInfo * r = new RouterInfo (buf, len); RouterInfo * r = new RouterInfo (buf, len);
DeleteRequestedDestination (r->GetIdentHash ());
auto it = m_RouterInfos.find(r->GetIdentHash ()); auto it = m_RouterInfos.find(r->GetIdentHash ());
if (it != m_RouterInfos.end ()) if (it != m_RouterInfos.end ())
{ {
@ -117,6 +134,7 @@ namespace data
void NetDb::AddLeaseSet (uint8_t * buf, int len) void NetDb::AddLeaseSet (uint8_t * buf, int len)
{ {
LeaseSet * l = new LeaseSet (buf, len); LeaseSet * l = new LeaseSet (buf, len);
DeleteRequestedDestination (l->GetIdentHash ());
m_LeaseSets[l->GetIdentHash ()] = l; m_LeaseSets[l->GetIdentHash ()] = l;
} }
@ -128,6 +146,15 @@ namespace data
else else
return nullptr; 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) void NetDb::Load (const char * directory)
{ {
@ -156,39 +183,67 @@ namespace data
void NetDb::SaveUpdated (const char * directory) 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) for (auto it: m_RouterInfos)
{
if (it.second->IsUpdated ()) if (it.second->IsUpdated ())
{ {
std::ofstream r (std::string (directory) + "/r" + std::ofstream r (GetFilePath(directory, it.second));
it.second->GetIdentHashBase64 ()[0] + "/routerInfo-" +
it.second->GetIdentHashBase64 () + ".dat");
r.write ((char *)it.second->GetBuffer (), it.second->GetBufferLen ()); r.write ((char *)it.second->GetBuffer (), it.second->GetBufferLen ());
it.second->SetUpdated (false); it.second->SetUpdated (false);
count++; 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) if (count > 0)
LogPrint (count," new/updated routers saved"); 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]; uint8_t destination[32];
Base32ToByteStream (b32, strlen(b32), 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 (); i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
if (outbound) if (outbound)
{ {
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel (); i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (inbound) if (inbound)
{ {
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (destination, inbound->GetNextIdentHash (), RequestedDestination * dest = CreateRequestedDestination (destination, isLeaseSet);
inbound->GetNextTunnelID ()); dest->SetLastOutboundTunnel (outbound);
outbound->SendTunnelDataMsg (router, 0, msg); auto msg = dest->CreateRequestMessage (floodfill, inbound);
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
} }
else else
LogPrint ("No inbound tunnels found"); LogPrint ("No inbound tunnels found");
@ -196,7 +251,7 @@ namespace data
else else
LogPrint ("No outbound tunnels found"); LogPrint ("No outbound tunnels found");
} }
void NetDb::HandleDatabaseStoreMsg (uint8_t * buf, size_t len) void NetDb::HandleDatabaseStoreMsg (uint8_t * buf, size_t len)
{ {
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)buf; I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)buf;
@ -236,83 +291,133 @@ namespace data
key[l] = 0; key[l] = 0;
int num = buf[32]; // num int num = buf[32]; // num
LogPrint ("DatabaseSearchReply for ", key, " num=", num); LogPrint ("DatabaseSearchReply for ", key, " num=", num);
if (num > 0) auto it = m_RequestedDestinations.find (IdentHash (buf));
{ if (it != m_RequestedDestinations.end ())
bool isExploratory = !memcmp (m_Exploratory, buf, 32) && m_LastFloodfill; {
i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel (); RequestedDestination * dest = it->second;
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel (); if (num > 0)
{
i2p::tunnel::OutboundTunnel * outbound = dest->GetLastOutboundTunnel ();
const i2p::tunnel::InboundTunnel * inbound = dest->GetLastReplyTunnel ();
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
{ {
uint8_t * router = buf + 33 + i*32; uint8_t * router = buf + 33 + i*32;
char peerHash[48]; char peerHash[48];
int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48); int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48);
peerHash[l1] = 0; peerHash[l1] = 0;
LogPrint (i,": ", peerHash); LogPrint (i,": ", peerHash);
if (isExploratory) if (dest->IsExploratory ())
{
if (m_RouterInfos.find (IdentHash(router)) == m_RouterInfos.end ())
{ {
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) if (outbound && inbound)
{ {
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (router, inbound->GetNextIdentHash (), auto r = FindRouter (router);
inbound->GetNextTunnelID ()); // do we have that floodfill router in our database?
outbound->GetTunnelGateway ().PutTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0, msg); if (r)
} {
} if (!dest->IsExcluded (r->GetIdentHash ()) && dest->GetNumExcludedPeers () < 10) // TODO: fix TunnelGateway first
else {
LogPrint ("Bayan"); // request destination
} auto msg = dest->CreateRequestMessage (r, dest->GetLastReplyTunnel ());
else outbound->GetTunnelGateway ().PutTunnelDataMsg (r->GetIdentHash (), 0, msg);
{ }
// reply to our destination. Try other floodfills }
if (outbound && inbound) else
{ {
// do we have that floodfill router in our database? // request router
if (!FindRouter (router)) LogPrint ("Found new floodfill. Request it");
{ RequestedDestination * d2 = CreateRequestedDestination (router, false, false);
// request router d2->SetLastOutboundTunnel (outbound);
LogPrint ("Found new floodfill. Request it"); I2NPMessage * msg = d2->CreateRequestMessage (dest->GetLastRouter (), inbound);
msg = i2p::CreateDatabaseLookupMsg (router, inbound->GetNextIdentHash (), outbound->GetTunnelGateway ().PutTunnelDataMsg (
inbound->GetNextTunnelID ()); dest->GetLastRouter ()->GetIdentHash (), 0, msg);
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);
} }
} }
} }
}
if (outbound) if (outbound)
outbound->GetTunnelGateway ().SendBuffer (); 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); i2p::DeleteI2NPMessage (msg);
} }
void NetDb::Explore () void NetDb::Explore ()
{ {
i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel (); auto outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel (); auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (outbound && inbound) if (outbound && inbound)
{ {
m_LastFloodfill = GetRandomNTCPRouter (true); auto floodfill = GetRandomNTCPRouter (true);
if (m_LastFloodfill) if (floodfill)
{ {
LogPrint ("Exploring new routers ..."); LogPrint ("Exploring new routers ...");
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (m_Exploratory, 32); uint8_t randomHash[32];
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Exploratory, inbound->GetNextIdentHash (), rnd.GenerateBlock (randomHash, 32);
inbound->GetNextTunnelID (), true); RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true);
outbound->SendTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0, msg); 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 const RouterInfo * NetDb::GetRandomNTCPRouter (bool floodfillOnly) const
{ {
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
@ -345,5 +450,26 @@ namespace data
{ {
if (msg) m_Queue.Put (msg); 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;
}
} }
} }

@ -2,6 +2,7 @@
#define NETDB_H__ #define NETDB_H__
#include <inttypes.h> #include <inttypes.h>
#include <set>
#include <map> #include <map>
#include <string> #include <string>
#include <thread> #include <thread>
@ -9,11 +10,41 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "LeaseSet.h" #include "LeaseSet.h"
#include "Tunnel.h"
namespace i2p namespace i2p
{ {
namespace data 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 class NetDb
{ {
public: public:
@ -27,9 +58,12 @@ namespace data
void AddRouterInfo (uint8_t * buf, int len); void AddRouterInfo (uint8_t * buf, int len);
void AddLeaseSet (uint8_t * buf, int len); void AddLeaseSet (uint8_t * buf, int len);
RouterInfo * FindRouter (const IdentHash& ident) const; RouterInfo * FindRouter (const IdentHash& ident) const;
LeaseSet * FindLeaseSet (const IdentHash& destination) const;
void RequestDestination (const char * b32, const uint8_t * router); // in base32
void RequestDestination (const uint8_t * destination, const uint8_t * router); 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 HandleDatabaseStoreMsg (uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg); void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
@ -44,16 +78,20 @@ namespace data
void SaveUpdated (const char * directory); void SaveUpdated (const char * directory);
void Run (); // exploratory thread void Run (); // exploratory thread
void Explore (); 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: private:
std::map<IdentHash, LeaseSet *> m_LeaseSets; std::map<IdentHash, LeaseSet *> m_LeaseSets;
std::map<IdentHash, RouterInfo *> m_RouterInfos; std::map<IdentHash, RouterInfo *> m_RouterInfos;
std::map<IdentHash, RequestedDestination *> m_RequestedDestinations;
bool m_IsRunning; bool m_IsRunning;
std::thread * m_Thread; std::thread * m_Thread;
uint8_t m_Exploratory[32];
const RouterInfo * m_LastFloodfill;
i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
}; };

@ -26,9 +26,7 @@ namespace i2p
CryptoPP::Integer (m_Keys.signingPrivateKey, 20)); CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
i2p::data::Identity ident; i2p::data::Identity ident;
// copy public and signing keys together ident = m_Keys;
memcpy (ident.publicKey, m_Keys.publicKey, sizeof (ident.publicKey) + sizeof (ident.signingKey));
memset (ident.certificate, 0, sizeof (ident.certificate));
m_RouterInfo.SetRouterIdentity (ident); m_RouterInfo.SetRouterIdentity (ident);
m_RouterInfo.AddNTCPAddress ("127.0.0.1", 17007); // TODO: m_RouterInfo.AddNTCPAddress ("127.0.0.1", 17007); // TODO:

@ -34,6 +34,8 @@ namespace data
{ {
m_RouterIdentity = identity; m_RouterIdentity = identity;
m_IdentHash = CalculateIdentHash (m_RouterIdentity); m_IdentHash = CalculateIdentHash (m_RouterIdentity);
UpdateIdentHashBase64 ();
UpdateRoutingKey ();
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
} }
@ -124,12 +126,23 @@ namespace data
} }
CryptoPP::SHA256().CalculateDigest(m_IdentHash, (uint8_t *)&m_RouterIdentity, sizeof (m_RouterIdentity)); 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); size_t l = i2p::data::ByteStreamToBase64 (m_IdentHash, 32, m_IdentHashBase64, 48);
m_IdentHashBase64[l] = 0; m_IdentHashBase64[l] = 0;
memcpy (m_IdentHashAbbreviation, m_IdentHashBase64, 4); memcpy (m_IdentHashAbbreviation, m_IdentHashBase64, 4);
m_IdentHashAbbreviation[4] = 0; m_IdentHashAbbreviation[4] = 0;
} }
void RouterInfo::UpdateRoutingKey ()
{
m_RoutingKey = CreateRoutingKey (m_IdentHash);
}
void RouterInfo::WriteToStream (std::ostream& s) void RouterInfo::WriteToStream (std::ostream& s)
{ {
s.write ((char *)&m_RouterIdentity, sizeof (m_RouterIdentity)); s.write ((char *)&m_RouterIdentity, sizeof (m_RouterIdentity));

@ -36,6 +36,7 @@ namespace data
RouterInfo (const char * filename); RouterInfo (const char * filename);
RouterInfo () = default; RouterInfo () = default;
RouterInfo (const RouterInfo& ) = default; RouterInfo (const RouterInfo& ) = default;
RouterInfo& operator=(const RouterInfo& ) = default;
RouterInfo (const uint8_t * buf, int len); RouterInfo (const uint8_t * buf, int len);
const Identity& GetRouterIdentity () const { return m_RouterIdentity; }; const Identity& GetRouterIdentity () const { return m_RouterIdentity; };
@ -45,6 +46,7 @@ namespace data
uint64_t GetTimestamp () const { return m_Timestamp; }; uint64_t GetTimestamp () const { return m_Timestamp; };
const std::vector<Address>& GetAddresses () const { return m_Addresses; }; const std::vector<Address>& GetAddresses () const { return m_Addresses; };
Address * GetNTCPAddress (); Address * GetNTCPAddress ();
const RoutingKey& GetRoutingKey () const { return m_RoutingKey; };
void AddNTCPAddress (const char * host, int port); void AddNTCPAddress (const char * host, int port);
void SetProperty (const char * key, const char * value); void SetProperty (const char * key, const char * value);
@ -55,6 +57,7 @@ namespace data
bool IsUnreachable () const { return m_IsUnreachable; }; bool IsUnreachable () const { return m_IsUnreachable; };
void CreateBuffer (); void CreateBuffer ();
void UpdateRoutingKey ();
const char * GetBuffer () const { return m_Buffer; }; const char * GetBuffer () const { return m_Buffer; };
int GetBufferLen () const { return m_BufferLen; }; int GetBufferLen () const { return m_BufferLen; };
@ -74,11 +77,13 @@ namespace data
void WriteToStream (std::ostream& s); void WriteToStream (std::ostream& s);
size_t ReadString (char * str, std::istream& s); size_t ReadString (char * str, std::istream& s);
void WriteString (const std::string& str, std::ostream& s); void WriteString (const std::string& str, std::ostream& s);
void UpdateIdentHashBase64 ();
private: private:
Identity m_RouterIdentity; Identity m_RouterIdentity;
IdentHash m_IdentHash; IdentHash m_IdentHash;
RoutingKey m_RoutingKey;
char m_IdentHashBase64[48], m_IdentHashAbbreviation[5]; char m_IdentHashBase64[48], m_IdentHashAbbreviation[5];
char m_Buffer[2048]; char m_Buffer[2048];
int m_BufferLen; int m_BufferLen;

@ -4,14 +4,18 @@
#include "Log.h" #include "Log.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "Tunnel.h"
#include "Timestamp.h"
#include "CryptoConst.h"
#include "Garlic.h"
#include "Streaming.h" #include "Streaming.h"
namespace i2p namespace i2p
{ {
namespace stream namespace stream
{ {
Stream::Stream (const i2p::data::IdentHash& destination): Stream::Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote):
m_SendStreamID (0) m_SendStreamID (0), m_SequenceNumber (0), m_LocalDestination (local), m_RemoteLeaseSet (remote)
{ {
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
} }
@ -56,9 +60,68 @@ namespace stream
std::string str((const char *)buf, end-buf); std::string str((const char *)buf, end-buf);
LogPrint ("Payload: ", str); 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) void StreamingDestination::HandleNextPacket (const uint8_t * buf, size_t len)
{ {
uint32_t sendStreamID = *(uint32_t *)(buf); uint32_t sendStreamID = *(uint32_t *)(buf);
@ -69,25 +132,93 @@ namespace stream
LogPrint ("Unknown stream ", sendStreamID); 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); Stream * s = new Stream (this, remote);
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);
m_Streams[s->GetRecvStreamID ()] = s; m_Streams[s->GetRecvStreamID ()] = s;
return 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) void HandleDataMessage (i2p::data::IdentHash * destination, const uint8_t * buf, size_t len)
{ {
uint32_t length = be32toh (*(uint32_t *)buf); uint32_t length = be32toh (*(uint32_t *)buf);
@ -104,7 +235,8 @@ namespace stream
decompressor.Get (uncompressed, uncompressedSize); decompressor.Get (uncompressed, uncompressedSize);
// then forward to streaming engine // then forward to streaming engine
// TODO: we have onle one destination, might be more // TODO: we have onle one destination, might be more
m_SharedLocalDestination.HandleNextPacket (uncompressed, uncompressedSize); if (sharedLocalDestination)
sharedLocalDestination->HandleNextPacket (uncompressed, uncompressedSize);
} }
else else
LogPrint ("Data: protocol ", buf[9], " is not supported"); LogPrint ("Data: protocol ", buf[9], " is not supported");
@ -118,7 +250,7 @@ namespace stream
compressor.MessageEnd(); compressor.MessageEnd();
int size = compressor.MaxRetrievable (); int size = compressor.MaxRetrievable ();
uint8_t * buf = msg->GetPayload (); uint8_t * buf = msg->GetPayload ();
*(uint16_t *)buf = htobe32 (size); // length *(uint32_t *)buf = htobe32 (size); // length
buf += 4; buf += 4;
compressor.Get (buf, size); compressor.Get (buf, size);
buf[9] = 6; // streaming protocol buf[9] = 6; // streaming protocol

@ -3,6 +3,8 @@
#include <inttypes.h> #include <inttypes.h>
#include <map> #include <map>
#include <cryptopp/dsa.h>
#include "Identity.h"
#include "LeaseSet.h" #include "LeaseSet.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
@ -22,32 +24,63 @@ namespace stream
const uint16_t PACKET_FLAG_ECHO = 0x0200; const uint16_t PACKET_FLAG_ECHO = 0x0200;
const uint16_t PACKET_FLAG_NO_ACK = 0x0400; const uint16_t PACKET_FLAG_NO_ACK = 0x0400;
const size_t STREAMING_MTU = 1730;
class StreamingDestination;
class Stream class Stream
{ {
public: public:
Stream (const i2p::data::IdentHash& destination); Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote);
uint32_t GetSendStreamID () const { return m_SendStreamID; }; uint32_t GetSendStreamID () const { return m_SendStreamID; };
uint32_t GetRecvStreamID () const { return m_RecvStreamID; }; 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); void HandleNextPacket (const uint8_t * buf, size_t len);
size_t Send (uint8_t * buf, size_t len, int timeout); // timeout in seconds
private: 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 class StreamingDestination
{ {
public: 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); void HandleNextPacket (const uint8_t * buf, size_t len);
private:
I2NPMessage * CreateLeaseSet () const;
private: private:
std::map<uint32_t, Stream *> m_Streams; 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 // assuming data is I2CP message
void HandleDataMessage (i2p::data::IdentHash * destination, const uint8_t * buf, size_t len); void HandleDataMessage (i2p::data::IdentHash * destination, const uint8_t * buf, size_t len);

@ -14,7 +14,8 @@ namespace tunnel
TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, TransitTunnel::TransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey): 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_LayerKey, layerKey, 32);
memcpy (m_IVKey, ivKey, 32); memcpy (m_IVKey, ivKey, 32);
@ -43,6 +44,7 @@ namespace tunnel
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData); FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
i2p::transports.SendMessage (m_NextIdent, tunnelMsg); i2p::transports.SendMessage (m_NextIdent, tunnelMsg);
m_NumTransmittedBytes += tunnelMsg->GetLength ();
} }
void TransitTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg) 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 HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
virtual void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg); 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; }; uint32_t GetTunnelID () const { return m_TunnelID; };
@ -37,6 +38,7 @@ namespace tunnel
i2p::data::IdentHash m_NextIdent; i2p::data::IdentHash m_NextIdent;
uint8_t m_LayerKey[32]; uint8_t m_LayerKey[32];
uint8_t m_IVKey[32]; uint8_t m_IVKey[32];
size_t m_NumTransmittedBytes;
CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_ECBEncryption; CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_ECBEncryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_CBCEncryption; CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_CBCEncryption;
@ -53,6 +55,7 @@ namespace tunnel
layerKey, ivKey), m_Gateway(this) {}; layerKey, ivKey), m_Gateway(this) {};
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg); void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); };
private: private:
@ -69,6 +72,7 @@ namespace tunnel
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey) {}; TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey) {};
void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg); void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }
private: private:

@ -23,6 +23,7 @@ namespace i2p
void Transports::Start () void Transports::Start ()
{ {
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this)); m_Thread = new std::thread (std::bind (&Transports::Run, this));
// create acceptors // create acceptors
auto addresses = context.GetRouterInfo ().GetAddresses (); auto addresses = context.GetRouterInfo ().GetAddresses ();
@ -48,6 +49,7 @@ namespace i2p
m_NTCPSessions.clear (); m_NTCPSessions.clear ();
delete m_NTCPAcceptor; delete m_NTCPAcceptor;
m_IsRunning = false;
m_Service.stop (); m_Service.stop ();
if (m_Thread) if (m_Thread)
{ {
@ -59,13 +61,16 @@ namespace i2p
void Transports::Run () void Transports::Run ()
{ {
try while (m_IsRunning)
{
m_Service.run ();
}
catch (std::exception& ex)
{ {
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 ()) if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
// we send it to ourself // we send it to ourself
i2p::HandleI2NPMessage (msg); i2p::HandleI2NPMessage (msg, false);
else else
{ m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg));
auto session = FindNTCPSession (ident); }
if (!session)
{ void Transports::PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
RouterInfo * r = netdb.FindRouter (ident); {
if (r) auto session = FindNTCPSession (ident);
if (!session)
{
RouterInfo * r = netdb.FindRouter (ident);
if (r)
{
auto address = r->GetNTCPAddress ();
if (address)
{ {
auto address = r->GetNTCPAddress (); session = new i2p::ntcp::NTCPClient (m_Service, address->host.c_str (), address->port, *r);
if (address) AddNTCPSession (session);
{ }
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");
} }
if (session)
session->SendI2NPMessage (msg);
else
LogPrint ("Session not found");
} }
} }

@ -36,9 +36,11 @@ namespace i2p
void Run (); void Run ();
void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error); void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error);
void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
private: private:
bool m_IsRunning;
std::thread * m_Thread; std::thread * m_Thread;
boost::asio::io_service m_Service; boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work; boost::asio::io_service::work m_Work;

@ -14,8 +14,7 @@ namespace i2p
namespace tunnel namespace tunnel
{ {
Tunnel::Tunnel (TunnelConfig * config): m_Config (config), Tunnel::Tunnel (TunnelConfig * config): m_Config (config), m_IsEstablished (false)
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_IsEstablished (false)
{ {
} }
@ -67,14 +66,9 @@ namespace tunnel
FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild); FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild);
if (outboundTunnel) if (outboundTunnel)
{ outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
DeleteI2NPMessage (msg);
}
else else
{
i2p::transports.SendMessage (GetNextIdentHash (), msg); i2p::transports.SendMessage (GetNextIdentHash (), msg);
}
} }
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
@ -220,7 +214,17 @@ namespace tunnel
OutboundTunnel * Tunnels::GetNextOutboundTunnel () 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; size_t minSent = 0;
for (auto it : m_OutboundTunnels) for (auto it : m_OutboundTunnels)
if (!tunnel || it->GetNumSentBytes () < minSent) if (!tunnel || it->GetNumSentBytes () < minSent)
@ -228,7 +232,7 @@ namespace tunnel
tunnel = it; tunnel = it;
minSent = it->GetNumSentBytes (); minSent = it->GetNumSentBytes ();
} }
return tunnel; return tunnel;*/
} }
void Tunnels::AddTransitTunnel (TransitTunnel * tunnel) void Tunnels::AddTransitTunnel (TransitTunnel * tunnel)
@ -311,6 +315,7 @@ namespace tunnel
ManageInboundTunnels (); ManageInboundTunnels ();
ManageOutboundTunnels (); ManageOutboundTunnels ();
ManageTransitTunnels ();
/* if (!m_IsTunnelCreated) /* if (!m_IsTunnelCreated)
{ {
@ -349,18 +354,24 @@ namespace tunnel
{ {
LogPrint ("Creating one hop outbound tunnel..."); LogPrint ("Creating one hop outbound tunnel...");
CreateTunnel<OutboundTunnel> ( CreateTunnel<OutboundTunnel> (
new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (), new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
i2p::data::netdb.GetRandomNTCPRouter ()
},
inboundTunnel->GetTunnelConfig ())); inboundTunnel->GetTunnelConfig ()));
} }
else else
{ {
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel (); //OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
LogPrint ("Creating two hops outbound tunnel..."); LogPrint ("Creating two hops outbound tunnel...");
CreateTunnel<OutboundTunnel> ( CreateTunnel<OutboundTunnel> (
new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (), new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
i2p::data::netdb.GetRandomNTCPRouter (), {
inboundTunnel->GetTunnelConfig ()), i2p::data::netdb.GetRandomNTCPRouter (),
outboundTunnel); i2p::data::netdb.GetRandomNTCPRouter ()
},
inboundTunnel->GetTunnelConfig ())/*,
outboundTunnel*/);
} }
} }
} }
@ -392,19 +403,42 @@ namespace tunnel
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 3) if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 3)
{ {
LogPrint ("Creating one hop inbound tunnel..."); 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 else
{ {
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel (); OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
LogPrint ("Creating two hops inbound tunnel..."); LogPrint ("Creating two hops inbound tunnel...");
auto router = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router;
CreateTunnel<InboundTunnel> ( CreateTunnel<InboundTunnel> (
new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (), new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router), {
outboundTunnel); 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) void Tunnels::PostTunnelData (I2NPMessage * msg)
{ {
@ -437,7 +471,10 @@ namespace tunnel
void Tunnels::CreateZeroHopsInboundTunnel () void Tunnels::CreateZeroHopsInboundTunnel ()
{ {
CreateTunnel<InboundTunnel> ( CreateTunnel<InboundTunnel> (
new TunnelConfig (&i2p::context.GetRouterInfo ())); new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
&i2p::context.GetRouterInfo ()
}));
} }
OutboundTunnel * Tunnels::CreateOneHopOutboundTestTunnel (InboundTunnel * replyTunnel) OutboundTunnel * Tunnels::CreateOneHopOutboundTestTunnel (InboundTunnel * replyTunnel)
@ -451,7 +488,9 @@ namespace tunnel
if (peer) if (peer)
{ {
const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo (); 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 else
LogPrint ("No established peers"); LogPrint ("No established peers");
@ -470,7 +509,11 @@ namespace tunnel
{ {
const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo (); const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo ();
return CreateTunnel<InboundTunnel> ( return CreateTunnel<InboundTunnel> (
new TunnelConfig (&router, &i2p::context.GetRouterInfo ()), new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
&router,
&i2p::context.GetRouterInfo ()
}),
outboundTunnel); outboundTunnel);
} }
else else

@ -33,9 +33,7 @@ namespace tunnel
void Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel = 0); 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; } TunnelConfig * GetTunnelConfig () const { return m_Config; }
uint32_t GetCreationTime () const { return m_CreationTime; };
bool IsEstablished () const { return m_IsEstablished; }; bool IsEstablished () const { return m_IsEstablished; };
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len); bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
@ -54,7 +52,6 @@ namespace tunnel
private: private:
TunnelConfig * m_Config; TunnelConfig * m_Config;
uint32_t m_CreationTime; // seconds since epoch
bool m_IsEstablished; bool m_IsEstablished;
CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption m_ECBDecryption; CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption m_ECBDecryption;
@ -70,9 +67,11 @@ namespace tunnel
void SendTunnelDataMsg (i2p::I2NPMessage * msg); //local void SendTunnelDataMsg (i2p::I2NPMessage * msg); //local
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg); void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
uint32_t GetTunnelID () const { return GetNextTunnelID (); };
TunnelGateway& GetTunnelGateway () { return m_Gateway; }; TunnelGateway& GetTunnelGateway () { return m_Gateway; };
size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); }; size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
// implements TunnelBase
uint32_t GetTunnelID () const { return GetNextTunnelID (); };
private: private:
@ -85,10 +84,10 @@ namespace tunnel
InboundTunnel (TunnelConfig * config): Tunnel (config) {}; InboundTunnel (TunnelConfig * config): Tunnel (config) {};
void HandleTunnelDataMsg (I2NPMessage * msg); void HandleTunnelDataMsg (I2NPMessage * msg);
size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
// implements TunnelBase
uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; }; uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; };
size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
private: private:
TunnelEndpoint m_Endpoint; TunnelEndpoint m_Endpoint;
@ -128,6 +127,7 @@ namespace tunnel
void ManageTunnels (); void ManageTunnels ();
void ManageOutboundTunnels (); void ManageOutboundTunnels ();
void ManageInboundTunnels (); void ManageInboundTunnels ();
void ManageTransitTunnels ();
void CreateZeroHopsInboundTunnel (); void CreateZeroHopsInboundTunnel ();

@ -2,6 +2,7 @@
#define TUNNEL_BASE_H__ #define TUNNEL_BASE_H__
#include <inttypes.h> #include <inttypes.h>
#include "Timestamp.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
namespace i2p namespace i2p
@ -30,11 +31,20 @@ namespace tunnel
{ {
public: public:
TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
virtual ~TunnelBase () {}; virtual ~TunnelBase () {};
virtual void EncryptTunnelMsg (I2NPMessage * tunnelMsg) = 0; virtual void EncryptTunnelMsg (I2NPMessage * tunnelMsg) = 0;
virtual uint32_t GetNextTunnelID () const = 0; virtual uint32_t GetNextTunnelID () const = 0;
virtual const i2p::data::IdentHash& GetNextIdentHash () 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 <inttypes.h>
#include <sstream> #include <sstream>
#include <vector>
#include "RouterInfo.h" #include "RouterInfo.h"
#include "RouterContext.h" #include "RouterContext.h"
@ -83,34 +84,30 @@ namespace tunnel
{ {
public: 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 TunnelConfig (std::vector<const i2p::data::RouterInfo *> peers,
TunnelConfig * replyTunnelConfig = 0) // replyTunnelConfig=0 means inbound
{ {
m_FirstHop = new TunnelHopConfig (peer1); TunnelHopConfig * prev = nullptr;
m_LastHop = new TunnelHopConfig (peer2); for (auto it: peers)
m_FirstHop->SetNext (m_LastHop); {
auto hop = new TunnelHopConfig (it);
if (prev)
prev->SetNext (hop);
else
m_FirstHop = hop;
prev = hop;
}
m_LastHop = prev;
if (replyTunnelConfig) if (replyTunnelConfig) // outbound
{ {
m_FirstHop->isGateway = false; m_FirstHop->isGateway = false;
m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ()); m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ());
} }
else else // inbound
m_LastHop->SetNextRouter (&i2p::context.GetRouterInfo ()); m_LastHop->SetNextRouter (&i2p::context.GetRouterInfo ());
} }
~TunnelConfig () ~TunnelConfig ()
{ {

@ -147,7 +147,7 @@ namespace tunnel
switch (msg.deliveryType) switch (msg.deliveryType)
{ {
case eDeliveryTypeLocal: case eDeliveryTypeLocal:
i2p::HandleI2NPMessage (msg.data); i2p::HandleI2NPMessage (msg.data, true);
break; break;
case eDeliveryTypeTunnel: case eDeliveryTypeTunnel:
i2p::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); 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) void TunnelGatewayBuffer::PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg)
{ {
TunnelMessageBlockExt * block = new TunnelMessageBlockExt; if (!m_CurrentTunnelDataMsg)
block->deliveryInstructionsLen = 1; // flag 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) if (gwHash)
{ {
block->deliveryInstructionsLen += 32; // hash
memcpy (block->hash, gwHash, 32);
if (gwTunnel) if (gwTunnel)
{ {
block->deliveryType = eDeliveryTypeTunnel; *(uint32_t *)(di + diLen) = htobe32 (gwTunnel);
block->deliveryInstructionsLen += 4; // tunnelID diLen += 4; // tunnelID
block->tunnelID = gwTunnel; dt = eDeliveryTypeTunnel;
} }
else else
block->deliveryType = eDeliveryTypeRouter; dt = eDeliveryTypeRouter;
memcpy (di + diLen, gwHash, 32);
diLen += 32; //len
} }
else di[0] = dt << 5; // set delivery type
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; // create fragments
if (block->totalLen <= m_Remaining) // message fits if (diLen + msg->GetLength () + 2<= m_RemainingSize)
{ {
block->isFragmented = false; // message fits. First and last fragment
m_Remaining -= block->totalLen; *(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);
} }
else // message doesn't fit else
{ {
if (block->deliveryInstructionsLen + 4 <= m_Remaining) if (diLen + 6 <= m_RemainingSize)
{ {
// delivery instructions of first fragment fits // delivery instructions fit
block->isFragmented = true; uint32_t msgID = msg->GetHeader ()->msgID;
block->deliveryInstructionsLen += 4; size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size)
block->totalLen += 4;
m_Remaining = m_Remaining + TUNNEL_DATA_MAX_PAYLOAD_SIZE - block->totalLen - 7; // TODO: handle case if more than two fragments // 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 else
{ {
// delivery instructions of first fragment don't fit // delivery instructions don't fit. Create new message
block->isFragmented = false; CompleteCurrentTunnelDataMessage ();
m_Remaining = 0; PutI2NPMsg (gwHash, gwTunnel, msg);
// don't delete msg because it's taken care inside
} }
} }
}
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);
}
for (auto msg: m_I2NPMsgs)
delete msg;
m_I2NPMsgs.clear ();
}
return res;
} }
size_t TunnelGatewayBuffer::CreateFirstFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len) std::vector<I2NPMessage *> TunnelGatewayBuffer::GetTunnelDataMsgs ()
{ {
size_t ret = 1; CompleteCurrentTunnelDataMessage ();
buf[0] = block->deliveryType << 5; // flag std::vector<I2NPMessage *> ret = m_TunnelDataMsgs; // TODO: implement it better
if (block->deliveryType == eDeliveryTypeTunnel) m_TunnelDataMsgs.clear ();
{ return ret;
*(uint32_t *)(buf + ret) = htobe32 (block->tunnelID);
ret += 4;
}
if (block->deliveryType == eDeliveryTypeTunnel || block->deliveryType == eDeliveryTypeRouter)
{
memcpy (buf + ret, block->hash, 32);
ret += 32;
}
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) void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage ()
{ {
int ret = 0; m_CurrentTunnelDataMsg = NewI2NPMessage ();
buf[0] = 0x80 | (m_NextSeqn << 1);// follow-on flag and seqn // we reserve space for padding
size_t fragmentLen = len - 7; // 7 bytes of header m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + sizeof (I2NPHeader);
if (fragmentLen >= block->data->GetLength () - m_NextOffset) m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset;
{ m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE;
// fragment fits
fragmentLen = block->data->GetLength () - m_NextOffset;
buf[0] |= 0x01; // last fragment
}
else
m_NextSeqn++;
*(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;
return ret;
} }
I2NPMessage * TunnelGatewayBuffer::CreateNextTunnelMessage (int& ind) void TunnelGatewayBuffer::CompleteCurrentTunnelDataMessage ()
{ {
int cnt = m_I2NPMsgs.size (); if (!m_CurrentTunnelDataMsg) return;
if (ind > cnt - 1) return nullptr; // no more messages uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer ();
// calculate payload size size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset;
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++;
}
I2NPMessage * tunnelMsg = NewI2NPMessage (); m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - sizeof (I2NPHeader);
uint8_t * buf = tunnelMsg->GetPayload (); uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload ();
*(uint32_t *)(buf) = htobe32 (m_TunnelID); *(uint32_t *)(buf) = htobe32 (m_TunnelID);
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (buf + 4, 16); // original IV rnd.GenerateBlock (buf + 4, 16); // original IV
memcpy (buf + TUNNEL_DATA_MSG_SIZE, buf + 4, 16); // copy IV for checksum memcpy (payload + 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;
}
uint8_t hash[32]; uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest(hash, buf+zero+1, size+16); CryptoPP::SHA256().CalculateDigest (hash, payload, size+16);
memcpy (buf+20, hash, 4); // checksum memcpy (buf+20, hash, 4); // checksum
if (zero > 24) payload[-1] = 0; // zero
memset (buf+24, 1, zero-24); // padding TODO: fill with random data ssize_t paddingSize = payload - buf - 25; // 25 = 24 + 1
tunnelMsg->len += TUNNEL_DATA_MSG_SIZE; 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 // 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) void TunnelGateway::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)

@ -12,32 +12,23 @@ namespace tunnel
{ {
class TunnelGatewayBuffer class TunnelGatewayBuffer
{ {
struct TunnelMessageBlockExt: public TunnelMessageBlock
{
size_t deliveryInstructionsLen, totalLen;
bool isFragmented;
};
public: public:
TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID),
TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID), m_Remaining (0) {}; m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) {};
void PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg); void PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg);
std::vector<I2NPMessage *> GetTunnelDataMsgs (); std::vector<I2NPMessage *> GetTunnelDataMsgs ();
private: private:
size_t CreateFirstFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len); void CreateCurrentTunnelDataMessage ();
size_t CreateFollowOnFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len); void CompleteCurrentTunnelDataMessage ();
I2NPMessage * CreateNextTunnelMessage (int& ind);
private: private:
uint32_t m_TunnelID; uint32_t m_TunnelID;
std::vector<TunnelMessageBlockExt *> m_I2NPMsgs; std::vector<I2NPMessage *> m_TunnelDataMsgs;
// for fragmented messages I2NPMessage * m_CurrentTunnelDataMsg;
size_t m_NextOffset, m_NextSeqn, m_Remaining; size_t m_RemainingSize;
uint32_t m_NextMsgID;
}; };
class TunnelGateway class TunnelGateway

Loading…
Cancel
Save