This commit is contained in:
mikhail4021 2014-02-23 21:54:36 +04:00
parent 885dc6603f
commit 2deb4118bc
31 changed files with 783 additions and 212 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -121,6 +121,15 @@ namespace i2p
header->size = htobe16 (len - offset - sizeof (I2NPHeader));
header->chks = 0;
}
uint32_t ToSSU () // return msgID
{
I2NPHeader header = *GetHeader ();
I2NPHeaderShort * ssu = (I2NPHeaderShort *)GetSSUHeader ();
ssu->typeID = header.typeID;
ssu->shortExpiration = htobe32 (be64toh (header.expiration)/1000LL);
len = offset + sizeof (I2NPHeaderShort) + be16toh (header.size);
return be32toh (header.msgID);
}
};
I2NPMessage * NewI2NPMessage ();
void DeleteI2NPMessage (I2NPMessage * msg);

View file

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

View file

@ -4,7 +4,8 @@ CFLAGS = -g -Wall -std=c++0x
OBJECTS = obj/i2p.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transports.o \
obj/RouterContext.o obj/NetDb.o obj/LeaseSet.o obj/Tunnel.o obj/TunnelEndpoint.o \
obj/TunnelGateway.o obj/TransitTunnel.o obj/I2NPProtocol.o obj/Log.o obj/Garlic.o \
obj/HTTPServer.o obj/Streaming.o obj/Identity.o obj/SSU.o obj/util.o obj/Reseed.o
obj/HTTPServer.o obj/Streaming.o obj/Identity.o obj/SSU.o obj/util.o obj/Reseed.o \
obj/UPnP.o
INCFLAGS =
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS =

View file

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

100
NetDb.cpp
View file

@ -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)
@ -448,10 +456,18 @@ namespace data
{
auto r = FindRouter (router);
// do we have that floodfill router in our database?
if (r)
if (r)
{
// we do
if (!dest->IsExcluded (r->GetIdentHash ()) && dest->GetNumExcludedPeers () < 30) // TODO: fix TunnelGateway first
{
// tell floodfill about us
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
r->GetIdentHash (), 0,
CreateDatabaseStoreMsg ()
});
// request destination
auto msg = dest->CreateRequestMessage (r, dest->GetLastReplyTunnel ());
msgs.push_back (i2p::tunnel::TunnelMessageBlock
@ -478,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");
}
@ -506,16 +525,15 @@ namespace data
auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (outbound && inbound)
{
auto floodfill = GetRandomRouter (outbound->GetEndpointRouter (), true);
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint8_t randomHash[32];
rnd.GenerateBlock (randomHash, 32);
RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true);
dest->SetLastOutboundTunnel (outbound);
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
if (floodfill)
{
{
LogPrint ("Exploring new routers ...");
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint8_t randomHash[32];
rnd.GenerateBlock (randomHash, 32);
RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true);
dest->SetLastOutboundTunnel (outbound);
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
@ -529,11 +547,24 @@ namespace data
floodfill->GetIdentHash (), 0,
dest->CreateRequestMessage (floodfill, inbound) // explore
});
outbound->SendTunnelDataMsg (msgs);
outbound->SendTunnelDataMsg (msgs);
}
else
DeleteRequestedDestination (dest);
}
}
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)
{
@ -557,6 +588,15 @@ namespace data
m_RequestedDestinations.erase (it);
}
}
void NetDb::DeleteRequestedDestination (RequestedDestination * dest)
{
if (dest)
{
m_RequestedDestinations.erase (dest->GetDestination ());
delete dest;
}
}
const RouterInfo * NetDb::GetRandomNTCPRouter (bool floodfillOnly) const
{
@ -626,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);
}
}
}
}
}

View file

@ -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,17 +84,21 @@ 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,
bool isLeaseSet, bool isExploratory = false);
void DeleteRequestedDestination (const IdentHash& dest);
void DeleteRequestedDestination (RequestedDestination * dest);
private:
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;

View file

@ -125,7 +125,7 @@ namespace util
private:
std::thread m_Thread;
int running;
volatile int running;
};
}
}

View file

@ -43,7 +43,7 @@ namespace i2p
void RouterContext::OverrideNTCPAddress (const char * host, int port)
{
m_RouterInfo.CreateBuffer ();
auto address = m_RouterInfo.GetNTCPAddress ();
auto address = const_cast<i2p::data::RouterInfo::Address *>(m_RouterInfo.GetNTCPAddress ());
if (address)
{
address->host = boost::asio::ip::address::from_string (host);
@ -68,24 +68,46 @@ namespace i2p
bool RouterContext::Load ()
{
std::ifstream fk (ROUTER_KEYS, std::ifstream::binary | std::ofstream::in);
std::string dataDir = i2p::util::filesystem::GetDataDir ().string ();
#ifndef _WIN32
dataDir.append ("/");
#else
dataDir.append ("\\");
#endif
std::string router_keys = dataDir;
router_keys.append (ROUTER_KEYS);
std::string router_info = dataDir;
router_info.append (ROUTER_INFO);
std::ifstream fk (router_keys.c_str (), std::ifstream::binary | std::ofstream::in);
if (!fk.is_open ()) return false;
fk.read ((char *)&m_Keys, sizeof (m_Keys));
m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
m_RouterInfo = i2p::data::RouterInfo (ROUTER_INFO); // TODO
m_RouterInfo = i2p::data::RouterInfo (router_info.c_str ()); // TODO
return true;
}
void RouterContext::Save ()
{
std::ofstream fk (ROUTER_KEYS, std::ofstream::binary | std::ofstream::out);
std::string dataDir = i2p::util::filesystem::GetDataDir ().string ();
#ifndef _WIN32
dataDir.append ("/");
#else
dataDir.append ("\\");
#endif
std::string router_keys = dataDir;
router_keys.append (ROUTER_KEYS);
std::string router_info = dataDir;
router_info.append (ROUTER_INFO);
std::ofstream fk (router_keys.c_str (), std::ofstream::binary | std::ofstream::out);
fk.write ((char *)&m_Keys, sizeof (m_Keys));
std::ofstream fi (ROUTER_INFO, std::ofstream::binary | std::ofstream::out);
std::ofstream fi (router_info.c_str (), std::ofstream::binary | std::ofstream::out);
fi.write ((char *)m_RouterInfo.GetBuffer (), m_RouterInfo.GetBufferLen ());
}
}

View file

@ -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,18 +323,25 @@ 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 ();
}
RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only)
const RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) const
{
return GetAddress (eTransportNTCP, v4only);
}
RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only)
const RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) const
{
return GetAddress (eTransportSSU, v4only);
}
RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only)
const RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only) const
{
for (auto& address : m_Addresses)
{

View file

@ -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);
@ -54,8 +64,8 @@ namespace data
const char * GetIdentHashAbbreviation () const { return m_IdentHashAbbreviation; };
uint64_t GetTimestamp () const { return m_Timestamp; };
std::vector<Address>& GetAddresses () { return m_Addresses; };
Address * GetNTCPAddress (bool v4only = true);
Address * GetSSUAddress (bool v4only = true);
const Address * GetNTCPAddress (bool v4only = true) const;
const Address * GetSSUAddress (bool v4only = true) const;
const RoutingKey& GetRoutingKey () const { return m_RoutingKey; };
void AddNTCPAddress (const char * host, int port);
@ -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; };
@ -91,7 +102,7 @@ namespace data
size_t ReadString (char * str, std::istream& s);
void WriteString (const std::string& str, std::ostream& s);
void UpdateIdentHashBase64 ();
Address * GetAddress (TransportStyle s, bool v4only);
const Address * GetAddress (TransportStyle s, bool v4only) const;
private:

348
SSU.cpp
View file

@ -14,8 +14,8 @@ namespace i2p
namespace ssu
{
SSUSession::SSUSession (SSUServer * server, const boost::asio::ip::udp::endpoint& remoteEndpoint,
i2p::data::RouterInfo * router): m_Server (server), m_RemoteEndpoint (remoteEndpoint),
SSUSession::SSUSession (SSUServer * server, boost::asio::ip::udp::endpoint& remoteEndpoint,
const i2p::data::RouterInfo * router): m_Server (server), m_RemoteEndpoint (remoteEndpoint),
m_RemoteRouter (router), m_State (eSessionStateUnknown)
{
}
@ -47,6 +47,7 @@ namespace ssu
{
switch (m_State)
{
case eSessionStateConfirmedSent:
case eSessionStateEstablished:
// most common case
ProcessMessage (buf, len);
@ -64,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");
}
@ -86,23 +96,42 @@ namespace ssu
LogPrint ("SSU test received");
break;
case PAYLOAD_TYPE_SESSION_DESTROYED:
{
LogPrint ("SSU session destroy received");
break;
if (m_Server)
m_Server->DeleteSession (this); // delete this
break;
}
case PAYLOAD_TYPE_RELAY_INTRO:
LogPrint ("SSU relay intro received");
// TODO:
break;
default:
LogPrint ("Unexpected SSU payload type ", (int)payloadType);
}
}
// TODO: try intro key as well
else
LogPrint ("MAC verifcation failed");
{
LogPrint ("MAC key failed. Trying intro key");
auto introKey = GetIntroKey ();
if (introKey && Validate (buf, len, introKey))
{
Decrypt (buf, len, introKey);
SSUHeader * header = (SSUHeader *)buf;
LogPrint ("Unexpected SSU payload type ", (int)(header->flag >> 4));
// TODO:
}
else
LogPrint ("MAC verifcation failed");
m_State = eSessionStateUnknown;
}
}
void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{
LogPrint ("Process session request");
// use our intro key
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST,
i2p::context.GetRouterInfo (), buf, len))
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, buf, len))
{
m_State = eSessionStateRequestReceived;
LogPrint ("Session request received");
@ -121,7 +150,7 @@ namespace ssu
}
// use remote intro key
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, *m_RemoteRouter, buf, len))
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, buf, len))
{
m_State = eSessionStateCreatedReceived;
LogPrint ("Session created received");
@ -132,7 +161,6 @@ namespace ssu
i2p::context.UpdateAddress (ourIP.to_string ().c_str ());
uint32_t relayTag = be32toh (*(uint32_t *)(buf + sizeof (SSUHeader) + 263));
SendSessionConfirmed (buf + sizeof (SSUHeader), ourAddress, relayTag);
m_State = eSessionStateEstablished;
}
}
@ -146,9 +174,10 @@ namespace ssu
if ((header->flag >> 4) == PAYLOAD_TYPE_SESSION_CONFIRMED)
{
m_State = eSessionStateConfirmedReceived;
LogPrint ("Session confirmed received");
// TODO:
LogPrint ("Session confirmed received");
m_State = eSessionStateEstablished;
// TODO: send DeliverStatus
Established ();
}
else
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
@ -159,10 +188,10 @@ namespace ssu
void SSUSession::SendSessionRequest ()
{
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr;
if (!address)
auto introKey = GetIntroKey ();
if (!introKey)
{
LogPrint ("Missing remote SSU address");
LogPrint ("SSU is not supported");
return;
}
@ -175,18 +204,49 @@ namespace ssu
uint8_t iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, 304, address->key, iv, address->key);
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, 304, introKey, iv, introKey);
m_State = eSessionStateRequestSent;
m_Server->Send (buf, 304, m_RemoteEndpoint);
}
void SSUSession::SendSessionCreated (const uint8_t * x)
void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer)
{
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr;
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (!address)
{
LogPrint ("Missing remote SSU 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 ();
if (!introKey)
{
LogPrint ("SSU is not supported");
return;
}
uint8_t signedData[532]; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time
@ -222,20 +282,13 @@ namespace ssu
m_Encryption.ProcessData (payload, payload, 48);
// encrypt message with intro key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, address->key, iv, address->key);
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, introKey, iv, introKey);
m_State = eSessionStateRequestSent;
m_Server->Send (buf, 368, m_RemoteEndpoint);
}
void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag)
{
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr;
if (!address)
{
LogPrint ("Missing remote SSU address");
return;
}
uint8_t buf[480 + 18];
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = 1; // 1 fragment
@ -273,15 +326,55 @@ namespace ssu
m_Server->Send (buf, 480, m_RemoteEndpoint);
}
bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, i2p::data::RouterInfo& r, uint8_t * buf, size_t len)
void SSUSession::ProcessRelayResponse (uint8_t * buf, size_t len)
{
auto address = r.GetSSUAddress ();
if (address)
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 ();
if (introKey)
{
// use intro key for verification and decryption
if (Validate (buf, len, address->key))
if (Validate (buf, len, introKey))
{
Decrypt (buf, len, address->key);
Decrypt (buf, len, introKey);
SSUHeader * header = (SSUHeader *)buf;
if ((header->flag >> 4) == expectedPayloadType)
{
@ -292,14 +385,15 @@ 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 by ", r.GetIdentHashAbbreviation ());
LogPrint ("SSU is not supported");
return false;
}
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, uint8_t * aesKey, uint8_t * iv, uint8_t * macKey)
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len,
const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey)
{
if (len < sizeof (SSUHeader))
{
@ -320,7 +414,7 @@ namespace ssu
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac);
}
void SSUSession::Decrypt (uint8_t * buf, size_t len, uint8_t * aesKey)
void SSUSession::Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey)
{
if (len < sizeof (SSUHeader))
{
@ -335,7 +429,7 @@ namespace ssu
m_Decryption.ProcessData (encrypted, encrypted, encryptedLen);
}
bool SSUSession::Validate (uint8_t * buf, size_t len, uint8_t * macKey)
bool SSUSession::Validate (uint8_t * buf, size_t len, const uint8_t * macKey)
{
if (len < sizeof (SSUHeader))
{
@ -358,16 +452,60 @@ namespace ssu
SendSessionRequest ();
}
void SSUSession::ConnectThroughIntroducer (const i2p::data::RouterInfo::Introducer& introducer)
{
SendRelayRequest (introducer);
}
void SSUSession::Close ()
{
SendSesionDestroyed ();
}
void SSUSession::SendI2NPMessage (I2NPMessage * msg)
{
// TODO:
if (!m_DelayedMessages.empty ())
{
for (auto it :m_DelayedMessages)
delete it;
m_DelayedMessages.clear ();
}
}
void SSUSession::Established ()
{
SendI2NPMessage (CreateDatabaseStoreMsg ());
if (!m_DelayedMessages.empty ())
{
for (auto it :m_DelayedMessages)
Send (it);
m_DelayedMessages.clear ();
}
}
const uint8_t * SSUSession::GetIntroKey () const
{
if (m_RemoteRouter)
{
// we are client
auto address = m_RemoteRouter->GetSSUAddress ();
return address ? address->key : nullptr;
}
else
{
// we are server
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
return address ? address->key : nullptr;
}
}
void SSUSession::SendI2NPMessage (I2NPMessage * msg)
{
if (msg)
{
if (m_State == eSessionStateEstablished)
Send (msg);
else
m_DelayedMessages.push_back (msg);
}
}
void SSUSession::ProcessData (uint8_t * buf, size_t len)
{
//uint8_t * start = buf;
@ -438,11 +576,25 @@ namespace ssu
m_IncomleteMessages[msgID] = msg;
if (isLast)
{
SendMsgAck (msgID);
if (fragmentNum > 0)
m_IncomleteMessages.erase (msgID);
msg->FromSSU (msgID);
i2p::HandleI2NPMessage (msg, false);
SendMsgAck (msgID);
if (m_State == eSessionStateEstablished)
i2p::HandleI2NPMessage (msg, false);
else
{
// we expect DeliveryStatus
if (msg->GetHeader ()->typeID == eI2NPDeliveryStatus)
{
LogPrint ("SSU session established");
m_State = eSessionStateEstablished;
Established ();
}
else
LogPrint ("SSU unexpected message ", (int)msg->GetHeader ()->typeID);
DeleteI2NPMessage (msg);
}
}
}
buf += fragmentSize;
@ -474,10 +626,73 @@ namespace ssu
uint8_t buf[48 + 18], iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey);
if (m_State == eSessionStateEstablished)
// encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey);
else
{
auto introKey = GetIntroKey ();
if (introKey)
// encrypt message with intro key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, introKey, iv, introKey);
else
{
LogPrint ("SSU: can't send SessionDestroyed message");
return;
}
}
m_Server->Send (buf, 48, m_RemoteEndpoint);
}
void SSUSession::Send (i2p::I2NPMessage * msg)
{
uint32_t msgID = htobe32 (msg->ToSSU ());
size_t payloadSize = SSU_MTU - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
size_t len = msg->GetLength ();
uint8_t * msgBuf = msg->GetSSUHeader ();
uint32_t fragmentNum = 0;
while (len > 0)
{
uint8_t buf[SSU_MTU + 18], iv[16], * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
payload++;
*payload = 1; // always 1 message fragment per message
payload++;
*(uint32_t *)payload = msgID;
payload += 4;
bool isLast = (len <= payloadSize);
size_t size = isLast ? len : payloadSize;
uint32_t fragmentInfo = (fragmentNum << 17);
if (isLast)
fragmentInfo |= 0x010000;
fragmentInfo |= size;
fragmentInfo = htobe32 (fragmentInfo);
memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3);
payload += 3;
memcpy (payload, msgBuf, size);
size += payload - buf;
if (size % 16) // make sure 16 bytes boundary
size = (size/16 + 1)*16;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size, m_SessionKey, iv, m_MacKey);
m_Server->Send (buf, size, m_RemoteEndpoint);
if (!isLast)
{
len -= payloadSize;
msgBuf += payloadSize;
}
else
len = 0;
fragmentNum++;
}
}
SSUServer::SSUServer (boost::asio::io_service& service, int port):
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (service, m_Endpoint)
@ -499,12 +714,14 @@ namespace ssu
void SSUServer::Stop ()
{
DeleteAllSessions ();
m_Socket.close ();
}
void SSUServer::Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to)
{
m_Socket.send_to (boost::asio::buffer (buf, len), to);
LogPrint ("SSU sent ", len, " bytes");
}
void SSUServer::Receive ()
@ -535,7 +752,7 @@ namespace ssu
LogPrint ("SSU receive error: ", ecode.message ());
}
SSUSession * SSUServer::GetSession (i2p::data::RouterInfo * router)
SSUSession * SSUServer::GetSession (const i2p::data::RouterInfo * router)
{
SSUSession * session = nullptr;
if (router)
@ -549,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
@ -581,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 ());
}
}
}
}

31
SSU.h
View file

@ -3,6 +3,7 @@
#include <inttypes.h>
#include <map>
#include <list>
#include <boost/asio.hpp>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
@ -54,6 +55,8 @@ namespace ssu
eSessionStateCreatedReceived,
eSessionStateConfirmedSent,
eSessionStateConfirmedReceived,
eSessionRelayRequestSent,
eSessionRelayRequestReceived,
eSessionStateEstablished
};
@ -62,11 +65,12 @@ namespace ssu
{
public:
SSUSession (SSUServer * server, const boost::asio::ip::udp::endpoint& remoteEndpoint,
i2p::data::RouterInfo * router = nullptr);
SSUSession (SSUServer * server, boost::asio::ip::udp::endpoint& remoteEndpoint,
const i2p::data::RouterInfo * router = nullptr);
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);
@ -78,29 +82,35 @@ 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);
void SendSesionDestroyed ();
void Send (i2p::I2NPMessage * msg);
bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, i2p::data::RouterInfo& r, uint8_t * buf, size_t len);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, uint8_t * aesKey, uint8_t * iv, uint8_t * macKey);
void Decrypt (uint8_t * buf, size_t len, uint8_t * aesKey);
bool Validate (uint8_t * buf, size_t len, uint8_t * macKey);
bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey);
void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey);
bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey);
const uint8_t * GetIntroKey () const;
private:
SSUServer * m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
i2p::data::RouterInfo * m_RemoteRouter;
const i2p::data::RouterInfo * m_RemoteRouter;
SessionState m_State;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption;
uint8_t m_SessionKey[32], m_MacKey[32];
std::map<uint32_t, I2NPMessage *> m_IncomleteMessages;
std::list<i2p::I2NPMessage *> m_DelayedMessages;
};
class SSUServer
@ -111,12 +121,13 @@ namespace ssu
~SSUServer ();
void Start ();
void Stop ();
SSUSession * GetSession (i2p::data::RouterInfo * router);
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:

View file

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

View file

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

View file

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

View file

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

View file

@ -55,8 +55,7 @@ namespace i2p
}
}
// TODO: do it for SSU only
DetectExternalIP ();
//DetectExternalIP ();
}
void Transports::Stop ()
@ -65,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)
{
@ -169,7 +171,23 @@ namespace i2p
AddNTCPSession (session);
}
else
LogPrint ("No NTCP addresses available");
{
// SSU always have lower prioprity than NTCP
// TODO: it shouldn't
LogPrint ("No NTCP addresses available. Trying SSU");
address = r->GetSSUAddress ();
if (address && m_SSUServer)
{
auto s = m_SSUServer->GetSession (r);
if (s)
{
s->SendI2NPMessage (msg);
return;
}
}
else
LogPrint ("No SSU addresses available");
}
}
else
{
@ -189,7 +207,7 @@ namespace i2p
{
auto router = i2p::data::netdb.GetRandomRouter ();
if (router && router->IsSSU () && m_SSUServer)
m_SSUServer->GetSession (const_cast<i2p::data::RouterInfo *>(router)); //TODO
m_SSUServer->GetSession (router);
}
if (m_Timer)
{

View file

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

View file

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

View file

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

View file

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

68
UPnP.cpp Normal file
View file

@ -0,0 +1,68 @@
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
#include "Log.h"
#include "UPnP.h"
namespace i2p
{
UPnP::UPnP (): m_Timer (m_Service),
m_Endpoint (boost::asio::ip::udp::v4 (), UPNP_REPLY_PORT),
m_MulticastEndpoint (boost::asio::ip::address::from_string (UPNP_GROUP), UPNP_PORT),
m_Socket (m_Service, m_Endpoint.protocol ())
{
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
m_Socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
}
UPnP::~UPnP ()
{
}
void UPnP::Run ()
{
DiscoverRouter ();
m_Service.run ();
}
void UPnP::DiscoverRouter ()
{
m_Timer.expires_from_now (boost::posix_time::seconds(5)); // 5 seconds
m_Timer.async_wait (boost::bind (&UPnP::HandleTimer, this, boost::asio::placeholders::error));
std::string address = UPNP_GROUP;
address += ":" + boost::lexical_cast<std::string>(UPNP_PORT);
std::string request = "M-SEARCH * HTTP/1.1\r\n"
"HOST: " + address + "\r\n"
"ST:" + UPNP_ROUTER + "\r\n"
"MAN:\"ssdp:discover\"\r\n"
"MX:3\r\n"
"\r\n\r\n";
m_Socket.send_to (boost::asio::buffer (request.c_str (), request.length ()), m_MulticastEndpoint);
Receive ();
}
void UPnP::Receive ()
{
m_Socket.async_receive_from (boost::asio::buffer (m_ReceiveBuffer, UPNP_MAX_PACKET_LEN), m_SenderEndpoint,
boost::bind (&UPnP::HandleReceivedFrom, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void UPnP::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred)
{
LogPrint ("UPnP: ", bytes_transferred, " received from ", m_SenderEndpoint.address ());
std::string str (m_ReceiveBuffer, bytes_transferred);
LogPrint (str);
m_Timer.cancel ();
}
void UPnP::HandleTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint ("UPnP: timeout expired");
m_Service.stop ();
}
}
}

41
UPnP.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef UPNP_H__
#define UPNP_H__
#include <boost/asio.hpp>
namespace i2p
{
const int UPNP_MAX_PACKET_LEN = 1500;
const char UPNP_GROUP[] = "239.255.255.250";
const int UPNP_PORT = 1900;
const int UPNP_REPLY_PORT = 1901;
const char UPNP_ROUTER[] = "urn:schemas-upnp-org:device:InternetGatewayDevice:1";
class UPnP
{
public:
UPnP ();
~UPnP ();
void Run ();
private:
void DiscoverRouter ();
void Receive ();
void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred);
void HandleTimer (const boost::system::error_code& ecode);
private:
boost::asio::io_service m_Service;
boost::asio::deadline_timer m_Timer;
boost::asio::ip::udp::endpoint m_Endpoint, m_MulticastEndpoint, m_SenderEndpoint;
boost::asio::ip::udp::socket m_Socket;
char m_ReceiveBuffer[UPNP_MAX_PACKET_LEN];
};
}
#endif

2
hmac.h
View file

@ -13,7 +13,7 @@ namespace crypto
const uint64_t IPAD = 0x3636363636363636;
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
inline void HMACMD5Digest (uint8_t * msg, size_t len, uint8_t * key, uint8_t * digest)
inline void HMACMD5Digest (uint8_t * msg, size_t len, const uint8_t * key, uint8_t * digest)
// key is 32 bytes
// digest is 16 bytes
// block size is 64 bytes

View file

@ -48,8 +48,8 @@
#endif
// Global
int running = 1;
int isDaemon;
volatile int running = 1;
volatile int isDaemon;
#ifndef _WIN32
void handle_signal(int sig)
@ -82,7 +82,7 @@ void handle_signal(int sig)
int main( int argc, char* argv[] )
{
i2p::util::config::OptionParser(argc,argv);
isDaemon = i2p::util::config::GetArg("-daemon", 0);
volatile int isDaemon = i2p::util::config::GetArg("-daemon", 0);
#ifdef _WIN32
setlocale(LC_CTYPE, "");
SetConsoleCP(1251);
@ -139,7 +139,8 @@ int main( int argc, char* argv[] )
}
#endif
int isLogging = i2p::util::config::GetArg("-log", 0);
volatile int isLogging = i2p::util::config::GetArg("-log", 0);
if (isLogging == 1)
{
std::string logfile = i2p::util::filesystem::GetDataDir().string();