mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-02-02 11:04:00 +01:00
Merge pull request #12 from orignal/master
Merge pull request from orignal/master
This commit is contained in:
commit
05f0a72a0f
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)
|
||||
|
|
72
Garlic.cpp
72
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);
|
||||
|
@ -265,8 +262,14 @@ namespace garlic
|
|||
return ret;
|
||||
}
|
||||
|
||||
void GarlicRouting::HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel)
|
||||
void GarlicRouting::HandleGarlicMessage (I2NPMessage * msg)
|
||||
{
|
||||
if (msg) m_Queue.Put (msg);
|
||||
}
|
||||
|
||||
void GarlicRouting::ProcessGarlicMessage (I2NPMessage * msg)
|
||||
{
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
uint32_t length = be32toh (*(uint32_t *)buf);
|
||||
buf += 4;
|
||||
std::string sessionTag((const char *)buf, 32);
|
||||
|
@ -287,7 +290,7 @@ namespace garlic
|
|||
// new session
|
||||
ElGamalBlock elGamal;
|
||||
if (i2p::crypto::ElGamalDecrypt (
|
||||
isFromTunnel ? i2p::context.GetLeaseSetPrivateKey () : i2p::context.GetPrivateKey (),
|
||||
msg->from ? i2p::context.GetLeaseSetPrivateKey () : i2p::context.GetPrivateKey (),
|
||||
buf, (uint8_t *)&elGamal, true))
|
||||
{
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
|
@ -299,7 +302,7 @@ namespace garlic
|
|||
else
|
||||
LogPrint ("Failed to decrypt garlic");
|
||||
}
|
||||
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
|
||||
void GarlicRouting::HandleAESBlock (uint8_t * buf, size_t len, uint8_t * sessionKey)
|
||||
|
@ -354,7 +357,7 @@ namespace garlic
|
|||
{
|
||||
case eGarlicDeliveryTypeLocal:
|
||||
LogPrint ("Garlic type local");
|
||||
i2p::HandleI2NPMessage (buf, len, false);
|
||||
i2p::HandleI2NPMessage (buf, len);
|
||||
break;
|
||||
case eGarlicDeliveryTypeDestination:
|
||||
{
|
||||
|
@ -413,5 +416,40 @@ namespace garlic
|
|||
LogPrint ("Garlic message ", be32toh (msg->msgID), " acknowledged");
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicRouting::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&GarlicRouting::Run, this));
|
||||
}
|
||||
|
||||
void GarlicRouting::Stop ()
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_Queue.WakeUp ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicRouting::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
I2NPMessage * msg = m_Queue.GetNext ();
|
||||
if (msg)
|
||||
ProcessGarlicMessage (msg);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint ("GarlicRouting: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
21
Garlic.h
21
Garlic.h
|
@ -4,11 +4,14 @@
|
|||
#include <inttypes.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <cryptopp/modes.h>
|
||||
#include <cryptopp/aes.h>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include "I2NPProtocol.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "Tunnel.h"
|
||||
#include "Queue.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -37,7 +40,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 +60,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;
|
||||
|
@ -75,20 +78,28 @@ namespace garlic
|
|||
GarlicRouting ();
|
||||
~GarlicRouting ();
|
||||
|
||||
void HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel);
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
void HandleGarlicMessage (I2NPMessage * msg);
|
||||
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:
|
||||
|
||||
void Run ();
|
||||
void ProcessGarlicMessage (I2NPMessage * msg);
|
||||
void HandleAESBlock (uint8_t * buf, size_t len, uint8_t * sessionKey);
|
||||
void HandleGarlicPayload (uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
i2p::util::Queue<I2NPMessage> m_Queue;
|
||||
// outgoing sessions
|
||||
std::map<i2p::data::IdentHash, GarlicRoutingSession *> m_Sessions;
|
||||
std::map<uint32_t, GarlicRoutingSession *> m_CreatedSessions; // msgID -> session
|
||||
|
|
|
@ -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(),
|
||||
|
@ -133,6 +144,7 @@ namespace util
|
|||
}
|
||||
|
||||
s << "<P>Transports</P>";
|
||||
s << "NTCP<BR>";
|
||||
for (auto it: i2p::transports.GetNTCPSessions ())
|
||||
{
|
||||
// RouterInfo of incoming connection doesn't have address
|
||||
|
@ -146,17 +158,36 @@ namespace util
|
|||
s << "<BR>";
|
||||
}
|
||||
}
|
||||
auto ssuServer = i2p::transports.GetSSUServer ();
|
||||
if (ssuServer)
|
||||
{
|
||||
s << "<BR>SSU<BR>";
|
||||
for (auto it: ssuServer->GetSessions ())
|
||||
{
|
||||
// incoming connections don't have remote router
|
||||
bool outgoing = it.second->GetRemoteRouter ();
|
||||
auto endpoint = it.second->GetRemoteEndpoint ();
|
||||
if (outgoing) s << "-->";
|
||||
s << endpoint.address ().to_string () << ":" << endpoint.port ();
|
||||
if (!outgoing) s << "-->";
|
||||
s << "<BR>";
|
||||
}
|
||||
}
|
||||
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 +201,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 +224,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 ();
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace i2p
|
|||
I2NPMessage * msg = new I2NPMessage;
|
||||
msg->offset = 2; // reserve 2 bytes for NTCP header, should reserve more for SSU in future
|
||||
msg->len = sizeof (I2NPHeader) + 2;
|
||||
msg->from = nullptr;
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
@ -70,8 +71,16 @@ namespace i2p
|
|||
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
|
||||
{
|
||||
I2NPDeliveryStatusMsg msg;
|
||||
msg.msgID = htobe32 (msgID);
|
||||
msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ());
|
||||
if (msgID)
|
||||
{
|
||||
msg.msgID = htobe32 (msgID);
|
||||
msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ());
|
||||
}
|
||||
else // for SSU establishment
|
||||
{
|
||||
msg.msgID = htobe32 (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ());
|
||||
msg.timestamp = htobe64 (2); // netID = 2
|
||||
}
|
||||
return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg));
|
||||
}
|
||||
|
||||
|
@ -212,7 +221,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 +401,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");
|
||||
|
@ -406,7 +415,7 @@ namespace i2p
|
|||
return be16toh (header->size) + sizeof (I2NPHeader);
|
||||
}
|
||||
|
||||
void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel)
|
||||
void HandleI2NPMessage (uint8_t * msg, size_t len)
|
||||
{
|
||||
I2NPHeader * header = (I2NPHeader *)msg;
|
||||
uint32_t msgID = be32toh (header->msgID);
|
||||
|
@ -415,12 +424,7 @@ namespace i2p
|
|||
uint8_t * buf = msg + sizeof (I2NPHeader);
|
||||
int size = be16toh (header->size);
|
||||
switch (header->typeID)
|
||||
{
|
||||
case eI2NPGarlic:
|
||||
LogPrint ("Garlic");
|
||||
i2p::garlic::routing.HandleGarlicMessage (buf, size, isFromTunnel);
|
||||
break;
|
||||
break;
|
||||
{
|
||||
case eI2NPDeliveryStatus:
|
||||
LogPrint ("DeliveryStatus");
|
||||
// we assume DeliveryStatusMessage is sent with garlic only
|
||||
|
@ -443,7 +447,7 @@ namespace i2p
|
|||
}
|
||||
}
|
||||
|
||||
void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel)
|
||||
void HandleI2NPMessage (I2NPMessage * msg)
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
|
@ -465,8 +469,12 @@ namespace i2p
|
|||
LogPrint ("DatabaseSearchReply");
|
||||
i2p::data::netdb.PostI2NPMsg (msg);
|
||||
break;
|
||||
case eI2NPGarlic:
|
||||
LogPrint ("Garlic");
|
||||
i2p::garlic::routing.HandleGarlicMessage (msg);
|
||||
break;
|
||||
default:
|
||||
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength (), isFromTunnel);
|
||||
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <inttypes.h>
|
||||
#include <set>
|
||||
#include <string.h>
|
||||
#include "I2PEndian.h"
|
||||
#include "RouterInfo.h"
|
||||
|
||||
namespace i2p
|
||||
|
@ -19,6 +20,12 @@ namespace i2p
|
|||
uint8_t chks;
|
||||
};
|
||||
|
||||
struct I2NPHeaderShort
|
||||
{
|
||||
uint8_t typeID;
|
||||
uint32_t shortExpiration;
|
||||
};
|
||||
|
||||
struct I2NPDatabaseStoreMsg
|
||||
{
|
||||
uint8_t key[32];
|
||||
|
@ -84,11 +91,17 @@ namespace i2p
|
|||
eI2NPVariableTunnelBuildReply = 24
|
||||
};
|
||||
|
||||
namespace tunnel
|
||||
{
|
||||
class InboundTunnel;
|
||||
}
|
||||
|
||||
const int NTCP_MAX_MESSAGE_SIZE = 16384;
|
||||
struct I2NPMessage
|
||||
{
|
||||
uint8_t buf[NTCP_MAX_MESSAGE_SIZE];
|
||||
size_t len, offset;
|
||||
i2p::tunnel::InboundTunnel * from;
|
||||
|
||||
I2NPHeader * GetHeader () { return (I2NPHeader *)(buf + offset); };
|
||||
uint8_t * GetPayload () { return buf + offset + sizeof(I2NPHeader); };
|
||||
|
@ -99,8 +112,31 @@ namespace i2p
|
|||
{
|
||||
memcpy (buf + offset, other.buf + other.offset, other.GetLength ());
|
||||
len = offset + other.GetLength ();
|
||||
from = other.from;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// for SSU only
|
||||
uint8_t * GetSSUHeader () { return buf + offset + sizeof(I2NPHeader) - sizeof(I2NPHeaderShort); };
|
||||
void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular
|
||||
{
|
||||
I2NPHeaderShort ssu = *(I2NPHeaderShort *)GetSSUHeader ();
|
||||
I2NPHeader * header = GetHeader ();
|
||||
header->typeID = ssu.typeID;
|
||||
header->msgID = htobe32 (msgID);
|
||||
header->expiration = htobe64 (be32toh (ssu.shortExpiration)*1000LL);
|
||||
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);
|
||||
|
@ -140,8 +176,8 @@ namespace i2p
|
|||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg);
|
||||
|
||||
size_t GetI2NPMessageLength (uint8_t * msg);
|
||||
void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel);
|
||||
void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel);
|
||||
void HandleI2NPMessage (uint8_t * msg, size_t len);
|
||||
void HandleI2NPMessage (I2NPMessage * msg);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
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 =
|
||||
|
|
|
@ -424,7 +424,7 @@ namespace ntcp
|
|||
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
|
||||
{
|
||||
// we have a complete I2NP message
|
||||
i2p::HandleI2NPMessage (m_NextMessage, false);
|
||||
i2p::HandleI2NPMessage (m_NextMessage);
|
||||
m_NextMessage = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
102
NetDb.cpp
102
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)
|
||||
{
|
||||
|
@ -111,7 +111,7 @@ namespace data
|
|||
else // WTF?
|
||||
{
|
||||
LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID);
|
||||
i2p::HandleI2NPMessage (msg, false);
|
||||
i2p::HandleI2NPMessage (msg);
|
||||
}
|
||||
msg = m_Queue.Get ();
|
||||
}
|
||||
|
@ -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;
|
||||
|
|
8
Queue.h
8
Queue.h
|
@ -98,17 +98,18 @@ namespace util
|
|||
{
|
||||
public:
|
||||
|
||||
MsgQueue (): m_Thread (std::bind (&MsgQueue<Msg>::Run, this)) {};
|
||||
MsgQueue (): m_Thread (std::bind (&MsgQueue<Msg>::Run, this)) , running(1) {};
|
||||
void Stop()
|
||||
{
|
||||
m_Thread.detach();
|
||||
running = 0;
|
||||
m_Thread.join();
|
||||
}
|
||||
|
||||
private:
|
||||
void Run ()
|
||||
{
|
||||
Msg * msg = nullptr;
|
||||
while ((msg = Queue<Msg>::GetNext ()) != nullptr)
|
||||
while ((msg = Queue<Msg>::GetNext ()) != nullptr && running)
|
||||
{
|
||||
msg->Process ();
|
||||
delete msg;
|
||||
|
@ -117,6 +118,7 @@ namespace util
|
|||
|
||||
private:
|
||||
std::thread m_Thread;
|
||||
volatile int running;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,25 +25,32 @@ namespace i2p
|
|||
m_Keys = i2p::data::CreateRandomKeys ();
|
||||
m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
|
||||
CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
|
||||
|
||||
i2p::data::Identity ident;
|
||||
ident = m_Keys;
|
||||
m_RouterInfo.SetRouterIdentity (ident);
|
||||
|
||||
m_RouterInfo.AddNTCPAddress ("127.0.0.1", 17007); // TODO:
|
||||
m_RouterInfo.SetProperty ("caps", "LR");
|
||||
m_RouterInfo.SetProperty ("coreVersion", "0.9.8.1");
|
||||
m_RouterInfo.SetProperty ("netId", "2");
|
||||
m_RouterInfo.SetProperty ("router.version", "0.9.8.1");
|
||||
m_RouterInfo.SetProperty ("start_uptime", "90m");
|
||||
|
||||
m_RouterInfo.CreateBuffer ();
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::UpdateRouterInfo ()
|
||||
{
|
||||
i2p::data::Identity ident;
|
||||
ident = m_Keys;
|
||||
|
||||
i2p::data::RouterInfo routerInfo;
|
||||
routerInfo.SetRouterIdentity (ident);
|
||||
routerInfo.AddSSUAddress ("127.0.0.1", 17007, routerInfo.GetIdentHash ());
|
||||
routerInfo.AddNTCPAddress ("127.0.0.1", 17007); // TODO:
|
||||
routerInfo.SetProperty ("caps", "LR");
|
||||
routerInfo.SetProperty ("coreVersion", "0.9.8.1");
|
||||
routerInfo.SetProperty ("netId", "2");
|
||||
routerInfo.SetProperty ("router.version", "0.9.8.1");
|
||||
routerInfo.SetProperty ("start_uptime", "90m");
|
||||
routerInfo.CreateBuffer ();
|
||||
|
||||
m_RouterInfo = routerInfo;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -51,6 +58,14 @@ namespace i2p
|
|||
}
|
||||
|
||||
m_RouterInfo.CreateBuffer ();
|
||||
Save (true);
|
||||
}
|
||||
|
||||
void RouterContext::UpdateAddress (const char * host)
|
||||
{
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
address.host = boost::asio::ip::address::from_string (host);
|
||||
m_RouterInfo.CreateBuffer ();
|
||||
}
|
||||
|
||||
void RouterContext::Sign (uint8_t * buf, int len, uint8_t * signature)
|
||||
|
@ -61,24 +76,49 @@ 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 ()
|
||||
void RouterContext::Save (bool infoOnly)
|
||||
{
|
||||
std::ofstream fk (ROUTER_KEYS, 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::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);
|
||||
|
||||
if (!infoOnly)
|
||||
{
|
||||
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.c_str (), std::ofstream::binary | std::ofstream::out);
|
||||
fi.write ((char *)m_RouterInfo.GetBuffer (), m_RouterInfo.GetBufferLen ());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,12 +28,14 @@ namespace i2p
|
|||
void Sign (uint8_t * buf, int len, uint8_t * signature);
|
||||
|
||||
void OverrideNTCPAddress (const char * host, int port); // temporary
|
||||
void UpdateAddress (const char * host); // called from SSU
|
||||
|
||||
private:
|
||||
|
||||
void CreateNewRouter ();
|
||||
void UpdateRouterInfo ();
|
||||
bool Load ();
|
||||
void Save ();
|
||||
void Save (bool infoOnly = false);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -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] - '0'; // 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);
|
||||
}
|
||||
|
@ -159,6 +180,9 @@ namespace data
|
|||
CryptoPP::SHA256().CalculateDigest(m_IdentHash, (uint8_t *)&m_RouterIdentity, sizeof (m_RouterIdentity));
|
||||
UpdateIdentHashBase64 ();
|
||||
UpdateRoutingKey ();
|
||||
|
||||
if (!m_SupportedTransports)
|
||||
SetUnreachable (true);
|
||||
}
|
||||
|
||||
void RouterInfo::UpdateIdentHashBase64 ()
|
||||
|
@ -187,18 +211,36 @@ namespace data
|
|||
{
|
||||
s.write ((char *)&address.cost, sizeof (address.cost));
|
||||
s.write ((char *)&address.date, sizeof (address.date));
|
||||
std::stringstream properties;
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
WriteString ("NTCP", s);
|
||||
else if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
WriteString ("SSU", s);
|
||||
// caps
|
||||
WriteString ("caps", properties);
|
||||
properties << '=';
|
||||
WriteString ("B", properties); // TODO: should be 'BC' for introducers
|
||||
properties << ';';
|
||||
}
|
||||
else
|
||||
WriteString ("", s);
|
||||
|
||||
std::stringstream properties;
|
||||
WriteString ("host", properties);
|
||||
properties << '=';
|
||||
WriteString (address.host.to_string (), properties);
|
||||
properties << ';';
|
||||
if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
// wtite intro key
|
||||
WriteString ("key", properties);
|
||||
properties << '=';
|
||||
char value[64];
|
||||
size_t l = ByteStreamToBase64 (address.key, 32, value, 64);
|
||||
value[l] = 0;
|
||||
WriteString (value, properties);
|
||||
properties << ';';
|
||||
}
|
||||
WriteString ("port", properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(address.port), properties);
|
||||
|
@ -264,8 +306,22 @@ namespace data
|
|||
addr.cost = 2;
|
||||
addr.date = 0;
|
||||
m_Addresses.push_back(addr);
|
||||
m_SupportedTransports |= eNTCPV4;
|
||||
}
|
||||
|
||||
void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key)
|
||||
{
|
||||
Address addr;
|
||||
addr.host = boost::asio::ip::address::from_string (host);
|
||||
addr.port = port;
|
||||
addr.transportStyle = eTransportSSU;
|
||||
addr.cost = 10; // NTCP should have prioprity over SSU
|
||||
addr.date = 0;
|
||||
memcpy (addr.key, key, 32);
|
||||
m_Addresses.push_back(addr);
|
||||
m_SupportedTransports |= eSSUV4;
|
||||
}
|
||||
|
||||
void RouterInfo::SetProperty (const char * key, const char * value)
|
||||
{
|
||||
m_Properties[key] = value;
|
||||
|
@ -294,18 +350,33 @@ namespace data
|
|||
else
|
||||
return m_SupportedTransports & (eNTCPV4 | eNTCPV6);
|
||||
}
|
||||
|
||||
bool RouterInfo::IsSSU (bool v4only) const
|
||||
{
|
||||
if (v4only)
|
||||
return m_SupportedTransports & eSSUV4;
|
||||
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)
|
||||
{
|
||||
|
|
23
RouterInfo.h
23
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);
|
||||
|
@ -53,17 +63,20 @@ namespace data
|
|||
const char * GetIdentHashBase64 () const { return m_IdentHashBase64; };
|
||||
const char * GetIdentHashAbbreviation () const { return m_IdentHashAbbreviation; };
|
||||
uint64_t GetTimestamp () const { return m_Timestamp; };
|
||||
const std::vector<Address>& GetAddresses () const { return m_Addresses; };
|
||||
Address * GetNTCPAddress (bool v4only = true);
|
||||
Address * GetSSUAddress (bool v4only = true);
|
||||
std::vector<Address>& GetAddresses () { return m_Addresses; };
|
||||
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);
|
||||
void AddSSUAddress (const char * host, int port, const uint8_t * key);
|
||||
void SetProperty (const char * key, const char * value);
|
||||
const char * GetProperty (const char * key) const;
|
||||
bool IsFloodfill () const;
|
||||
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; };
|
||||
|
@ -90,7 +103,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:
|
||||
|
||||
|
|
505
SSU.cpp
505
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,7 +47,8 @@ namespace ssu
|
|||
{
|
||||
switch (m_State)
|
||||
{
|
||||
case eSessionStateEstablised:
|
||||
case eSessionStateConfirmedSent:
|
||||
case eSessionStateEstablished:
|
||||
// most common case
|
||||
ProcessMessage (buf, len);
|
||||
break;
|
||||
|
@ -64,6 +65,21 @@ namespace ssu
|
|||
// session confirmed
|
||||
ProcessSessionConfirmed (buf, len);
|
||||
break;
|
||||
case eSessionRelayRequestSent:
|
||||
// relay response
|
||||
ProcessRelayResponse (buf,len);
|
||||
break;
|
||||
case eSessionRelayResponseReceived:
|
||||
// HolePunch received
|
||||
LogPrint ("SSU HolePuch of ", len, " bytes received");
|
||||
m_State = eSessionStateEstablished;
|
||||
Established ();
|
||||
break;
|
||||
case eSessionRelayRequestReceived:
|
||||
// HolePunch
|
||||
m_State = eSessionStateUnknown;
|
||||
Connect ();
|
||||
break;
|
||||
default:
|
||||
LogPrint ("SSU state not implemented yet");
|
||||
}
|
||||
|
@ -85,24 +101,43 @@ namespace ssu
|
|||
case PAYLOAD_TYPE_TEST:
|
||||
LogPrint ("SSU test received");
|
||||
break;
|
||||
case PAYLOAD_TYPE_SESSION_DESTROY:
|
||||
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,17 +156,42 @@ 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");
|
||||
uint8_t * ourAddress = buf + sizeof (SSUHeader) + 257;
|
||||
boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )(ourAddress)));
|
||||
uint16_t ourPort = be16toh (*(uint16_t *)(ourAddress + 4));
|
||||
uint8_t signedData[532]; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
uint8_t * y = payload;
|
||||
memcpy (signedData, i2p::context.GetRouterIdentity ().publicKey, 256); // x
|
||||
memcpy (signedData + 256, y, 256); // y
|
||||
payload += 256;
|
||||
payload += 1; // size, assume 4
|
||||
uint8_t * ourAddress = payload;
|
||||
boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )ourAddress));
|
||||
payload += 4; // address
|
||||
uint16_t ourPort = be16toh (*(uint16_t *)payload);
|
||||
payload += 2; // port
|
||||
memcpy (signedData + 512, ourAddress, 6); // our IP and port
|
||||
LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort);
|
||||
uint32_t relayTag = be32toh (*(uint32_t *)(buf + sizeof (SSUHeader) + 263));
|
||||
SendSessionConfirmed (buf + sizeof (SSUHeader), ourAddress, relayTag);
|
||||
m_State = eSessionStateEstablised;
|
||||
i2p::context.UpdateAddress (ourIP.to_string ().c_str ());
|
||||
*(uint32_t *)(signedData + 518) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ()); // remote IP
|
||||
*(uint16_t *)(signedData + 522) = htobe16 (m_RemoteEndpoint.port ()); // remote port
|
||||
memcpy (signedData + 524, payload, 8); // relayTag and signed on time
|
||||
uint32_t relayTag = be32toh (*(uint32_t *)payload);
|
||||
payload += 4; // relayTag
|
||||
payload += 4; // signed on time
|
||||
// decrypt DSA signature
|
||||
m_Decryption.SetKeyWithIV (m_SessionKey, 32, ((SSUHeader *)buf)->iv);
|
||||
m_Decryption.ProcessData (payload, payload, 48);
|
||||
// verify
|
||||
CryptoPP::DSA::PublicKey pubKey;
|
||||
pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, CryptoPP::Integer (m_RemoteRouter->GetRouterIdentity ().signingKey, 128));
|
||||
CryptoPP::DSA::Verifier verifier (pubKey);
|
||||
if (!verifier.VerifyMessage (signedData, 532, payload, 40))
|
||||
LogPrint ("SSU signature verification failed");
|
||||
|
||||
SendSessionConfirmed (y, ourAddress, relayTag);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,9 +205,10 @@ namespace ssu
|
|||
if ((header->flag >> 4) == PAYLOAD_TYPE_SESSION_CONFIRMED)
|
||||
{
|
||||
m_State = eSessionStateConfirmedReceived;
|
||||
LogPrint ("Session confirmed received");
|
||||
// TODO:
|
||||
m_State = eSessionStateEstablised;
|
||||
LogPrint ("Session confirmed received");
|
||||
m_State = eSessionStateEstablished;
|
||||
SendI2NPMessage (CreateDeliveryStatusMsg (0));
|
||||
Established ();
|
||||
}
|
||||
else
|
||||
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
|
||||
|
@ -158,10 +219,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;
|
||||
}
|
||||
|
||||
|
@ -174,18 +235,50 @@ 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 ();
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
|
||||
if (!introKey || !address)
|
||||
{
|
||||
LogPrint ("SSU is not supported");
|
||||
return;
|
||||
}
|
||||
uint8_t signedData[532]; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time
|
||||
|
@ -203,8 +296,8 @@ namespace ssu
|
|||
*(uint16_t *)(payload) = htobe16 (m_RemoteEndpoint.port ());
|
||||
payload += 2;
|
||||
memcpy (signedData + 512, payload - 6, 6); // remote endpoint IP and port
|
||||
*(uint32_t *)(signedData + 518) = htobe32 (m_Server->GetEndpoint ().address ().to_v4 ().to_ulong ()); // our IP
|
||||
*(uint16_t *)(signedData + 522) = htobe16 (m_Server->GetEndpoint ().port ()); // our port
|
||||
*(uint32_t *)(signedData + 518) = htobe32 (address->host.to_v4 ().to_ulong ()); // our IP
|
||||
*(uint16_t *)(signedData + 522) = htobe16 (address->port); // our port
|
||||
*(uint32_t *)(payload) = 0; // relay tag, always 0 for now
|
||||
payload += 4;
|
||||
*(uint32_t *)(payload) = htobe32 (i2p::util::GetSecondsSinceEpoch ()); // signed on time
|
||||
|
@ -221,20 +314,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);
|
||||
m_State = eSessionStateRequestSent;
|
||||
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, introKey, iv, introKey);
|
||||
m_State = eSessionStateCreatedSent;
|
||||
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
|
||||
|
@ -272,15 +358,56 @@ 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 ());
|
||||
m_State= eSessionRelayResponseReceived;
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
@ -291,14 +418,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))
|
||||
{
|
||||
|
@ -319,7 +447,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))
|
||||
{
|
||||
|
@ -334,7 +462,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))
|
||||
{
|
||||
|
@ -357,17 +485,88 @@ namespace ssu
|
|||
SendSessionRequest ();
|
||||
}
|
||||
|
||||
void SSUSession::SendI2NPMessage (I2NPMessage * msg)
|
||||
void SSUSession::ConnectThroughIntroducer (const i2p::data::RouterInfo::Introducer& introducer)
|
||||
{
|
||||
SendRelayRequest (introducer);
|
||||
}
|
||||
|
||||
void SSUSession::Close ()
|
||||
{
|
||||
// TODO:
|
||||
SendSesionDestroyed ();
|
||||
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;
|
||||
uint8_t flag = *buf;
|
||||
buf++;
|
||||
LogPrint ("Process SSU data flags=", (int)flag);
|
||||
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
|
||||
{
|
||||
// explicit ACKs
|
||||
uint8_t numAcks =*buf;
|
||||
buf++;
|
||||
// TODO: process ACKs
|
||||
buf += numAcks*4;
|
||||
}
|
||||
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
|
||||
{
|
||||
// explicit ACK bitfields
|
||||
uint8_t numBitfields =*buf;
|
||||
buf++;
|
||||
for (int i = 0; i < numBitfields; i++)
|
||||
{
|
||||
buf += 4; // msgID
|
||||
// TODO: process ACH bitfields
|
||||
while (*buf & 0x80) // not last
|
||||
buf++;
|
||||
buf++; // last byte
|
||||
}
|
||||
}
|
||||
uint8_t numFragments = *buf; // number of fragments
|
||||
buf++;
|
||||
for (int i = 0; i < numFragments; i++)
|
||||
|
@ -382,14 +581,157 @@ namespace ssu
|
|||
uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
|
||||
bool isLast = fragmentInfo & 0x010000; // bit 16
|
||||
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
|
||||
LogPrint ("SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last");
|
||||
LogPrint ("SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last");
|
||||
I2NPMessage * msg = nullptr;
|
||||
if (fragmentNum > 0) // follow-up fragment
|
||||
{
|
||||
auto it = m_IncomleteMessages.find (msgID);
|
||||
if (it != m_IncomleteMessages.end ())
|
||||
{
|
||||
msg = it->second;
|
||||
memcpy (msg->buf + msg->len, buf, fragmentSize);
|
||||
msg->len += fragmentSize;
|
||||
}
|
||||
else
|
||||
// TODO:
|
||||
LogPrint ("Unexpected follow-on fragment ", fragmentNum, " of message ", msgID);
|
||||
}
|
||||
else // first fragment
|
||||
{
|
||||
msg = NewI2NPMessage ();
|
||||
memcpy (msg->GetSSUHeader (), buf, fragmentSize);
|
||||
msg->len += fragmentSize - sizeof (I2NPHeaderShort);
|
||||
}
|
||||
|
||||
if (msg)
|
||||
{
|
||||
if (!fragmentNum && !isLast)
|
||||
m_IncomleteMessages[msgID] = msg;
|
||||
if (isLast)
|
||||
{
|
||||
SendMsgAck (msgID);
|
||||
if (fragmentNum > 0)
|
||||
m_IncomleteMessages.erase (msgID);
|
||||
msg->FromSSU (msgID);
|
||||
if (m_State == eSessionStateEstablished)
|
||||
i2p::HandleI2NPMessage (msg);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::SendMsgAck (uint32_t msgID)
|
||||
{
|
||||
uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16
|
||||
uint8_t iv[16];
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
|
||||
payload++;
|
||||
*payload = 1; // number of ACKs
|
||||
payload++;
|
||||
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
|
||||
payload += 4;
|
||||
*payload = 0; // number of fragments
|
||||
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
rnd.GenerateBlock (iv, 16); // random iv
|
||||
// encrypt message with session key
|
||||
FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48, m_SessionKey, iv, m_MacKey);
|
||||
m_Server->Send (buf, 48, m_RemoteEndpoint);
|
||||
}
|
||||
|
||||
void SSUSession::SendSesionDestroyed ()
|
||||
{
|
||||
uint8_t buf[48 + 18], iv[16];
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
rnd.GenerateBlock (iv, 16); // random iv
|
||||
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)
|
||||
{
|
||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
||||
}
|
||||
|
||||
SSUServer::~SSUServer ()
|
||||
|
@ -405,12 +747,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 ()
|
||||
|
@ -441,7 +785,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)
|
||||
|
@ -455,12 +799,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
|
||||
|
@ -468,6 +827,38 @@ namespace ssu
|
|||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
void SSUServer::DeleteSession (SSUSession * session)
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
session->Close ();
|
||||
m_Sessions.erase (session->GetRemoteEndpoint ());
|
||||
delete session;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::DeleteAllSessions ()
|
||||
{
|
||||
for (auto it: m_Sessions)
|
||||
{
|
||||
it.second->Close ();
|
||||
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 ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
56
SSU.h
56
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>
|
||||
|
@ -35,7 +36,15 @@ namespace ssu
|
|||
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
|
||||
const uint8_t PAYLOAD_TYPE_DATA = 6;
|
||||
const uint8_t PAYLOAD_TYPE_TEST = 7;
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_DESTROY = 8;
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
|
||||
|
||||
// data flags
|
||||
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
|
||||
const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
|
||||
const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08;
|
||||
const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10;
|
||||
const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
|
||||
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
|
||||
|
||||
enum SessionState
|
||||
{
|
||||
|
@ -46,7 +55,10 @@ namespace ssu
|
|||
eSessionStateCreatedReceived,
|
||||
eSessionStateConfirmedSent,
|
||||
eSessionStateConfirmedReceived,
|
||||
eSessionStateEstablised
|
||||
eSessionRelayRequestSent,
|
||||
eSessionRelayRequestReceived,
|
||||
eSessionRelayResponseReceived,
|
||||
eSessionStateEstablished
|
||||
};
|
||||
|
||||
class SSUServer;
|
||||
|
@ -54,13 +66,17 @@ 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; };
|
||||
const i2p::data::RouterInfo * GetRemoteRouter () const { return m_RemoteRouter; };
|
||||
void SendI2NPMessage (I2NPMessage * msg);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey);
|
||||
|
@ -68,26 +84,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);
|
||||
|
||||
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);
|
||||
void SendMsgAck (uint32_t msgID);
|
||||
void SendSesionDestroyed ();
|
||||
void Send (i2p::I2NPMessage * msg);
|
||||
|
||||
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
|
||||
|
@ -98,10 +123,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 ();
|
||||
|
||||
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:
|
||||
|
||||
|
@ -115,6 +143,10 @@ namespace ssu
|
|||
boost::asio::ip::udp::endpoint m_SenderEndpoint;
|
||||
uint8_t m_ReceiveBuffer[2*SSU_MTU];
|
||||
std::map<boost::asio::ip::udp::endpoint, SSUSession *> m_Sessions;
|
||||
|
||||
public:
|
||||
// for HTTP only
|
||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace i2p
|
|||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&Transports::Run, this));
|
||||
m_Timer = new boost::asio::deadline_timer (m_Service);
|
||||
// create acceptors
|
||||
auto addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (auto& address : addresses)
|
||||
|
@ -46,6 +47,7 @@ namespace i2p
|
|||
m_SSUServer = new i2p::ssu::SSUServer (m_Service, address.port);
|
||||
LogPrint ("Start listening UDP port ", address.port);
|
||||
m_SSUServer->Start ();
|
||||
DetectExternalIP ();
|
||||
}
|
||||
else
|
||||
LogPrint ("SSU server already exists");
|
||||
|
@ -60,6 +62,12 @@ namespace i2p
|
|||
m_NTCPSessions.clear ();
|
||||
delete m_NTCPAcceptor;
|
||||
|
||||
if (m_Timer)
|
||||
{
|
||||
m_Timer->cancel ();
|
||||
delete m_Timer;
|
||||
}
|
||||
|
||||
if (m_SSUServer)
|
||||
{
|
||||
m_SSUServer->Stop ();
|
||||
|
@ -140,7 +148,7 @@ namespace i2p
|
|||
{
|
||||
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
|
||||
// we send it to ourself
|
||||
i2p::HandleI2NPMessage (msg, false);
|
||||
i2p::HandleI2NPMessage (msg);
|
||||
else
|
||||
m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg));
|
||||
}
|
||||
|
@ -160,7 +168,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
|
||||
{
|
||||
|
@ -173,4 +197,30 @@ namespace i2p
|
|||
else
|
||||
LogPrint ("Session not found");
|
||||
}
|
||||
|
||||
void Transports::DetectExternalIP ()
|
||||
{
|
||||
for (int i = 0; i < 5; i ++)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomRouter ();
|
||||
if (router && router->IsSSU () && m_SSUServer)
|
||||
m_SSUServer->GetSession (router);
|
||||
}
|
||||
if (m_Timer)
|
||||
{
|
||||
m_Timer->expires_from_now (boost::posix_time::seconds(5)); // 5 seconds
|
||||
m_Timer->async_wait (boost::bind (&Transports::HandleTimer, this, boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::HandleTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
// end of external IP detection
|
||||
if (m_SSUServer)
|
||||
m_SSUServer->DeleteAllSessions ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,6 +38,9 @@ namespace i2p
|
|||
void Run ();
|
||||
void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error);
|
||||
void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
|
||||
|
||||
void DetectExternalIP ();
|
||||
void HandleTimer (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -49,11 +52,13 @@ namespace i2p
|
|||
|
||||
std::map<i2p::data::IdentHash, i2p::ntcp::NTCPSession *> m_NTCPSessions;
|
||||
i2p::ssu::SSUServer * m_SSUServer;
|
||||
boost::asio::deadline_timer * m_Timer;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; };
|
||||
const i2p::ssu::SSUServer * GetSSUServer () const { return m_SSUServer; };
|
||||
};
|
||||
|
||||
extern Transports transports;
|
||||
|
|
46
Tunnel.cpp
46
Tunnel.cpp
|
@ -130,34 +130,35 @@ namespace tunnel
|
|||
|
||||
void InboundTunnel::HandleTunnelDataMsg (I2NPMessage * msg)
|
||||
{
|
||||
msg->from = this;
|
||||
EncryptTunnelMsg (msg);
|
||||
m_Endpoint.HandleDecryptedTunnelDataMsg (msg);
|
||||
}
|
||||
|
||||
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 ();
|
||||
}
|
||||
|
||||
|
@ -389,7 +390,7 @@ namespace tunnel
|
|||
CreateTunnel<OutboundTunnel> (
|
||||
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
|
||||
{
|
||||
i2p::data::netdb.GetRandomNTCPRouter ()
|
||||
i2p::data::netdb.GetRandomRouter ()
|
||||
},
|
||||
inboundTunnel->GetTunnelConfig ()));
|
||||
}
|
||||
|
@ -397,7 +398,7 @@ namespace tunnel
|
|||
{
|
||||
|
||||
LogPrint ("Creating two hops outbound tunnel...");
|
||||
auto firstHop = i2p::data::netdb.GetRandomNTCPRouter (); // first hop must be NTCP
|
||||
auto firstHop = i2p::data::netdb.GetRandomRouter ();
|
||||
CreateTunnel<OutboundTunnel> (
|
||||
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
|
||||
{
|
||||
|
@ -439,7 +440,7 @@ namespace tunnel
|
|||
CreateTunnel<InboundTunnel> (
|
||||
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
|
||||
{
|
||||
i2p::data::netdb.GetRandomNTCPRouter ()
|
||||
i2p::data::netdb.GetRandomRouter ()
|
||||
}));
|
||||
}
|
||||
else
|
||||
|
@ -447,11 +448,12 @@ namespace tunnel
|
|||
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
|
||||
LogPrint ("Creating two hops inbound tunnel...");
|
||||
auto router = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router;
|
||||
auto firstHop = i2p::data::netdb.GetRandomRouter (outboundTunnel->GetEndpointRouter ());
|
||||
CreateTunnel<InboundTunnel> (
|
||||
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
|
||||
{
|
||||
i2p::data::netdb.GetRandomRouter (outboundTunnel->GetEndpointRouter ()),
|
||||
router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomNTCPRouter () // last hop must be NTCP
|
||||
firstHop,
|
||||
router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomRouter (firstHop)
|
||||
}),
|
||||
outboundTunnel);
|
||||
}
|
||||
|
|
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;
|
||||
|
|
|
@ -147,7 +147,7 @@ namespace tunnel
|
|||
switch (msg.deliveryType)
|
||||
{
|
||||
case eDeliveryTypeLocal:
|
||||
i2p::HandleI2NPMessage (msg.data, true);
|
||||
i2p::HandleI2NPMessage (msg.data);
|
||||
break;
|
||||
case eDeliveryTypeTunnel:
|
||||
i2p::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
|
||||
|
|
|
@ -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
|
||||
|
|
37
i2p.cpp
37
i2p.cpp
|
@ -22,11 +22,13 @@
|
|||
#include "Tunnel.h"
|
||||
#include "NetDb.h"
|
||||
#include "HTTPServer.h"
|
||||
#include "Garlic.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
// Global
|
||||
int running = 1;
|
||||
volatile int running = 1;
|
||||
volatile int isDaemon;
|
||||
|
||||
#ifndef _WIN32
|
||||
void handle_signal(int sig)
|
||||
|
@ -59,6 +61,7 @@ void handle_signal(int sig)
|
|||
int main( int argc, char* argv[] )
|
||||
{
|
||||
i2p::util::config::OptionParser(argc,argv);
|
||||
volatile int isDaemon = i2p::util::config::GetArg("-daemon", 0);
|
||||
#ifdef _WIN32
|
||||
setlocale(LC_CTYPE, "");
|
||||
SetConsoleCP(1251);
|
||||
|
@ -71,8 +74,22 @@ int main( int argc, char* argv[] )
|
|||
LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string());
|
||||
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
|
||||
|
||||
volatile int isLogging = i2p::util::config::GetArg("-log", 0);
|
||||
if (isLogging == 1)
|
||||
{
|
||||
std::string logfile = i2p::util::filesystem::GetDataDir().string();
|
||||
#ifndef _WIN32
|
||||
if (i2p::util::config::GetArg("-daemon", 0) == 1)
|
||||
logfile.append("/debug.log");
|
||||
#else
|
||||
logfile.append("\\debug.log");
|
||||
#endif
|
||||
freopen(logfile.c_str(),"a",stdout);
|
||||
LogPrint("Logging to file enabled.");
|
||||
}
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
if (isDaemon == 1)
|
||||
{
|
||||
pid_t pid;
|
||||
pid = fork();
|
||||
|
@ -125,18 +142,6 @@ int main( int argc, char* argv[] )
|
|||
sigaction(SIGINT,&sa,0);
|
||||
#endif
|
||||
|
||||
if (i2p::util::config::GetArg("-log", 0) == 1)
|
||||
{
|
||||
std::string logfile = i2p::util::filesystem::GetDataDir().string();
|
||||
#ifndef _WIN32
|
||||
logfile.append("/debug.log");
|
||||
#else
|
||||
logfile.append("\\debug.log");
|
||||
#endif
|
||||
LogPrint("Logging to file enabled.");
|
||||
freopen(logfile.c_str(),"a",stdout);
|
||||
}
|
||||
|
||||
//TODO: This is an ugly workaround. fix it.
|
||||
//TODO: Autodetect public IP.
|
||||
i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"),
|
||||
|
@ -148,6 +153,7 @@ int main( int argc, char* argv[] )
|
|||
i2p::data::netdb.Start ();
|
||||
i2p::transports.Start ();
|
||||
i2p::tunnel::tunnels.Start ();
|
||||
i2p::garlic::routing.Start ();
|
||||
|
||||
while (running)
|
||||
{
|
||||
|
@ -156,12 +162,13 @@ int main( int argc, char* argv[] )
|
|||
}
|
||||
LogPrint("Shutdown started.");
|
||||
|
||||
i2p::garlic::routing.Stop ();
|
||||
i2p::tunnel::tunnels.Stop ();
|
||||
i2p::transports.Stop ();
|
||||
i2p::data::netdb.Stop ();
|
||||
httpServer.Stop ();
|
||||
|
||||
if (i2p::util::config::GetArg("-log", 0) == 1)
|
||||
if (isLogging == 1)
|
||||
{
|
||||
fclose (stdout);
|
||||
}
|
||||
|
|
10
util.cpp
10
util.cpp
|
@ -99,11 +99,12 @@ namespace filesystem
|
|||
{
|
||||
static boost::filesystem::path path;
|
||||
|
||||
if (i2p::util::config::mapArgs.count("-datadir")) {
|
||||
// TODO: datadir parameter is useless because GetDataDir is called before OptionParser
|
||||
// and mapArgs is not initialized yet
|
||||
/*if (i2p::util::config::mapArgs.count("-datadir"))
|
||||
path = boost::filesystem::system_complete(i2p::util::config::mapArgs["-datadir"]);
|
||||
} else {
|
||||
else */
|
||||
path = GetDefaultDataDir();
|
||||
}
|
||||
|
||||
if (!boost::filesystem::exists( path ))
|
||||
{
|
||||
|
@ -115,9 +116,8 @@ namespace filesystem
|
|||
return path;
|
||||
}
|
||||
}
|
||||
if (!boost::filesystem::is_directory(path)) {
|
||||
if (!boost::filesystem::is_directory(path))
|
||||
path = GetDefaultDataDir();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue