Browse Source

Merge pull request #1 from orignal/master

merge with master
pull/7/head
mikhail4021 11 years ago
parent
commit
653a2be378
  1. 117
      Garlic.cpp
  2. 13
      Garlic.h
  3. 11
      HTTPServer.cpp
  4. 62
      I2NPProtocol.cpp
  5. 13
      I2NPProtocol.h
  6. 35
      Identity.cpp
  7. 21
      Identity.h
  8. 16
      LeaseSet.cpp
  9. 5
      LeaseSet.h
  10. 20
      NTCPSession.cpp
  11. 14
      NTCPSession.h
  12. 198
      NetDb.cpp
  13. 46
      NetDb.h
  14. 4
      RouterContext.cpp
  15. 13
      RouterInfo.cpp
  16. 5
      RouterInfo.h
  17. 168
      Streaming.cpp
  18. 39
      Streaming.h
  19. 4
      TransitTunnel.cpp
  20. 4
      TransitTunnel.h
  21. 12
      Transports.cpp
  22. 2
      Transports.h
  23. 83
      Tunnel.cpp
  24. 12
      Tunnel.h
  25. 10
      TunnelBase.h
  26. 31
      TunnelConfig.h
  27. 2
      TunnelEndpoint.cpp
  28. 260
      TunnelGateway.cpp
  29. 23
      TunnelGateway.h

117
Garlic.cpp

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

13
Garlic.h

@ -40,13 +40,15 @@ namespace garlic @@ -40,13 +40,15 @@ namespace garlic
GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags);
~GarlicRoutingSession ();
I2NPMessage * WrapSingleMessage (I2NPMessage * msg);
I2NPMessage * WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet);
int GetNumRemainingSessionTags () const { return m_NumTags - m_NextTag; };
private:
size_t CreateAESBlock (uint8_t * buf, I2NPMessage * msg);
size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg);
size_t CreateAESBlock (uint8_t * buf, I2NPMessage * msg, I2NPMessage * leaseSet);
size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg, I2NPMessage * leaseSet);
size_t CreateGarlicClove (uint8_t * buf, I2NPMessage * msg, bool isDestination);
size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID);
private:
@ -66,9 +68,10 @@ namespace garlic @@ -66,9 +68,10 @@ namespace garlic
GarlicRouting ();
~GarlicRouting ();
void HandleGarlicMessage (uint8_t * buf, size_t len);
void HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel);
I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * msg);
I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination,
I2NPMessage * msg, I2NPMessage * leaseSet = nullptr);
private:

11
HTTPServer.cpp

@ -104,15 +104,22 @@ namespace util @@ -104,15 +104,22 @@ namespace util
s << "-->" << it.second->GetTunnelID ();
else
s << "-->" << it.second->GetTunnelID () << "-->";
s << "<BR>";
s << " " << it.second->GetNumTransmittedBytes () << "<BR>";
}
s << "<P>Transports</P>";
for (auto it: i2p::transports.GetNTCPSessions ())
{
// RouterInfo of incoming connection doesn't have address
bool outgoing = it.second->GetRemoteRouterInfo ().GetNTCPAddress ();
if (it.second->IsEstablished ())
{
if (outgoing) s << "-->";
s << it.second->GetRemoteRouterInfo ().GetIdentHashAbbreviation () << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string () << "<BR>";
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
if (!outgoing) s << "-->";
s << "<BR>";
}
}
}

62
I2NPProtocol.cpp

@ -67,7 +67,7 @@ namespace i2p @@ -67,7 +67,7 @@ namespace i2p
return msg;
}
I2NPMessage * CreateDeliveryStatusMsg ()
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
{
#pragma pack(1)
struct
@ -77,13 +77,13 @@ namespace i2p @@ -77,13 +77,13 @@ namespace i2p
} msg;
#pragma pack ()
msg.msgID = 0;
msg.msgID = htobe32 (msgID);
msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ());
return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg));
}
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory)
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
{
I2NPMessage * m = NewI2NPMessage ();
uint8_t * buf = m->GetPayload ();
@ -112,16 +112,60 @@ namespace i2p @@ -112,16 +112,60 @@ namespace i2p
buf += 32;
}
else
{
if (excludedPeers)
{
int cnt = excludedPeers->size ();
*(uint16_t *)buf = htobe16 (cnt);
buf += 2;
for (auto& it: *excludedPeers)
{
memcpy (buf, it, 32);
buf += 32;
}
}
else
{
// nothing to exclude
*(uint16_t *)buf = htobe16 (0);
buf += 2;
}
}
m->len += (buf - m->GetPayload ());
FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
return m;
}
void HandleDatabaseLookupMsg (uint8_t * buf, size_t len)
{
char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
LogPrint ("DatabaseLookup for ", key, " recieved");
uint8_t flag = buf[64];
uint32_t replyTunnelID = 0;
if (flag & 0x01) //reply to yunnel
replyTunnelID = be32toh (*(uint32_t *)(buf + 64));
// TODO: implement search. We send non-found for now
I2NPMessage * replyMsg = CreateDatabaseSearchReply (buf);
if (replyTunnelID)
i2p::tunnel::tunnels.GetNextOutboundTunnel ()->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg);
else
i2p::transports.SendMessage (buf, replyMsg);
}
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident)
{
I2NPMessage * m = NewI2NPMessage ();
uint8_t * buf = m->GetPayload ();
memcpy (buf, ident, 32);
buf[32] = 0; // TODO:
memcpy (buf + 33, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
m->len += 65;
FillI2NPMessageHeader (m, eI2NPDatabaseSearchReply);
return m;
}
I2NPMessage * CreateDatabaseStoreMsg ()
{
I2NPMessage * m = NewI2NPMessage ();
@ -370,7 +414,7 @@ namespace i2p @@ -370,7 +414,7 @@ namespace i2p
return be16toh (header->size) + sizeof (I2NPHeader);
}
void HandleI2NPMessage (uint8_t * msg, size_t len)
void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel)
{
I2NPHeader * header = (I2NPHeader *)msg;
uint32_t msgID = be32toh (header->msgID);
@ -382,7 +426,7 @@ namespace i2p @@ -382,7 +426,7 @@ namespace i2p
{
case eI2NPGarlic:
LogPrint ("Garlic");
i2p::garlic::routing.HandleGarlicMessage (buf, size);
i2p::garlic::routing.HandleGarlicMessage (buf, size, isFromTunnel);
break;
break;
case eI2NPDeliveryStatus:
@ -396,12 +440,16 @@ namespace i2p @@ -396,12 +440,16 @@ namespace i2p
LogPrint ("VariableTunnelBuildReply");
HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
break;
case eI2NPDatabaseLookup:
LogPrint ("DatabaseLookup");
HandleDatabaseLookupMsg (buf, size);
break;
default:
LogPrint ("Unexpected message ", (int)header->typeID);
}
}
void HandleI2NPMessage (I2NPMessage * msg)
void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel)
{
if (msg)
{
@ -424,7 +472,7 @@ namespace i2p @@ -424,7 +472,7 @@ namespace i2p
i2p::data::netdb.PostI2NPMsg (msg);
break;
default:
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength (), isFromTunnel);
DeleteI2NPMessage (msg);
}
}

13
I2NPProtocol.h

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
#define I2NP_PROTOCOL_H__
#include <inttypes.h>
#include <vector>
#include <set>
#include <string.h>
#include "RouterInfo.h"
@ -103,9 +103,12 @@ namespace i2p @@ -103,9 +103,12 @@ namespace i2p
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0);
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len);
I2NPMessage * CreateDeliveryStatusMsg ();
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID);
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false);
uint32_t replyTunnelID, bool exploratory = false,
std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
void HandleDatabaseLookupMsg (uint8_t * buf, size_t len);
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident);
I2NPMessage * CreateDatabaseStoreMsg ();
@ -132,8 +135,8 @@ namespace i2p @@ -132,8 +135,8 @@ namespace i2p
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg);
size_t GetI2NPMessageLength (uint8_t * msg);
void HandleI2NPMessage (uint8_t * msg, size_t len);
void HandleI2NPMessage (I2NPMessage * msg);
void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel);
void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel);
}
#endif

35
Identity.cpp

@ -1,3 +1,5 @@ @@ -1,3 +1,5 @@
#include <time.h>
#include <stdio.h>
#include <cryptopp/sha.h>
#include <cryptopp/osrng.h>
#include <cryptopp/dh.h>
@ -9,6 +11,14 @@ namespace i2p @@ -9,6 +11,14 @@ namespace i2p
{
namespace data
{
Identity& Identity::operator=(const Keys& keys)
{
// copy public and signing keys together
memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey));
memset (certificate, 0, sizeof (certificate));
return *this;
}
IdentHash CalculateIdentHash (const Identity& identity)
{
IdentHash hash;
@ -35,5 +45,30 @@ namespace data @@ -35,5 +45,30 @@ namespace data
return keys;
}
RoutingKey CreateRoutingKey (const IdentHash& ident)
{
uint8_t buf[41]; // ident + yyyymmdd
memcpy (buf, (const uint8_t *)ident, 32);
time_t t = time (nullptr);
struct tm tm;
gmtime_r (&t, &tm);
sprintf ((char *)(buf + 32),"%4i%2i%2i", tm.tm_year, tm.tm_mon, tm.tm_mday);
RoutingKey key;
CryptoPP::SHA256().CalculateDigest(key.hash, buf, 40);
return key;
}
XORMetric operator^(const RoutingKey& key1, const RoutingKey& key2)
{
// TODO: implementation depends on CPU
XORMetric m;
((uint64_t *)m.metric)[0] = ((uint64_t *)key1.hash)[0] ^ ((uint64_t *)key2.hash)[0];
((uint64_t *)m.metric)[1] = ((uint64_t *)key1.hash)[1] ^ ((uint64_t *)key2.hash)[1];
((uint64_t *)m.metric)[2] = ((uint64_t *)key1.hash)[2] ^ ((uint64_t *)key2.hash)[2];
((uint64_t *)m.metric)[3] = ((uint64_t *)key1.hash)[3] ^ ((uint64_t *)key2.hash)[3];
return m;
}
}
}

21
Identity.h

@ -23,6 +23,8 @@ namespace data @@ -23,6 +23,8 @@ namespace data
uint8_t publicKey[256];
uint8_t signingKey[128];
uint8_t certificate[3];
Identity& operator=(const Keys& keys);
};
#pragma pack()
@ -60,6 +62,25 @@ namespace data @@ -60,6 +62,25 @@ namespace data
IdentHash CalculateIdentHash (const Identity& identity);
Keys CreateRandomKeys ();
// kademlia
struct RoutingKey
{
uint8_t hash[32];
};
struct XORMetric
{
uint8_t metric[32];
void SetMin () { memset (metric, 0, 32); };
void SetMax () { memset (metric, 0xFF, 32); };
bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; };
};
RoutingKey CreateRoutingKey (const IdentHash& ident);
XORMetric operator^(const RoutingKey& key1, const RoutingKey& key2);
// destination for delivery instuctions
class RoutingDestination
{
public:

16
LeaseSet.cpp

@ -1,3 +1,5 @@ @@ -1,3 +1,5 @@
#include <cryptopp/dsa.h>
#include "CryptoConst.h"
#include "Log.h"
#include "LeaseSet.h"
@ -24,10 +26,22 @@ namespace data @@ -24,10 +26,22 @@ namespace data
memcpy (m_EncryptionKey, header->encryptionKey, 256);
LogPrint ("LeaseSet num=", (int)header->num);
const uint8_t * leases = buf + sizeof (H);
for (int i = 0; i < header->num; i++)
{
m_Leases.push_back (*(Lease *)(buf + sizeof (H)));
Lease lease = *(Lease *)leases;
lease.tunnelID = be32toh (lease.tunnelID);
m_Leases.push_back (lease);
leases += sizeof (Lease);
}
// verify
CryptoPP::DSA::PublicKey pubKey;
pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
CryptoPP::Integer (m_Identity.signingKey, 128));
CryptoPP::DSA::Verifier verifier (pubKey);
if (!verifier.VerifyMessage (buf, leases - buf, leases, 40))
LogPrint ("LeaseSet verification failed");
}
}
}

5
LeaseSet.h

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
#include <inttypes.h>
#include <string.h>
#include <list>
#include <vector>
#include "Identity.h"
namespace i2p
@ -31,12 +31,13 @@ namespace data @@ -31,12 +31,13 @@ namespace data
// implements RoutingDestination
const Identity& GetIdentity () const { return m_Identity; };
const IdentHash& GetIdentHash () const { return m_IdentHash; };
const std::vector<Lease>& GetLeases () const { return m_Leases; };
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; };
bool IsDestination () const { return true; };
private:
std::list<Lease> m_Leases;
std::vector<Lease> m_Leases;
Identity m_Identity;
IdentHash m_IdentHash;
uint8_t m_EncryptionKey[256];

20
NTCPSession.cpp

@ -20,12 +20,10 @@ namespace i2p @@ -20,12 +20,10 @@ namespace i2p
{
namespace ntcp
{
NTCPSession::NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo * in_RemoteRouterInfo):
NTCPSession::NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo& in_RemoteRouterInfo):
m_Socket (service), m_TerminationTimer (service), m_IsEstablished (false),
m_ReceiveBufferOffset (0), m_NextMessage (nullptr)
m_RemoteRouterInfo (in_RemoteRouterInfo), m_ReceiveBufferOffset (0), m_NextMessage (nullptr)
{
if (in_RemoteRouterInfo)
m_RemoteRouterInfo = *in_RemoteRouterInfo;
}
void NTCPSession::CreateAESKey (uint8_t * pubKey, uint8_t * aesKey)
@ -186,7 +184,8 @@ namespace ntcp @@ -186,7 +184,8 @@ namespace ntcp
{
if (ecode)
{
LogPrint ("Phase 2 read error: ", ecode.message ());
LogPrint ("Phase 2 read error: ", ecode.message (), ". Wrong ident assumed");
GetRemoteRouterInfo ().SetUnreachable (true); // this RouterInfo is not valid
Terminate ();
}
else
@ -386,6 +385,7 @@ namespace ntcp @@ -386,6 +385,7 @@ namespace ntcp
memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset);
}
ScheduleTermination (); // reset termination timer
Receive ();
}
}
@ -424,7 +424,7 @@ namespace ntcp @@ -424,7 +424,7 @@ namespace ntcp
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
{
// we have a complete I2NP message
i2p::HandleI2NPMessage (m_NextMessage);
i2p::HandleI2NPMessage (m_NextMessage, false);
m_NextMessage = nullptr;
}
}
@ -461,14 +461,10 @@ namespace ntcp @@ -461,14 +461,10 @@ namespace ntcp
m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding);
int l = len + padding + 6;
{
std::lock_guard<std::mutex> lock (m_EncryptionMutex);
m_Encryption.ProcessData(sendBuffer, sendBuffer, l);
}
boost::asio::async_write (m_Socket, boost::asio::buffer (sendBuffer, l), boost::asio::transfer_all (),
boost::bind(&NTCPSession::HandleSent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, msg));
ScheduleTermination (); // reset termination timer
}
void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, i2p::I2NPMessage * msg)
@ -483,6 +479,7 @@ namespace ntcp @@ -483,6 +479,7 @@ namespace ntcp
else
{
LogPrint ("Msg sent: ", bytes_transferred);
ScheduleTermination (); // reset termination timer
}
}
@ -521,7 +518,8 @@ namespace ntcp @@ -521,7 +518,8 @@ namespace ntcp
NTCPClient::NTCPClient (boost::asio::io_service& service, const char * address,
int port, i2p::data::RouterInfo& in_RouterInfo): NTCPSession (service, &in_RouterInfo),
int port, i2p::data::RouterInfo& in_RouterInfo):
NTCPSession (service, in_RouterInfo),
m_Endpoint (boost::asio::ip::address::from_string (address), port)
{
Connect ();

14
NTCPSession.h

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

198
NetDb.cpp

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
#include "I2NPProtocol.h"
#include "Tunnel.h"
#include "RouterContext.h"
#include "Garlic.h"
#include "NetDb.h"
namespace i2p
@ -15,9 +16,22 @@ namespace i2p @@ -15,9 +16,22 @@ namespace i2p
namespace data
{
I2NPMessage * RequestedDestination::CreateRequestMessage (const RouterInfo * router,
const i2p::tunnel::InboundTunnel * replyTunnel)
{
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, &m_ExcludedPeers);
if (m_IsLeaseSet) // wrap lookup message into garlic
msg = i2p::garlic::routing.WrapSingleMessage (router, msg);
m_ExcludedPeers.insert (router->GetIdentHash ());
m_LastRouter = router;
m_LastReplyTunnel = replyTunnel;
return msg;
}
NetDb netdb;
NetDb::NetDb (): m_IsRunning (false), m_Thread (0), m_LastFloodfill (0)
NetDb::NetDb (): m_IsRunning (false), m_Thread (0)
{
}
@ -28,6 +42,8 @@ namespace data @@ -28,6 +42,8 @@ namespace data
delete l.second;
for (auto r:m_RouterInfos)
delete r.second;
for (auto r:m_RequestedDestinations)
delete r.second;
}
void NetDb::Start ()
@ -70,7 +86,7 @@ namespace data @@ -70,7 +86,7 @@ namespace data
else // WTF?
{
LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID);
i2p::HandleI2NPMessage (msg);
i2p::HandleI2NPMessage (msg, false);
}
msg = m_Queue.Get ();
}
@ -96,6 +112,7 @@ namespace data @@ -96,6 +112,7 @@ namespace data
void NetDb::AddRouterInfo (uint8_t * buf, int len)
{
RouterInfo * r = new RouterInfo (buf, len);
DeleteRequestedDestination (r->GetIdentHash ());
auto it = m_RouterInfos.find(r->GetIdentHash ());
if (it != m_RouterInfos.end ())
{
@ -117,6 +134,7 @@ namespace data @@ -117,6 +134,7 @@ namespace data
void NetDb::AddLeaseSet (uint8_t * buf, int len)
{
LeaseSet * l = new LeaseSet (buf, len);
DeleteRequestedDestination (l->GetIdentHash ());
m_LeaseSets[l->GetIdentHash ()] = l;
}
@ -129,6 +147,15 @@ namespace data @@ -129,6 +147,15 @@ namespace data
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)
{
boost::filesystem::path p (directory);
@ -156,39 +183,67 @@ namespace data @@ -156,39 +183,67 @@ namespace data
void NetDb::SaveUpdated (const char * directory)
{
int count = 0;
auto GetFilePath = [](const char * directory, const RouterInfo * routerInfo)
{
return std::string (directory) + "/r" +
routerInfo->GetIdentHashBase64 ()[0] + "/routerInfo-" +
routerInfo->GetIdentHashBase64 () + ".dat";
};
int count = 0, deletedCount = 0;
for (auto it: m_RouterInfos)
{
if (it.second->IsUpdated ())
{
std::ofstream r (std::string (directory) + "/r" +
it.second->GetIdentHashBase64 ()[0] + "/routerInfo-" +
it.second->GetIdentHashBase64 () + ".dat");
std::ofstream r (GetFilePath(directory, it.second));
r.write ((char *)it.second->GetBuffer (), it.second->GetBufferLen ());
it.second->SetUpdated (false);
count++;
}
else if (it.second->IsUnreachable ())
{
if (boost::filesystem::exists (GetFilePath (directory, it.second)))
{
boost::filesystem::remove (GetFilePath (directory, it.second));
deletedCount++;
}
}
}
if (count > 0)
LogPrint (count," new/updated routers saved");
if (deletedCount > 0)
LogPrint (deletedCount," routers deleted");
}
void NetDb::RequestDestination (const char * b32, const uint8_t * router)
void NetDb::RequestDestination (const char * b32)
{
uint8_t destination[32];
Base32ToByteStream (b32, strlen(b32), destination, 32);
RequestDestination (destination, router);
RequestDestination (destination, true);
}
void NetDb::RequestDestination (const IdentHash& destination, bool isLeaseSet)
{
auto floodfill= GetRandomNTCPRouter (true);
if (floodfill)
RequestDestination (destination, floodfill, isLeaseSet);
else
LogPrint ("No floodfill routers found");
}
void NetDb::RequestDestination (const uint8_t * destination, const uint8_t * router)
void NetDb::RequestDestination (const IdentHash& destination, const RouterInfo * floodfill, bool isLeaseSet)
{
if (!floodfill) return;
i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
if (outbound)
{
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (inbound)
{
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (destination, inbound->GetNextIdentHash (),
inbound->GetNextTunnelID ());
outbound->SendTunnelDataMsg (router, 0, msg);
RequestedDestination * dest = CreateRequestedDestination (destination, isLeaseSet);
dest->SetLastOutboundTunnel (outbound);
auto msg = dest->CreateRequestMessage (floodfill, inbound);
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
}
else
LogPrint ("No inbound tunnels found");
@ -236,11 +291,14 @@ namespace data @@ -236,11 +291,14 @@ namespace data
key[l] = 0;
int num = buf[32]; // num
LogPrint ("DatabaseSearchReply for ", key, " num=", num);
auto it = m_RequestedDestinations.find (IdentHash (buf));
if (it != m_RequestedDestinations.end ())
{
RequestedDestination * dest = it->second;
if (num > 0)
{
bool isExploratory = !memcmp (m_Exploratory, buf, 32) && m_LastFloodfill;
i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
i2p::tunnel::OutboundTunnel * outbound = dest->GetLastOutboundTunnel ();
const i2p::tunnel::InboundTunnel * inbound = dest->GetLastReplyTunnel ();
for (int i = 0; i < num; i++)
{
@ -250,16 +308,17 @@ namespace data @@ -250,16 +308,17 @@ namespace data
peerHash[l1] = 0;
LogPrint (i,": ", peerHash);
if (isExploratory)
if (dest->IsExploratory ())
{
if (m_RouterInfos.find (IdentHash(router)) == m_RouterInfos.end ())
if (!FindRouter (router)) // router with ident not found
{
LogPrint ("Found new router. Requesting RouterInfo ...");
if (outbound && inbound)
{
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (router, inbound->GetNextIdentHash (),
inbound->GetNextTunnelID ());
outbound->GetTunnelGateway ().PutTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0, msg);
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
@ -270,19 +329,26 @@ namespace data @@ -270,19 +329,26 @@ namespace data
// reply to our destination. Try other floodfills
if (outbound && inbound)
{
auto r = FindRouter (router);
// do we have that floodfill router in our database?
if (!FindRouter (router))
if (r)
{
if (!dest->IsExcluded (r->GetIdentHash ()) && dest->GetNumExcludedPeers () < 10) // TODO: fix TunnelGateway first
{
// request destination
auto msg = dest->CreateRequestMessage (r, dest->GetLastReplyTunnel ());
outbound->GetTunnelGateway ().PutTunnelDataMsg (r->GetIdentHash (), 0, msg);
}
}
else
{
// request router
LogPrint ("Found new floodfill. Request it");
msg = i2p::CreateDatabaseLookupMsg (router, inbound->GetNextIdentHash (),
inbound->GetNextTunnelID ());
RequestedDestination * d2 = CreateRequestedDestination (router, false, false);
d2->SetLastOutboundTunnel (outbound);
I2NPMessage * msg = d2->CreateRequestMessage (dest->GetLastRouter (), inbound);
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);
dest->GetLastRouter ()->GetIdentHash (), 0, msg);
}
}
}
@ -291,26 +357,65 @@ namespace data @@ -291,26 +357,65 @@ namespace data
if (outbound)
outbound->GetTunnelGateway ().SendBuffer ();
}
else
{
// no more requests for detination possible. delete it
m_RequestedDestinations.erase (it);
delete it->second;
}
}
else
LogPrint ("Requested destination for ", key, " not found");
i2p::DeleteI2NPMessage (msg);
}
void NetDb::Explore ()
{
i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
auto outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (outbound && inbound)
{
m_LastFloodfill = GetRandomNTCPRouter (true);
if (m_LastFloodfill)
auto floodfill = GetRandomNTCPRouter (true);
if (floodfill)
{
LogPrint ("Exploring new routers ...");
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (m_Exploratory, 32);
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Exploratory, inbound->GetNextIdentHash (),
inbound->GetNextTunnelID (), true);
outbound->SendTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0, msg);
uint8_t randomHash[32];
rnd.GenerateBlock (randomHash, 32);
RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true);
dest->SetLastOutboundTunnel (outbound);
outbound->GetTunnelGateway ().PutTunnelDataMsg (floodfill->GetIdentHash (), 0,
CreateDatabaseStoreMsg ()); // tell floodfill about us
outbound->GetTunnelGateway ().PutTunnelDataMsg (floodfill->GetIdentHash (), 0,
dest->CreateRequestMessage (floodfill, inbound)); // explore
outbound->GetTunnelGateway ().SendBuffer ();
}
}
}
RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest,
bool isLeaseSet, bool isExploratory)
{
auto it = m_RequestedDestinations.find (dest);
if (it == m_RequestedDestinations.end ()) // not exist yet
{
RequestedDestination * d = new RequestedDestination (dest, isLeaseSet, isExploratory);
m_RequestedDestinations[dest] = d;
return d;
}
else
return it->second;
}
void NetDb::DeleteRequestedDestination (const IdentHash& dest)
{
auto it = m_RequestedDestinations.find (dest);
if (it != m_RequestedDestinations.end ())
{
m_RequestedDestinations.erase (it);
delete it->second;
}
}
const RouterInfo * NetDb::GetRandomNTCPRouter (bool floodfillOnly) const
@ -345,5 +450,26 @@ namespace data @@ -345,5 +450,26 @@ namespace data
{
if (msg) m_Queue.Put (msg);
}
const RouterInfo * NetDb::GetClosestFloodfill (const IdentHash& destination) const
{
RouterInfo * r = nullptr;
XORMetric minMetric;
RoutingKey destKey = CreateRoutingKey (destination);
minMetric.SetMax ();
for (auto it: m_RouterInfos)
{
if (it.second->IsFloodfill () &&! it.second->IsUnreachable ())
{
XORMetric m = destKey ^ it.second->GetRoutingKey ();
if (m < minMetric)
{
minMetric = m;
r = it.second;
}
}
}
return r;
}
}
}

46
NetDb.h

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
#define NETDB_H__
#include <inttypes.h>
#include <set>
#include <map>
#include <string>
#include <thread>
@ -9,11 +10,41 @@ @@ -9,11 +10,41 @@
#include "I2NPProtocol.h"
#include "RouterInfo.h"
#include "LeaseSet.h"
#include "Tunnel.h"
namespace i2p
{
namespace data
{
class RequestedDestination
{
public:
RequestedDestination (const IdentHash& destination, bool isLeaseSet, bool isExploratory = false):
m_Destination (destination), m_IsLeaseSet (isLeaseSet), m_IsExploratory (isExploratory),
m_LastRouter (nullptr), m_LastReplyTunnel (nullptr), m_LastOutboundTunnel (nullptr) {};
const IdentHash& GetDestination () const { return m_Destination; };
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
const RouterInfo * GetLastRouter () const { return m_LastRouter; };
const i2p::tunnel::InboundTunnel * GetLastReplyTunnel () const { return m_LastReplyTunnel; };
bool IsExploratory () const { return m_IsExploratory; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
I2NPMessage * CreateRequestMessage (const RouterInfo * router, const i2p::tunnel::InboundTunnel * replyTunnel);
i2p::tunnel::OutboundTunnel * GetLastOutboundTunnel () const { return m_LastOutboundTunnel; };
void SetLastOutboundTunnel (i2p::tunnel::OutboundTunnel * tunnel) { m_LastOutboundTunnel = tunnel; };
private:
IdentHash m_Destination;
bool m_IsLeaseSet, m_IsExploratory;
std::set<IdentHash> m_ExcludedPeers;
const RouterInfo * m_LastRouter;
const i2p::tunnel::InboundTunnel * m_LastReplyTunnel;
i2p::tunnel::OutboundTunnel * m_LastOutboundTunnel;
};
class NetDb
{
public:
@ -27,9 +58,12 @@ namespace data @@ -27,9 +58,12 @@ namespace data
void AddRouterInfo (uint8_t * buf, int len);
void AddLeaseSet (uint8_t * buf, int len);
RouterInfo * FindRouter (const IdentHash& ident) const;
LeaseSet * FindLeaseSet (const IdentHash& destination) const;
void RequestDestination (const char * b32); // in base32
void RequestDestination (const IdentHash& destination, bool isLeaseSet = false);
void RequestDestination (const IdentHash& destination, const RouterInfo * floodfill, bool isLeaseSet = false);
void RequestDestination (const char * b32, const uint8_t * router); // in base32
void RequestDestination (const uint8_t * destination, const uint8_t * router);
void HandleDatabaseStoreMsg (uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
@ -44,16 +78,20 @@ namespace data @@ -44,16 +78,20 @@ namespace data
void SaveUpdated (const char * directory);
void Run (); // exploratory thread
void Explore ();
const RouterInfo * GetClosestFloodfill (const IdentHash& destination) const;
RequestedDestination * CreateRequestedDestination (const IdentHash& dest,
bool isLeaseSet, bool isExploratory = false);
void DeleteRequestedDestination (const IdentHash& dest);
private:
std::map<IdentHash, LeaseSet *> m_LeaseSets;
std::map<IdentHash, RouterInfo *> m_RouterInfos;
std::map<IdentHash, RequestedDestination *> m_RequestedDestinations;
bool m_IsRunning;
std::thread * m_Thread;
uint8_t m_Exploratory[32];
const RouterInfo * m_LastFloodfill;
i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
};

4
RouterContext.cpp

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

13
RouterInfo.cpp

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

5
RouterInfo.h

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

168
Streaming.cpp

@ -4,14 +4,18 @@ @@ -4,14 +4,18 @@
#include "Log.h"
#include "RouterInfo.h"
#include "RouterContext.h"
#include "Tunnel.h"
#include "Timestamp.h"
#include "CryptoConst.h"
#include "Garlic.h"
#include "Streaming.h"
namespace i2p
{
namespace stream
{
Stream::Stream (const i2p::data::IdentHash& destination):
m_SendStreamID (0)
Stream::Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote):
m_SendStreamID (0), m_SequenceNumber (0), m_LocalDestination (local), m_RemoteLeaseSet (remote)
{
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
}
@ -57,7 +61,66 @@ namespace stream @@ -57,7 +61,66 @@ namespace stream
LogPrint ("Payload: ", str);
}
StreamingDestination m_SharedLocalDestination;
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);
}
void StreamingDestination::HandleNextPacket (const uint8_t * buf, size_t len)
{
@ -69,23 +132,91 @@ namespace stream @@ -69,23 +132,91 @@ namespace stream
LogPrint ("Unknown stream ", sendStreamID);
}
Stream * StreamingDestination::CreateNewStream (const i2p::data::IdentHash& destination)
Stream * StreamingDestination::CreateNewStream (const i2p::data::LeaseSet * remote)
{
/*i2p::data::LeaseSet * leaseSet = i2p::data::netdb.FindLeaseSet (destination);
if (!leaseSet)
Stream * s = new Stream (this, remote);
m_Streams[s->GetRecvStreamID ()] = s;
return s;
}
void StreamingDestination::DeleteStream (Stream * stream)
{
i2p::data::netdb.RequestDestination (destination);
sleep (5); // wait for 5 seconds
leaseSet = i2p::data::netdb.FindLeaseSet (destination);
if (!leaseSet)
if (stream)
{
LogPrint ("Couldn't find LeaseSet");
return nullptr;
m_Streams.erase (stream->GetRecvStreamID ());
delete stream;
}
} */
Stream * s = new Stream (destination);
m_Streams[s->GetRecvStreamID ()] = s;
return s;
}
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)
@ -104,7 +235,8 @@ namespace stream @@ -104,7 +235,8 @@ namespace stream
decompressor.Get (uncompressed, uncompressedSize);
// then forward to streaming engine
// TODO: we have onle one destination, might be more
m_SharedLocalDestination.HandleNextPacket (uncompressed, uncompressedSize);
if (sharedLocalDestination)
sharedLocalDestination->HandleNextPacket (uncompressed, uncompressedSize);
}
else
LogPrint ("Data: protocol ", buf[9], " is not supported");
@ -118,7 +250,7 @@ namespace stream @@ -118,7 +250,7 @@ namespace stream
compressor.MessageEnd();
int size = compressor.MaxRetrievable ();
uint8_t * buf = msg->GetPayload ();
*(uint16_t *)buf = htobe32 (size); // length
*(uint32_t *)buf = htobe32 (size); // length
buf += 4;
compressor.Get (buf, size);
buf[9] = 6; // streaming protocol

39
Streaming.h

@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
#include <inttypes.h>
#include <map>
#include <cryptopp/dsa.h>
#include "Identity.h"
#include "LeaseSet.h"
#include "I2NPProtocol.h"
@ -22,33 +24,64 @@ namespace stream @@ -22,33 +24,64 @@ namespace stream
const uint16_t PACKET_FLAG_ECHO = 0x0200;
const uint16_t PACKET_FLAG_NO_ACK = 0x0400;
const size_t STREAMING_MTU = 1730;
class StreamingDestination;
class Stream
{
public:
Stream (const i2p::data::IdentHash& destination);
Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote);
uint32_t GetSendStreamID () const { return m_SendStreamID; };
uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
const i2p::data::LeaseSet * GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
bool IsEstablished () const { return !m_SendStreamID; };
void HandleNextPacket (const uint8_t * buf, size_t len);
size_t Send (uint8_t * buf, size_t len, int timeout); // timeout in seconds
private:
uint32_t m_SendStreamID, m_RecvStreamID;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
StreamingDestination * m_LocalDestination;
const i2p::data::LeaseSet * m_RemoteLeaseSet;
};
class StreamingDestination
{
public:
Stream * CreateNewStream (const i2p::data::IdentHash& destination);
StreamingDestination ();
~StreamingDestination ();
const i2p::data::Keys& GetKeys () const { return m_Keys; };
const i2p::data::Identity& GetIdentity () const { return m_Identity; };
I2NPMessage * GetLeaseSet ();
void Sign (uint8_t * buf, int len, uint8_t * signature) const;
Stream * CreateNewStream (const i2p::data::LeaseSet * remote);
void DeleteStream (Stream * stream);
void HandleNextPacket (const uint8_t * buf, size_t len);
private:
I2NPMessage * CreateLeaseSet () const;
private:
std::map<uint32_t, Stream *> m_Streams;
i2p::data::Keys m_Keys;
i2p::data::Identity m_Identity;
i2p::data::IdentHash m_IdentHash;
I2NPMessage * m_LeaseSet;
CryptoPP::DSA::PrivateKey m_SigningPrivateKey;
};
Stream * CreateStream (const i2p::data::LeaseSet * remote);
void CloseStream (Stream * stream);
// assuming data is I2CP message
void HandleDataMessage (i2p::data::IdentHash * destination, const uint8_t * buf, size_t len);
I2NPMessage * CreateDataMessage (Stream * s, uint8_t * payload, size_t len);

