mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-04-18 23:16:37 +02:00
result
This commit is contained in:
parent
885dc6603f
commit
2deb4118bc
31 changed files with 783 additions and 212 deletions
58
ElGamal.h
58
ElGamal.h
|
@ -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)
|
||||
|
|
23
Garlic.cpp
23
Garlic.cpp
|
@ -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);
|
||||
|
|
8
Garlic.h
8
Garlic.h
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
16
Identity.h
16
Identity.h
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
3
Makefile
3
Makefile
|
@ -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 =
|
||||
|
|
|
@ -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
100
NetDb.cpp
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
8
NetDb.h
8
NetDb.h
|
@ -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;
|
||||
|
|
2
Queue.h
2
Queue.h
|
@ -125,7 +125,7 @@ namespace util
|
|||
|
||||
private:
|
||||
std::thread m_Thread;
|
||||
int running;
|
||||
volatile int running;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
19
RouterInfo.h
19
RouterInfo.h
|
@ -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
348
SSU.cpp
|
@ -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
31
SSU.h
|
@ -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:
|
||||
|
||||
|
|
|
@ -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 ();
|
||||
|
|
10
Streaming.h
10
Streaming.h
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
34
Tunnel.cpp
34
Tunnel.cpp
|
@ -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 ();
|
||||
}
|
||||
|
||||
|
|
2
Tunnel.h
2
Tunnel.h
|
@ -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; };
|
||||
|
||||
|
|
68
UPnP.cpp
Normal file
68
UPnP.cpp
Normal 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
41
UPnP.h
Normal 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
2
hmac.h
|
@ -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
|
||||
|
|
9
i2p.cpp
9
i2p.cpp
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue