Browse Source

Merge pull request #1 from orignal/master

merge with master
pull/7/head
mikhail4021 11 years ago
parent
commit
653a2be378
  1. 123
      Garlic.cpp
  2. 13
      Garlic.h
  3. 11
      HTTPServer.cpp
  4. 70
      I2NPProtocol.cpp
  5. 15
      I2NPProtocol.h
  6. 35
      Identity.cpp
  7. 21
      Identity.h
  8. 16
      LeaseSet.cpp
  9. 5
      LeaseSet.h
  10. 24
      NTCPSession.cpp
  11. 14
      NTCPSession.h
  12. 262
      NetDb.cpp
  13. 50
      NetDb.h
  14. 4
      RouterContext.cpp
  15. 13
      RouterInfo.cpp
  16. 5
      RouterInfo.h
  17. 174
      Streaming.cpp
  18. 41
      Streaming.h
  19. 4
      TransitTunnel.cpp
  20. 4
      TransitTunnel.h
  21. 58
      Transports.cpp
  22. 2
      Transports.h
  23. 91
      Tunnel.cpp
  24. 12
      Tunnel.h
  25. 10
      TunnelBase.h
  26. 37
      TunnelConfig.h
  27. 2
      TunnelEndpoint.cpp
  28. 276
      TunnelGateway.cpp
  29. 23
      TunnelGateway.h

123
Garlic.cpp

@ -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:
{ {

13
Garlic.h

@ -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:

11
HTTPServer.cpp

@ -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>";
}
} }
} }

70
I2NPProtocol.cpp

@ -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);
} }
} }

15
I2NPProtocol.h

@ -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

35
Identity.cpp

@ -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;
}
} }
} }

21
Identity.h

@ -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:

16
LeaseSet.cpp

@ -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");
} }
} }
} }

5
LeaseSet.h

@ -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];

24
NTCPSession.cpp

@ -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 ();

14
NTCPSession.h

@ -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;
}; };
} }
} }

262
NetDb.cpp

@ -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;
}
} }
} }

50
NetDb.h

@ -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
}; };

4
RouterContext.cpp

@ -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:

13
RouterInfo.cpp

@ -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));

5
RouterInfo.h

@ -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;

174
Streaming.cpp

@ -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

41
Streaming.h

@ -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);

4
TransitTunnel.cpp

@ -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)

4
TransitTunnel.h

@ -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:

58
Transports.cpp

@ -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");
} }
} }

2
Transports.h

@ -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;

91
Tunnel.cpp

@ -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

12
Tunnel.h

@ -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 ();

10
TunnelBase.h

@ -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
}; };
} }
} }

37
TunnelConfig.h

@ -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 ()
{ {

2
TunnelEndpoint.cpp

@ -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));

276
TunnelGateway.cpp

@ -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)

23
TunnelGateway.h

@ -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