4
TransitTunnel.cpp

@ -14,7 +14,8 @@ namespace tunnel @@ -14,7 +14,8 @@ namespace tunnel
TransitTunnel::TransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent)
m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID),
m_NextIdent (nextIdent), m_NumTransmittedBytes (0)
{
memcpy (m_LayerKey, layerKey, 32);
memcpy (m_IVKey, ivKey, 32);
@ -43,6 +44,7 @@ namespace tunnel @@ -43,6 +44,7 @@ namespace tunnel
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
i2p::transports.SendMessage (m_NextIdent, tunnelMsg);
m_NumTransmittedBytes += tunnelMsg->GetLength ();
}
void TransitTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)

4
TransitTunnel.h

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

12
Transports.cpp

@ -23,6 +23,7 @@ namespace i2p @@ -23,6 +23,7 @@ namespace i2p
void Transports::Start ()
{
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this));
// create acceptors
auto addresses = context.GetRouterInfo ().GetAddresses ();
@ -48,6 +49,7 @@ namespace i2p @@ -48,6 +49,7 @@ namespace i2p
m_NTCPSessions.clear ();
delete m_NTCPAcceptor;
m_IsRunning = false;
m_Service.stop ();
if (m_Thread)
{
@ -58,6 +60,8 @@ namespace i2p @@ -58,6 +60,8 @@ namespace i2p
}
void Transports::Run ()
{
while (m_IsRunning)
{
try
{
@ -68,6 +72,7 @@ namespace i2p @@ -68,6 +72,7 @@ namespace i2p
LogPrint ("Transports: ", ex.what ());
}
}
}
void Transports::AddNTCPSession (i2p::ntcp::NTCPSession * session)
{
@ -118,8 +123,12 @@ namespace i2p @@ -118,8 +123,12 @@ namespace i2p
{
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
// we send it to ourself
i2p::HandleI2NPMessage (msg);
i2p::HandleI2NPMessage (msg, false);
else
m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg));
}
void Transports::PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
{
auto session = FindNTCPSession (ident);
if (!session)
@ -141,4 +150,3 @@ namespace i2p @@ -141,4 +150,3 @@ namespace i2p
LogPrint ("Session not found");
}
}
}

2
Transports.h

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

83
Tunnel.cpp

@ -14,8 +14,7 @@ namespace i2p @@ -14,8 +14,7 @@ namespace i2p
namespace tunnel
{
Tunnel::Tunnel (TunnelConfig * config): m_Config (config),
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_IsEstablished (false)
Tunnel::Tunnel (TunnelConfig * config): m_Config (config), m_IsEstablished (false)
{
}
@ -67,15 +66,10 @@ namespace tunnel @@ -67,15 +66,10 @@ namespace tunnel
FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild);
if (outboundTunnel)
{
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
DeleteI2NPMessage (msg);
}
else
{
i2p::transports.SendMessage (GetNextIdentHash (), msg);
}
}
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
{
@ -220,7 +214,17 @@ namespace tunnel @@ -220,7 +214,17 @@ namespace tunnel
OutboundTunnel * Tunnels::GetNextOutboundTunnel ()
{
OutboundTunnel * tunnel = nullptr;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0;
for (auto it: m_OutboundTunnels)
{
if (i >= ind) return it;
else i++;
}
return nullptr;
// TODO: implement it base on profiling
/*OutboundTunnel * tunnel = nullptr;
size_t minSent = 0;
for (auto it : m_OutboundTunnels)
if (!tunnel || it->GetNumSentBytes () < minSent)
@ -228,7 +232,7 @@ namespace tunnel @@ -228,7 +232,7 @@ namespace tunnel
tunnel = it;
minSent = it->GetNumSentBytes ();
}
return tunnel;
return tunnel;*/
}
void Tunnels::AddTransitTunnel (TransitTunnel * tunnel)
@ -311,6 +315,7 @@ namespace tunnel @@ -311,6 +315,7 @@ namespace tunnel
ManageInboundTunnels ();
ManageOutboundTunnels ();
ManageTransitTunnels ();
/* if (!m_IsTunnelCreated)
{
@ -349,18 +354,24 @@ namespace tunnel @@ -349,18 +354,24 @@ namespace tunnel
{
LogPrint ("Creating one hop outbound tunnel...");
CreateTunnel<OutboundTunnel> (
new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
i2p::data::netdb.GetRandomNTCPRouter ()
},
inboundTunnel->GetTunnelConfig ()));
}
else
{
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
//OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
LogPrint ("Creating two hops outbound tunnel...");
CreateTunnel<OutboundTunnel> (
new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
i2p::data::netdb.GetRandomNTCPRouter (),
inboundTunnel->GetTunnelConfig ()),
outboundTunnel);
i2p::data::netdb.GetRandomNTCPRouter ()
},
inboundTunnel->GetTunnelConfig ())/*,
outboundTunnel*/);
}
}
}
@ -392,20 +403,43 @@ namespace tunnel @@ -392,20 +403,43 @@ namespace tunnel
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 3)
{
LogPrint ("Creating one hop inbound tunnel...");
CreateTunnel<InboundTunnel> (new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter ()));
CreateTunnel<InboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
i2p::data::netdb.GetRandomNTCPRouter ()
}));
}
else
{
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
LogPrint ("Creating two hops inbound tunnel...");
auto router = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router;
CreateTunnel<InboundTunnel> (
new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),
outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router),
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
i2p::data::netdb.GetRandomNTCPRouter (),
router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomNTCPRouter ()
}),
outboundTunnel);
}
}
}
void Tunnels::ManageTransitTunnels ()
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();)
{
if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
LogPrint ("Transit tunnel ", it->second->GetTunnelID (), " expired");
it = m_TransitTunnels.erase (it);
}
else
it++;
}
}
void Tunnels::PostTunnelData (I2NPMessage * msg)
{
if (msg) m_Queue.Put (msg);
@ -437,7 +471,10 @@ namespace tunnel @@ -437,7 +471,10 @@ namespace tunnel
void Tunnels::CreateZeroHopsInboundTunnel ()
{
CreateTunnel<InboundTunnel> (
new TunnelConfig (&i2p::context.GetRouterInfo ()));
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
&i2p::context.GetRouterInfo ()
}));
}
OutboundTunnel * Tunnels::CreateOneHopOutboundTestTunnel (InboundTunnel * replyTunnel)
@ -451,7 +488,9 @@ namespace tunnel @@ -451,7 +488,9 @@ namespace tunnel
if (peer)
{
const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo ();
return CreateTunnel<InboundTunnel> (new TunnelConfig (&router), outboundTunnel);
return CreateTunnel<InboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>{&router}),
outboundTunnel);
}
else
LogPrint ("No established peers");
@ -470,7 +509,11 @@ namespace tunnel @@ -470,7 +509,11 @@ namespace tunnel
{
const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo ();
return CreateTunnel<InboundTunnel> (
new TunnelConfig (&router, &i2p::context.GetRouterInfo ()),
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
&router,
&i2p::context.GetRouterInfo ()
}),
outboundTunnel);
}
else

