pull/40/head
Meeh 10 years ago
commit d071c50c4d

@ -12,25 +12,47 @@ namespace i2p
{
namespace crypto
{
inline void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, int len,
uint8_t * encrypted, bool zeroPadding = false) // 514 with padding and 512 without
class ElGamalEncryption
{
CryptoPP::AutoSeededRandomPool rnd;
CryptoPP::Integer y(key, 256), k(rnd, CryptoPP::Integer::One(), elgp-1);
if (zeroPadding)
{
encrypted[0] = 0;
encrypted[257] = 0;
}
a_exp_b_mod_c (elgg, k, elgp).Encode (zeroPadding ? encrypted + 1 : encrypted, 256);
uint8_t m[255];
m[0] = 0xFF;
memcpy (m+33, data, len);
CryptoPP::SHA256().CalculateDigest(m+1, m+33, 222);
a_times_b_mod_c (a_exp_b_mod_c (y, k, elgp),
CryptoPP::Integer (m, 255), elgp).Encode (zeroPadding ? encrypted + 258 : encrypted + 256, 256);
}
public:
ElGamalEncryption (const uint8_t * key):
y (key, 256), k (rnd, CryptoPP::Integer::One(), elgp-1),
a (a_exp_b_mod_c (elgg, k, elgp)), b1 (a_exp_b_mod_c (y, k, elgp))
{
}
void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false)
{
// calculate b = b1*m mod p
uint8_t m[255];
m[0] = 0xFF;
memcpy (m+33, data, len);
CryptoPP::SHA256().CalculateDigest(m+1, m+33, 222);
CryptoPP::Integer b (a_times_b_mod_c (b1, CryptoPP::Integer (m, 255), elgp));
// copy a and b
if (zeroPadding)
{
encrypted[0] = 0;
a.Encode (encrypted + 1, 256);
encrypted[257] = 0;
b.Encode (encrypted + 258, 256);
}
else
{
a.Encode (encrypted, 256);
b.Encode (encrypted + 256, 256);
}
}
private:
CryptoPP::AutoSeededRandomPool rnd;
CryptoPP::Integer y, k, a, b1;
bool m_ZeroPadding;
};
inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
uint8_t * data, bool zeroPadding = false)

@ -2,7 +2,6 @@
#include "I2PEndian.h"
#include <map>
#include <string>
#include "ElGamal.h"
#include "RouterContext.h"
#include "I2NPProtocol.h"
#include "Tunnel.h"
@ -14,7 +13,7 @@ namespace i2p
{
namespace garlic
{
GarlicRoutingSession::GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags):
GarlicRoutingSession::GarlicRoutingSession (const i2p::data::RoutingDestination& destination, int numTags):
m_Destination (destination), m_FirstMsgID (0), m_IsAcknowledged (false),
m_NumTags (numTags), m_NextTag (-1), m_SessionTags (0)
{
@ -56,7 +55,7 @@ namespace garlic
m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV
uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
i2p::crypto::ElGamalEncrypt (m_Destination->GetEncryptionPublicKey (), (uint8_t *)&elGamal, sizeof(elGamal), buf, true);
m_Destination.GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv);
buf += 514;
len += 514;
@ -140,7 +139,7 @@ namespace garlic
}
if (msg) // clove message ifself if presented
{
size += CreateGarlicClove (payload + size, msg, m_Destination->IsDestination ());
size += CreateGarlicClove (payload + size, msg, m_Destination.IsDestination ());
(*numCloves)++;
}
@ -161,7 +160,7 @@ namespace garlic
{
buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination
size++;
memcpy (buf + size, m_Destination->GetIdentHash (), 32);
memcpy (buf + size, m_Destination.GetIdentHash (), 32);
size += 32;
}
else
@ -230,33 +229,31 @@ 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)
{
if (!destination) return nullptr;
auto it = m_Sessions.find (destination->GetIdentHash ());
auto it = m_Sessions.find (destination.GetIdentHash ());
if (it != m_Sessions.end ())
{
delete it->second;
m_Sessions.erase (it);
}
GarlicRoutingSession * session = new GarlicRoutingSession (destination, 0); // not follow-on messages expected
m_Sessions[destination->GetIdentHash ()] = session;
m_Sessions[destination.GetIdentHash ()] = session;
return session->WrapSingleMessage (msg, nullptr);
}
I2NPMessage * GarlicRouting::WrapMessage (const i2p::data::RoutingDestination * destination,
I2NPMessage * GarlicRouting::WrapMessage (const i2p::data::RoutingDestination& destination,
I2NPMessage * msg, I2NPMessage * leaseSet)
{
if (!destination) return nullptr;
auto it = m_Sessions.find (destination->GetIdentHash ());
auto it = m_Sessions.find (destination.GetIdentHash ());
GarlicRoutingSession * session = nullptr;
if (it != m_Sessions.end ())
session = it->second;
if (!session)
{
session = new GarlicRoutingSession (destination, 4); // TODO: change it later
m_Sessions[destination->GetIdentHash ()] = session;
m_Sessions[destination.GetIdentHash ()] = session;
}
I2NPMessage * ret = session->WrapSingleMessage (msg, leaseSet);

@ -37,7 +37,7 @@ namespace garlic
{
public:
GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags);
GarlicRoutingSession (const i2p::data::RoutingDestination& destination, int numTags);
~GarlicRoutingSession ();
I2NPMessage * WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet);
int GetNextTag () const { return m_NextTag; };
@ -57,7 +57,7 @@ namespace garlic
private:
const i2p::data::RoutingDestination * m_Destination;
const i2p::data::RoutingDestination& m_Destination;
uint8_t m_SessionKey[32];
uint32_t m_FirstMsgID; // first message ID
bool m_IsAcknowledged;
@ -78,8 +78,8 @@ namespace garlic
void HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel);
void HandleDeliveryStatusMessage (uint8_t * buf, size_t len);
I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * msg);
I2NPMessage * WrapMessage (const i2p::data::RoutingDestination * destination,
I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg);
I2NPMessage * WrapMessage (const i2p::data::RoutingDestination& destination,
I2NPMessage * msg, I2NPMessage * leaseSet = nullptr);
private:

@ -60,9 +60,20 @@ namespace util
{
m_Buffer[bytes_transferred] = 0;
auto address = ExtractAddress ();
LogPrint (address);
if (address.length () > 1) // not just '/'
HandleDestinationRequest (address.substr (1)); // exclude '/'
{
std::string uri ("/"), b32;
size_t pos = address.find ('/', 1);
if (pos == std::string::npos)
b32 = address.substr (1); // excluding leading '/' to end of line
else
{
b32 = address.substr (1, pos - 1); // excluding leading '/' to next '/'
uri = address.substr (pos); // rest of line
}
HandleDestinationRequest (b32, uri);
}
else
HandleRequest ();
boost::asio::async_write (*m_Socket, m_Reply.to_buffers(),
@ -149,14 +160,18 @@ namespace util
s << "<p><a href=\"zmw2cyw2vj7f6obx3msmdvdepdhnw2ctc4okza2zjxlukkdfckhq\">Flibusta</a></p>";
}
void HTTPConnection::HandleDestinationRequest (std::string b32)
void HTTPConnection::HandleDestinationRequest (const std::string& b32, const std::string& uri)
{
uint8_t destination[32];
i2p::data::Base32ToByteStream (b32.c_str (), b32.length (), destination, 32);
if (i2p::data::Base32ToByteStream (b32.c_str (), b32.length (), destination, 32) != 32)
{
LogPrint ("Invalid Base32 address ", b32);
return;
}
auto leaseSet = i2p::data::netdb.FindLeaseSet (destination);
if (!leaseSet || !leaseSet->HasNonExpiredLeases ())
{
i2p::data::netdb.RequestDestination (i2p::data::IdentHash (destination), true);
i2p::data::netdb.Subscribe(destination);
std::this_thread::sleep_for (std::chrono::seconds(10)); // wait for 10 seconds
leaseSet = i2p::data::netdb.FindLeaseSet (destination);
if (!leaseSet || !leaseSet->HasNonExpiredLeases ()) // still no LeaseSet
@ -170,17 +185,10 @@ namespace util
return;
}
}
// we found LeaseSet
if (leaseSet->HasExpiredLeases ())
{
// we should re-request LeaseSet
LogPrint ("LeaseSet re-requested");
i2p::data::netdb.RequestDestination (i2p::data::IdentHash (destination), true);
}
auto s = i2p::stream::CreateStream (leaseSet);
auto s = i2p::stream::CreateStream (*leaseSet);
if (s)
{
std::string request = "GET / HTTP/1.1\n Host:" + b32 + ".b32.i2p\n";
std::string request = "GET " + uri + " HTTP/1.1\n Host:" + b32 + ".b32.i2p\n";
s->Send ((uint8_t *)request.c_str (), request.length (), 10);
std::stringstream ss;
uint8_t buf[8192];
@ -200,7 +208,7 @@ namespace util
else // nothing received
ss << "<html>Not responding</html>";
s->Close ();
//DeleteStream (s);
DeleteStream (s);
m_Reply.content = ss.str ();
m_Reply.headers.resize(2);

@ -48,7 +48,7 @@ namespace util
void HandleWrite(const boost::system::error_code& ecode);
void HandleRequest ();
void HandleDestinationRequest (std::string b32);
void HandleDestinationRequest (const std::string& b32, const std::string& uri);
void FillContent (std::stringstream& s);
std::string ExtractAddress ();

@ -212,7 +212,7 @@ namespace i2p
const I2NPBuildRequestRecordClearText& clearText,
I2NPBuildRequestRecordElGamalEncrypted& record)
{
i2p::crypto::ElGamalEncrypt (router.GetRouterIdentity ().publicKey, (uint8_t *)&clearText, sizeof(clearText), record.encrypted);
router.GetElGamalEncryption ()->Encrypt ((uint8_t *)&clearText, sizeof(clearText), record.encrypted);
memcpy (record.toPeer, (const uint8_t *)router.GetIdentHash (), 16);
}
@ -392,7 +392,7 @@ namespace i2p
LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID, ". Msg type ", (int)msg->GetHeader()->typeID);
i2p::tunnel::TransitTunnel * tunnel = i2p::tunnel::tunnels.GetTransitTunnel (tunnelID);
if (tunnel)
tunnel->SendTunnelDataMsg (nullptr, 0, msg);
tunnel->SendTunnelDataMsg (msg);
else
{
LogPrint ("Tunnel ", (unsigned int)tunnelID, " not found");

@ -3,6 +3,7 @@
#include <inttypes.h>
#include <string.h>
#include "ElGamal.h"
namespace i2p
{
@ -84,9 +85,24 @@ namespace data
class RoutingDestination
{
public:
RoutingDestination (): m_ElGamalEncryption (nullptr) {};
virtual ~RoutingDestination () { delete m_ElGamalEncryption; };
virtual const IdentHash& GetIdentHash () const = 0;
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
virtual bool IsDestination () const = 0; // for garlic
i2p::crypto::ElGamalEncryption * GetElGamalEncryption () const
{
if (!m_ElGamalEncryption)
m_ElGamalEncryption = new i2p::crypto::ElGamalEncryption (GetEncryptionPublicKey ());
return m_ElGamalEncryption;
}
private:
mutable i2p::crypto::ElGamalEncryption * m_ElGamalEncryption; // use lazy initialization
};
}
}

@ -60,7 +60,7 @@ namespace ntcp
#pragma pack()
const int TERMINATION_TIMEOUT = 60; // 1 minute
const int TERMINATION_TIMEOUT = 120; // 2 minutes
class NTCPSession
{
public:

@ -25,7 +25,7 @@ namespace data
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);
msg = i2p::garlic::routing.WrapSingleMessage (*router, msg);
m_ExcludedPeers.insert (router->GetIdentHash ());
m_LastRouter = router;
m_LastReplyTunnel = replyTunnel;
@ -90,7 +90,7 @@ namespace data
void NetDb::Run ()
{
uint32_t lastTs = 0;
uint32_t lastSave = 0, lastPublish = 0;
m_IsRunning = true;
while (m_IsRunning)
{
@ -120,11 +120,19 @@ namespace data
Explore ();
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts - lastTs >= 60) // save routers every minute
if (ts - lastSave >= 60) // save routers and validate subscriptions every minute
{
if (lastTs)
if (lastSave)
{
SaveUpdated (m_NetDbPath);
lastTs = ts;
ValidateSubscriptions ();
}
lastSave = ts;
}
if (ts - lastPublish >= 600) // publish every 10 minutes
{
Publish ();
lastPublish = ts;
}
}
catch (std::exception& ex)
@ -486,7 +494,10 @@ namespace data
else // we should send directly
{
if (!dest->IsLeaseSet ()) // if not LeaseSet
i2p::transports.SendMessage (router, dest->CreateRequestMessage (router));
{
if (!dest->IsExcluded (router) && dest->GetNumExcludedPeers () < 30)
i2p::transports.SendMessage (router, dest->CreateRequestMessage (router));
}
else
LogPrint ("Can't request LeaseSet");
}
@ -543,6 +554,17 @@ namespace data
}
}
void NetDb::Publish ()
{
std::set<IdentHash> excluded; // TODO: fill up later
auto floodfill = GetClosestFloodfill (i2p::context.GetRouterInfo ().GetIdentHash (), excluded);
if (floodfill)
{
LogPrint ("Publishing our RouterInfo to ", floodfill->GetIdentHashAbbreviation ());
transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg ());
}
}
RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest,
bool isLeaseSet, bool isExploratory)
{
@ -644,5 +666,33 @@ namespace data
return r;
}
void NetDb::Subscribe (const IdentHash& ident)
{
LeaseSet * leaseSet = FindLeaseSet (ident);
if (!leaseSet)
{
LogPrint ("LeaseSet requested");
RequestDestination (ident, true);
}
m_Subscriptions.insert (ident);
}
void NetDb::Unsubscribe (const IdentHash& ident)
{
m_Subscriptions.erase (ident);
}
void NetDb::ValidateSubscriptions ()
{
for (auto it : m_Subscriptions)
{
LeaseSet * leaseSet = FindLeaseSet (it);
if (!leaseSet || leaseSet->HasExpiredLeases ())
{
LogPrint ("LeaseSet re-requested");
RequestDestination (it, true);
}
}
}
}
}

@ -63,7 +63,9 @@ namespace data
void AddLeaseSet (uint8_t * buf, int len);
RouterInfo * FindRouter (const IdentHash& ident) const;
LeaseSet * FindLeaseSet (const IdentHash& destination) const;
void Subscribe (const IdentHash& ident); // keep LeaseSets upto date
void Unsubscribe (const IdentHash& ident);
void RequestDestination (const char * b32); // in base32
void RequestDestination (const IdentHash& destination, bool isLeaseSet = false);
@ -82,6 +84,8 @@ namespace data
void SaveUpdated (const char * directory);
void Run (); // exploratory thread
void Explore ();
void Publish ();
void ValidateSubscriptions ();
const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
RequestedDestination * CreateRequestedDestination (const IdentHash& dest,
@ -94,6 +98,7 @@ namespace data
std::map<IdentHash, LeaseSet *> m_LeaseSets;
std::map<IdentHash, RouterInfo *> m_RouterInfos;
std::map<IdentHash, RequestedDestination *> m_RequestedDestinations;
std::set<IdentHash> m_Subscriptions;
bool m_IsRunning;
int m_ReseedRetries;

@ -129,6 +129,27 @@ namespace data
address.port = boost::lexical_cast<int>(value);
else if (!strcmp (key, "key"))
Base64ToByteStream (value, strlen (value), address.key, 32);
else if (key[0] == 'i')
{
// introducers
size_t l = strlen(key);
unsigned char index = key[l-1]; // TODO:
key[l-1] = 0;
if (index >= address.introducers.size ())
address.introducers.resize (index + 1);
Introducer& introducer = address.introducers.at (index);
if (!strcmp (key, "ihost"))
{
boost::system::error_code ecode;
introducer.iHost = boost::asio::ip::address::from_string (value, ecode);
}
else if (!strcmp (key, "iport"))
introducer.iPort = boost::lexical_cast<int>(value);
else if (!strcmp (key, "itag"))
introducer.iTag = boost::lexical_cast<uint32_t>(value);
else if (!strcmp (key, "ikey"))
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
}
}
m_Addresses.push_back(address);
}
@ -302,6 +323,13 @@ namespace data
else
return m_SupportedTransports & (eSSUV4 | eSSUV6);
}
bool RouterInfo::UsesIntroducer () const
{
if (!IsSSU ()) return false;
auto address = GetSSUAddress (true); // no introducers for v6
return address && !address->introducers.empty ();
}
const RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) const
{

@ -32,6 +32,14 @@ namespace data
eTransportSSU
};
struct Introducer
{
boost::asio::ip::address iHost;
int iPort;
uint8_t iKey[32];
uint32_t iTag;
};
struct Address
{
TransportStyle transportStyle;
@ -39,7 +47,9 @@ namespace data
int port;
uint64_t date;
uint8_t cost;
uint8_t key[32]; // into key for SSU
// SSU only
uint8_t key[32]; // intro key for SSU
std::vector<Introducer> introducers;
};
RouterInfo (const char * filename);
@ -65,6 +75,7 @@ namespace data
bool IsNTCP (bool v4only = true) const;
bool IsSSU (bool v4only = true) const;
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool UsesIntroducer () const;
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };
bool IsUnreachable () const { return m_IsUnreachable; };

@ -65,6 +65,15 @@ namespace ssu
// session confirmed
ProcessSessionConfirmed (buf, len);
break;
case eSessionRelayRequestSent:
// relay response
ProcessRelayResponse (buf,len);
break;
case eSessionRelayRequestReceived:
// HolePunch
m_State = eSessionStateUnknown;
Connect ();
break;
default:
LogPrint ("SSU state not implemented yet");
}
@ -91,8 +100,12 @@ namespace ssu
LogPrint ("SSU session destroy received");
if (m_Server)
m_Server->DeleteSession (this); // delete this
}
break;
break;
}
case PAYLOAD_TYPE_RELAY_INTRO:
LogPrint ("SSU relay intro received");
// TODO:
break;
default:
LogPrint ("Unexpected SSU payload type ", (int)payloadType);
}
@ -197,6 +210,37 @@ namespace ssu
m_Server->Send (buf, 304, m_RemoteEndpoint);
}
void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer)
{
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (!address)
{
LogPrint ("SSU is not supported");
return;
}
uint8_t buf[96 + 18];
uint8_t * payload = buf + sizeof (SSUHeader);
*(uint32_t *)payload = htobe32 (introducer.iTag);
payload += 4;
*payload = 0; // no address
payload++;
*(uint16_t *)payload = 0; // port = 0
payload += 2;
*payload = 0; // challenge
payload++;
memcpy (payload, address->key, 32);
payload += 32;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
*(uint32_t *)payload = htobe32 (rnd.GenerateWord32 ()); // nonce
uint8_t iv[16];
rnd.GenerateBlock (iv, 16); // random iv
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, introducer.iKey, iv, introducer.iKey);
m_State = eSessionRelayRequestSent;
m_Server->Send (buf, 96, m_RemoteEndpoint);
}
void SSUSession::SendSessionCreated (const uint8_t * x)
{
auto introKey = GetIntroKey ();
@ -282,6 +326,46 @@ namespace ssu
m_Server->Send (buf, 480, m_RemoteEndpoint);
}
void SSUSession::ProcessRelayResponse (uint8_t * buf, size_t len)
{
LogPrint ("Process relay response");
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (!address)
{
LogPrint ("SSU is not supported");
return;
}
if (Validate (buf, len, address->key))
{
Decrypt (buf, len, address->key);
SSUHeader * header = (SSUHeader *)buf;
if ((header->flag >> 4) == PAYLOAD_TYPE_RELAY_RESPONSE)
{
LogPrint ("Relay response received");
m_State = eSessionRelayRequestReceived;
uint8_t * payload = buf + sizeof (SSUHeader);
payload++;
boost::asio::ip::address_v4 remoteIP (be32toh (*(uint32_t* )(payload)));
payload += 4;
uint16_t remotePort = be16toh (*(uint16_t *)(payload));
payload += 2;
boost::asio::ip::udp::endpoint newRemoteEndpoint(remoteIP, remotePort);
m_Server->ReassignSession (m_RemoteEndpoint, newRemoteEndpoint);
m_RemoteEndpoint = newRemoteEndpoint;
payload++;
boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )(payload)));
payload += 4;
uint16_t ourPort = be16toh (*(uint16_t *)(payload));
payload += 2;
LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP.to_string ().c_str ());
}
else
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
}
}
bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len)
{
auto introKey = GetIntroKey ();
@ -301,7 +385,7 @@ namespace ssu
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
}
else
LogPrint ("MAC verifcation failed");
LogPrint ("MAC verification failed");
}
else
LogPrint ("SSU is not supported");
@ -368,6 +452,11 @@ namespace ssu
SendSessionRequest ();
}
void SSUSession::ConnectThroughIntroducer (const i2p::data::RouterInfo::Introducer& introducer)
{
SendRelayRequest (introducer);
}
void SSUSession::Close ()
{
SendSesionDestroyed ();
@ -677,12 +766,27 @@ namespace ssu
session = it->second;
else
{
// otherwise create new session
session = new SSUSession (this, remoteEndpoint, router);
m_Sessions[remoteEndpoint] = session;
LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port (), " created");
session->Connect ();
// otherwise create new session
if (!router->UsesIntroducer ())
{
// connect directly
session = new SSUSession (this, remoteEndpoint, router);
m_Sessions[remoteEndpoint] = session;
LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port (), " created");
session->Connect ();
}
else
{
// connect to introducer
auto& introducer = address->introducers[0]; // TODO:
boost::asio::ip::udp::endpoint introducerEndpoint (introducer.iHost, introducer.iPort);
session = new SSUSession (this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = session;
LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (),
"] created through introducer ", introducerEndpoint.address ().to_string (), ":", introducerEndpoint.port ());
session->ConnectThroughIntroducer (introducer);
}
}
}
else
@ -709,6 +813,18 @@ namespace ssu
delete it.second;
}
m_Sessions.clear ();
}
void SSUServer::ReassignSession (const boost::asio::ip::udp::endpoint& oldEndpoint, const boost::asio::ip::udp::endpoint& newEndpoint)
{
auto it = m_Sessions.find (oldEndpoint);
if (it != m_Sessions.end ())
{
m_Sessions.erase (it);
m_Sessions[newEndpoint] = it->second;
LogPrint ("SSU session ressigned from ", oldEndpoint.address ().to_string (), ":", oldEndpoint.port (),
" to ", newEndpoint.address ().to_string (), ":", newEndpoint.port ());
}
}
}
}

10
SSU.h

@ -55,6 +55,8 @@ namespace ssu
eSessionStateCreatedReceived,
eSessionStateConfirmedSent,
eSessionStateConfirmedReceived,
eSessionRelayRequestSent,
eSessionRelayRequestReceived,
eSessionStateEstablished
};
@ -68,6 +70,7 @@ namespace ssu
void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void Connect ();
void ConnectThroughIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
void Close ();
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
void SendI2NPMessage (I2NPMessage * msg);
@ -79,10 +82,12 @@ namespace ssu
void ProcessMessage (uint8_t * buf, size_t len); // call for established session
void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendSessionRequest ();
void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer);
void ProcessSessionCreated (uint8_t * buf, size_t len);
void SendSessionCreated (const uint8_t * x);
void ProcessSessionConfirmed (uint8_t * buf, size_t len);
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag);
void ProcessRelayResponse (uint8_t * buf, size_t len);
void Established ();
void ProcessData (uint8_t * buf, size_t len);
void SendMsgAck (uint32_t msgID);
@ -118,10 +123,11 @@ namespace ssu
void Stop ();
SSUSession * GetSession (const i2p::data::RouterInfo * router);
void DeleteSession (SSUSession * session);
void DeleteAllSessions ();
void DeleteAllSessions ();
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; };
void Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
void ReassignSession (const boost::asio::ip::udp::endpoint& oldEndpoint, const boost::asio::ip::udp::endpoint& newEndpoint);
private:

@ -14,7 +14,7 @@ namespace i2p
{
namespace stream
{
Stream::Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote):
Stream::Stream (StreamingDestination * local, const i2p::data::LeaseSet& remote):
m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (0), m_IsOpen (false),
m_LocalDestination (local), m_RemoteLeaseSet (remote), m_OutboundTunnel (nullptr)
{
@ -172,7 +172,7 @@ namespace stream
if (!m_OutboundTunnel)
m_OutboundTunnel = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases ();
auto leases = m_RemoteLeaseSet.GetNonExpiredLeases ();
if (m_OutboundTunnel && !leases.empty ())
{
auto& lease = *leases.begin (); // TODO:
@ -206,7 +206,7 @@ namespace stream
CreateDataMessage (this, packet, size));
if (m_OutboundTunnel)
{
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases ();
auto leases = m_RemoteLeaseSet.GetNonExpiredLeases ();
if (!leases.empty ())
{
auto& lease = *leases.begin (); // TODO:
@ -252,7 +252,7 @@ namespace stream
I2NPMessage * msg = i2p::garlic::routing.WrapSingleMessage (m_RemoteLeaseSet,
CreateDataMessage (this, packet, size));
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases ();
auto leases = m_RemoteLeaseSet.GetNonExpiredLeases ();
if (m_OutboundTunnel && !leases.empty ())
{
auto& lease = *leases.begin (); // TODO:
@ -318,7 +318,7 @@ namespace stream
void StreamingDestination::HandleNextPacket (Packet * packet)
{
uint32_t sendStreamID = be32toh (*(uint32_t *)(packet->buf));
uint32_t sendStreamID = packet->GetSendStreamID ();
auto it = m_Streams.find (sendStreamID);
if (it != m_Streams.end ())
it->second->HandleNextPacket (packet);
@ -329,7 +329,7 @@ namespace stream
}
}
Stream * StreamingDestination::CreateNewStream (const i2p::data::LeaseSet * remote)
Stream * StreamingDestination::CreateNewStream (const i2p::data::LeaseSet& remote)
{
Stream * s = new Stream (this, remote);
m_Streams[s->GetRecvStreamID ()] = s;
@ -399,7 +399,7 @@ namespace stream
signer.SignMessage (i2p::context.GetRandomNumberGenerator (), buf, len, signature);
}
Stream * CreateStream (const i2p::data::LeaseSet * remote)
Stream * CreateStream (const i2p::data::LeaseSet& remote)
{
if (!sharedLocalDestination)
sharedLocalDestination = new StreamingDestination ();

@ -65,11 +65,11 @@ namespace stream
{
public:
Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote);
Stream (StreamingDestination * local, const i2p::data::LeaseSet& remote);
~Stream ();
uint32_t GetSendStreamID () const { return m_SendStreamID; };
uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
const i2p::data::LeaseSet * GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
const i2p::data::LeaseSet& GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
bool IsOpen () const { return m_IsOpen; };
bool IsEstablished () const { return m_SendStreamID; };
@ -91,7 +91,7 @@ namespace stream
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber, m_LastReceivedSequenceNumber;
bool m_IsOpen;
StreamingDestination * m_LocalDestination;
const i2p::data::LeaseSet * m_RemoteLeaseSet;
const i2p::data::LeaseSet& m_RemoteLeaseSet;
i2p::util::Queue<Packet> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets;
i2p::tunnel::OutboundTunnel * m_OutboundTunnel;
@ -109,7 +109,7 @@ namespace stream
I2NPMessage * GetLeaseSet ();
void Sign (uint8_t * buf, int len, uint8_t * signature) const;
Stream * CreateNewStream (const i2p::data::LeaseSet * remote);
Stream * CreateNewStream (const i2p::data::LeaseSet& remote);
void DeleteStream (Stream * stream);
void HandleNextPacket (Packet * packet);
@ -129,7 +129,7 @@ namespace stream
CryptoPP::DSA::PrivateKey m_SigningPrivateKey;
};
Stream * CreateStream (const i2p::data::LeaseSet * remote);
Stream * CreateStream (const i2p::data::LeaseSet& remote);
void DeleteStream (Stream * stream);
// assuming data is I2CP message

@ -47,15 +47,18 @@ namespace tunnel
m_NumTransmittedBytes += tunnelMsg->GetLength ();
}
void TransitTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
void TransitTunnel::SendTunnelDataMsg (i2p::I2NPMessage * msg)
{
LogPrint ("We are not a gateway for transit tunnel ", m_TunnelID);
i2p::DeleteI2NPMessage (msg);
}
void TransitTunnelGateway::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
void TransitTunnelGateway::SendTunnelDataMsg (i2p::I2NPMessage * msg)
{
m_Gateway.SendTunnelDataMsg (gwHash, gwTunnel, msg);
TunnelMessageBlock block;
block.deliveryType = eDeliveryTypeLocal;
block.data = msg;
m_Gateway.SendTunnelDataMsg (block);
}
void TransitTunnelEndpoint::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)

@ -22,7 +22,7 @@ namespace tunnel
const uint8_t * layerKey,const uint8_t * ivKey);
virtual void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
virtual void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
virtual void SendTunnelDataMsg (i2p::I2NPMessage * msg);
virtual size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
uint32_t GetTunnelID () const { return m_TunnelID; };
@ -54,7 +54,7 @@ namespace tunnel
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_Gateway(this) {};
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
void SendTunnelDataMsg (i2p::I2NPMessage * msg);
size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); };
private:

@ -64,9 +64,12 @@ namespace i2p
delete session.second;
m_NTCPSessions.clear ();
delete m_NTCPAcceptor;
m_Timer->cancel ();
delete m_Timer;
if (m_Timer)
{
m_Timer->cancel ();
delete m_Timer;
}
if (m_SSUServer)
{

@ -136,28 +136,28 @@ namespace tunnel
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
{
m_Gateway.SendTunnelDataMsg (gwHash, gwTunnel, msg);
TunnelMessageBlock block;
if (gwHash)
{
block.hash = gwHash;
if (gwTunnel)
{
block.deliveryType = eDeliveryTypeTunnel;
block.tunnelID = gwTunnel;
}
else
block.deliveryType = eDeliveryTypeRouter;
}
else
block.deliveryType = eDeliveryTypeLocal;
block.data = msg;
m_Gateway.SendTunnelDataMsg (block);
}
void OutboundTunnel::SendTunnelDataMsg (std::vector<TunnelMessageBlock> msgs)
{
for (auto& it : msgs)
{
switch (it.deliveryType)
{
case eDeliveryTypeLocal:
m_Gateway.SendTunnelDataMsg (nullptr, 0, it.data);
break;
case eDeliveryTypeTunnel:
m_Gateway.SendTunnelDataMsg (it.hash, it.tunnelID, it.data);
break;
case eDeliveryTypeRouter:
m_Gateway.SendTunnelDataMsg (it.hash, 0, it.data);
break;
default:
LogPrint ("Unexpected delivery type ", (int)it.deliveryType);
}
}
m_Gateway.PutTunnelDataMsg (it);
m_Gateway.SendBuffer ();
}

@ -21,7 +21,7 @@ namespace i2p
{
namespace tunnel
{
const int TUNNEL_EXPIRATION_TIMEOUT = 600; // 10 minutes
const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes
class OutboundTunnel;
class InboundTunnel;

@ -10,7 +10,7 @@ namespace i2p
{
namespace tunnel
{
void TunnelGatewayBuffer::PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg)
void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block)
{
if (!m_CurrentTunnelDataMsg)
CreateCurrentTunnelDataMessage ();
@ -18,24 +18,21 @@ namespace tunnel
// create delivery instructions
uint8_t di[43]; // max delivery instruction length is 43 for tunnel
size_t diLen = 1;// flag
TunnelDeliveryType dt = eDeliveryTypeLocal;
if (gwHash)
if (block.deliveryType != eDeliveryTypeLocal) // tunnel or router
{
if (gwTunnel)
if (block.deliveryType == eDeliveryTypeTunnel)
{
*(uint32_t *)(di + diLen) = htobe32 (gwTunnel);
*(uint32_t *)(di + diLen) = htobe32 (block.tunnelID);
diLen += 4; // tunnelID
dt = eDeliveryTypeTunnel;
}
else
dt = eDeliveryTypeRouter;
memcpy (di + diLen, gwHash, 32);
memcpy (di + diLen, block.hash, 32);
diLen += 32; //len
}
di[0] = dt << 5; // set delivery type
di[0] = block.deliveryType << 5; // set delivery type
// create fragments
I2NPMessage * msg = block.data;
if (diLen + msg->GetLength () + 2<= m_RemainingSize)
{
// message fits. First and last fragment
@ -104,7 +101,7 @@ namespace tunnel
{
// delivery instructions don't fit. Create new message
CompleteCurrentTunnelDataMessage ();
PutI2NPMsg (gwHash, gwTunnel, msg);
PutI2NPMsg (block);
// don't delete msg because it's taken care inside
}
}
@ -152,15 +149,15 @@ namespace tunnel
m_CurrentTunnelDataMsg = nullptr;
}
void TunnelGateway::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
void TunnelGateway::SendTunnelDataMsg (const TunnelMessageBlock& block)
{
PutTunnelDataMsg (gwHash, gwTunnel, msg);
PutTunnelDataMsg (block);
SendBuffer ();
}
void TunnelGateway::PutTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
void TunnelGateway::PutTunnelDataMsg (const TunnelMessageBlock& block)
{
m_Buffer.PutI2NPMsg (gwHash, gwTunnel, msg);
m_Buffer.PutI2NPMsg (block);
}
void TunnelGateway::SendBuffer ()

@ -15,7 +15,7 @@ namespace tunnel
public:
TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID),
m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) {};
void PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg);
void PutI2NPMsg (const TunnelMessageBlock& block);
std::vector<I2NPMessage *> GetTunnelDataMsgs ();
private:
@ -37,8 +37,8 @@ namespace tunnel
TunnelGateway (TunnelBase * tunnel):
m_Tunnel (tunnel), m_Buffer (tunnel->GetNextTunnelID ()), m_NumSentBytes (0) {};
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
void PutTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
void SendTunnelDataMsg (const TunnelMessageBlock& block);
void PutTunnelDataMsg (const TunnelMessageBlock& block);
void SendBuffer ();
size_t GetNumSentBytes () const { return m_NumSentBytes; };

Loading…
Cancel
Save