Merge pull request #1 from orignal/master

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -4,14 +4,18 @@
#include "Log.h"
#include "RouterInfo.h"
#include "RouterContext.h"
#include "Tunnel.h"
#include "Timestamp.h"
#include "CryptoConst.h"
#include "Garlic.h"
#include "Streaming.h"
namespace i2p
{
namespace stream
{
Stream::Stream (const i2p::data::IdentHash& destination):
m_SendStreamID (0)
Stream::Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote):
m_SendStreamID (0), m_SequenceNumber (0), m_LocalDestination (local), m_RemoteLeaseSet (remote)
{
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
}
@ -56,9 +60,68 @@ namespace stream
std::string str((const char *)buf, end-buf);
LogPrint ("Payload: ", str);
}
size_t Stream::Send (uint8_t * buf, size_t len, int timeout)
{
uint8_t packet[STREAMING_MTU];
size_t size = 0;
*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID);
size += 4; // sendStreamID
*(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID);
size += 4; // receiveStreamID
*(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber);
size += 4; // sequenceNum
*(uint32_t *)(packet + size) = 0; // TODO
size += 4; // ack Through
packet[size] = 0;
size++; // NACK count
size++; // resend delay
// TODO: for initial packet only, following packets have different falgs
*(uint16_t *)(packet + size) = htobe16 (PACKET_FLAG_SYNCHRONIZE |
PACKET_FLAG_FROM_INCLUDED | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_NO_ACK);
size += 2; // flags
*(uint16_t *)(packet + size) = htobe16 (sizeof (i2p::data::Identity) + 40); // identity + signature
size += 2; // options size
memcpy (packet + size, &m_LocalDestination->GetIdentity (), sizeof (i2p::data::Identity));
size += sizeof (i2p::data::Identity); // from
uint8_t * signature = packet + size; // set it later
memset (signature, 0, 40); // zeroes for now
size += 40; // signature
memcpy (packet + size, buf, len);
size += len; // payload
m_LocalDestination->Sign (packet, size, signature);
I2NPMessage * msg = i2p::garlic::routing.WrapSingleMessage (m_RemoteLeaseSet,
CreateDataMessage (this, packet, size), m_LocalDestination->GetLeaseSet ());
auto outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
if (outbound)
{
auto& lease = m_RemoteLeaseSet->GetLeases ()[0]; // TODO:
outbound->SendTunnelDataMsg (lease.tunnelGateway, lease.tunnelID, msg);
}
else
DeleteI2NPMessage (msg);
return len;
}
StreamingDestination * sharedLocalDestination = nullptr;
StreamingDestination::StreamingDestination (): m_LeaseSet (nullptr)
{
// TODO: read from file later
m_Keys = i2p::data::CreateRandomKeys ();
m_Identity = m_Keys;
m_IdentHash = i2p::data::CalculateIdentHash (m_Identity);
m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
}
StreamingDestination::~StreamingDestination ()
{
if (m_LeaseSet)
DeleteI2NPMessage (m_LeaseSet);
}
StreamingDestination m_SharedLocalDestination;
void StreamingDestination::HandleNextPacket (const uint8_t * buf, size_t len)
{
uint32_t sendStreamID = *(uint32_t *)(buf);
@ -69,25 +132,93 @@ namespace stream
LogPrint ("Unknown stream ", sendStreamID);
}
Stream * StreamingDestination::CreateNewStream (const i2p::data::IdentHash& destination)
Stream * StreamingDestination::CreateNewStream (const i2p::data::LeaseSet * remote)
{
/*i2p::data::LeaseSet * leaseSet = i2p::data::netdb.FindLeaseSet (destination);
if (!leaseSet)
{
i2p::data::netdb.RequestDestination (destination);
sleep (5); // wait for 5 seconds
leaseSet = i2p::data::netdb.FindLeaseSet (destination);
if (!leaseSet)
{
LogPrint ("Couldn't find LeaseSet");
return nullptr;
}
} */
Stream * s = new Stream (destination);
Stream * s = new Stream (this, remote);
m_Streams[s->GetRecvStreamID ()] = s;
return s;
}
void StreamingDestination::DeleteStream (Stream * stream)
{
if (stream)
{
m_Streams.erase (stream->GetRecvStreamID ());
delete stream;
}
}
I2NPMessage * StreamingDestination::GetLeaseSet ()
{
if (!m_LeaseSet)
m_LeaseSet = CreateLeaseSet ();
else
FillI2NPMessageHeader (m_LeaseSet, eI2NPDatabaseStore); // refresh msgID
return m_LeaseSet;
}
I2NPMessage * StreamingDestination::CreateLeaseSet () const
{
I2NPMessage * m = NewI2NPMessage ();
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)m->GetPayload ();
memcpy (msg->key, (const uint8_t *)m_IdentHash, 32);
msg->type = 1; // LeaseSet
msg->replyToken = 0;
uint8_t * buf = m->GetPayload () + sizeof (I2NPDatabaseStoreMsg);
size_t size = 0;
memcpy (buf + size, &m_Identity, sizeof (m_Identity));
size += sizeof (m_Identity); // destination
memcpy (buf + size, i2p::context.GetLeaseSetPublicKey (), 256);
size += 256; // encryption key
memset (buf + size, 0, 128);
size += 128; // signing key
auto tunnel = i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (tunnel)
{
buf[size] = 1; // 1 lease
size++; // num
memcpy (buf + size, (const uint8_t *)tunnel->GetNextIdentHash (), 32);
size += 32; // tunnel_gw
*(uint32_t *)(buf + size) = htobe32 (tunnel->GetNextTunnelID ());
size += 4; // tunnel_id
uint64_t ts = tunnel->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT;
ts *= 1000; // in milliseconds
*(uint64_t *)(buf + size) = htobe64 (ts);
size += 8; // end_date
}
else
{
buf[size] = 0; // zero leases
size++; // num
}
Sign (buf, size, buf+ size);
size += 40; // signature
m->len += size + sizeof (I2NPDatabaseStoreMsg);
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
return m;
}
void StreamingDestination::Sign (uint8_t * buf, int len, uint8_t * signature) const
{
CryptoPP::DSA::Signer signer (m_SigningPrivateKey);
signer.SignMessage (i2p::context.GetRandomNumberGenerator (), buf, len, signature);
}
Stream * CreateStream (const i2p::data::LeaseSet * remote)
{
if (!sharedLocalDestination)
sharedLocalDestination = new StreamingDestination ();
return sharedLocalDestination->CreateNewStream (remote);
}
void CloseStream (Stream * stream)
{
if (sharedLocalDestination)
sharedLocalDestination->DeleteStream (stream);
}
void HandleDataMessage (i2p::data::IdentHash * destination, const uint8_t * buf, size_t len)
{
uint32_t length = be32toh (*(uint32_t *)buf);
@ -104,7 +235,8 @@ namespace stream
decompressor.Get (uncompressed, uncompressedSize);
// then forward to streaming engine
// TODO: we have onle one destination, might be more
m_SharedLocalDestination.HandleNextPacket (uncompressed, uncompressedSize);
if (sharedLocalDestination)
sharedLocalDestination->HandleNextPacket (uncompressed, uncompressedSize);
}
else
LogPrint ("Data: protocol ", buf[9], " is not supported");
@ -118,7 +250,7 @@ namespace stream
compressor.MessageEnd();
int size = compressor.MaxRetrievable ();
uint8_t * buf = msg->GetPayload ();
*(uint16_t *)buf = htobe32 (size); // length
*(uint32_t *)buf = htobe32 (size); // length
buf += 4;
compressor.Get (buf, size);
buf[9] = 6; // streaming protocol

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

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

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save