12
Tunnel.h

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

10
TunnelBase.h

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
#define TUNNEL_BASE_H__
#include <inttypes.h>
#include "Timestamp.h"
#include "I2NPProtocol.h"
namespace i2p
@ -30,11 +31,20 @@ namespace tunnel @@ -30,11 +31,20 @@ namespace tunnel
{
public:
TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
virtual ~TunnelBase () {};
virtual void EncryptTunnelMsg (I2NPMessage * tunnelMsg) = 0;
virtual uint32_t GetNextTunnelID () const = 0;
virtual const i2p::data::IdentHash& GetNextIdentHash () const = 0;
virtual uint32_t GetTunnelID () const = 0; // as known at our side
uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t t) { m_CreationTime = t; };
private:
uint32_t m_CreationTime; // seconds since epoch
};
}
}

31
TunnelConfig.h

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
#include <inttypes.h>
#include <sstream>
#include <vector>
#include "RouterInfo.h"
#include "RouterContext.h"
@ -83,32 +84,28 @@ namespace tunnel @@ -83,32 +84,28 @@ namespace tunnel
{
public:
TunnelConfig (const i2p::data::RouterInfo * peer, TunnelConfig * replyTunnelConfig = 0) // one hop
{
m_FirstHop = new TunnelHopConfig (peer);
m_LastHop = m_FirstHop;
if (replyTunnelConfig) // outbound
TunnelConfig (std::vector<const i2p::data::RouterInfo *> peers,
TunnelConfig * replyTunnelConfig = 0) // replyTunnelConfig=0 means inbound
{
m_FirstHop->isGateway = false;
m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ());
}
TunnelHopConfig * prev = nullptr;
for (auto it: peers)
{
auto hop = new TunnelHopConfig (it);
if (prev)
prev->SetNext (hop);
else
m_FirstHop->SetNextRouter (&i2p::context.GetRouterInfo ());
m_FirstHop = hop;
prev = hop;
}
m_LastHop = prev;
TunnelConfig (const i2p::data::RouterInfo * peer1, const i2p::data::RouterInfo * peer2, TunnelConfig * replyTunnelConfig = 0) // two hops
{
m_FirstHop = new TunnelHopConfig (peer1);
m_LastHop = new TunnelHopConfig (peer2);
m_FirstHop->SetNext (m_LastHop);
if (replyTunnelConfig)
if (replyTunnelConfig) // outbound
{
m_FirstHop->isGateway = false;
m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ());
}
else
else // inbound
m_LastHop->SetNextRouter (&i2p::context.GetRouterInfo ());
}

2
TunnelEndpoint.cpp

@ -147,7 +147,7 @@ namespace tunnel @@ -147,7 +147,7 @@ namespace tunnel
switch (msg.deliveryType)
{
case eDeliveryTypeLocal:
i2p::HandleI2NPMessage (msg.data);
i2p::HandleI2NPMessage (msg.data, true);
break;
case eDeliveryTypeTunnel:
i2p::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));

260
TunnelGateway.cpp

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

23
TunnelGateway.h

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

Loading…
Cancel
Save