Browse Source

Merge pull request #12 from orignal/master

Merge pull request from orignal/master
pull/45/head
chertov 11 years ago
parent
commit
05f0a72a0f
  1. 58
      ElGamal.h
  2. 72
      Garlic.cpp
  3. 21
      Garlic.h
  4. 54
      HTTPServer.cpp
  5. 2
      HTTPServer.h
  6. 32
      I2NPProtocol.cpp
  7. 40
      I2NPProtocol.h
  8. 16
      Identity.h
  9. 3
      Makefile
  10. 2
      NTCPSession.cpp
  11. 2
      NTCPSession.h
  12. 96
      NetDb.cpp
  13. 6
      NetDb.h
  14. 8
      Queue.h
  15. 72
      RouterContext.cpp
  16. 4
      RouterContext.h
  17. 79
      RouterInfo.cpp
  18. 23
      RouterInfo.h
  19. 495
      SSU.cpp
  20. 52
      SSU.h
  21. 14
      Streaming.cpp
  22. 10
      Streaming.h
  23. 9
      TransitTunnel.cpp
  24. 4
      TransitTunnel.h
  25. 54
      Transports.cpp
  26. 5
      Transports.h
  27. 46
      Tunnel.cpp
  28. 2
      Tunnel.h
  29. 2
      TunnelEndpoint.cpp
  30. 27
      TunnelGateway.cpp
  31. 6
      TunnelGateway.h
  32. 68
      UPnP.cpp
  33. 41
      UPnP.h
  34. 2
      hmac.h
  35. 37
      i2p.cpp
  36. 10
      util.cpp

58
ElGamal.h

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

72
Garlic.cpp

@ -2,7 +2,6 @@ @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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

@ -4,11 +4,14 @@ @@ -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 @@ -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 @@ -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 @@ -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

54
HTTPServer.cpp

@ -60,9 +60,20 @@ namespace util @@ -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 @@ -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 @@ -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 @@ -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 @@ -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);

2
HTTPServer.h

@ -48,7 +48,7 @@ namespace util @@ -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 ();

32
I2NPProtocol.cpp

@ -22,6 +22,7 @@ namespace i2p @@ -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 @@ -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 @@ -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 @@ -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 @@ -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);
@ -416,11 +425,6 @@ namespace i2p @@ -416,11 +425,6 @@ namespace i2p
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 @@ -443,7 +447,7 @@ namespace i2p
}
}
void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel)
void HandleI2NPMessage (I2NPMessage * msg)
{
if (msg)
{
@ -465,8 +469,12 @@ namespace i2p @@ -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);
}
}

40
I2NPProtocol.h

@ -4,6 +4,7 @@ @@ -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 @@ -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 @@ -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 @@ -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 @@ -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

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
#include <inttypes.h>
#include <string.h>
#include "ElGamal.h"
namespace i2p
{
@ -84,9 +85,24 @@ namespace data @@ -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

@ -4,7 +4,8 @@ CFLAGS = -g -Wall -std=c++0x @@ -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 =

2
NTCPSession.cpp

@ -424,7 +424,7 @@ namespace ntcp @@ -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;
}
}

2
NTCPSession.h

@ -60,7 +60,7 @@ namespace ntcp @@ -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:

96
NetDb.cpp

@ -25,7 +25,7 @@ namespace data @@ -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 @@ -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 @@ -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 @@ -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)
@ -450,8 +458,16 @@ namespace data @@ -450,8 +458,16 @@ namespace data
// do we have that floodfill router in our database?
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 @@ -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 @@ -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
{
@ -531,6 +549,19 @@ namespace data @@ -531,6 +549,19 @@ namespace data
});
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 ());
}
}
@ -558,6 +589,15 @@ namespace data @@ -558,6 +589,15 @@ namespace data
}
}
void NetDb::DeleteRequestedDestination (RequestedDestination * dest)
{
if (dest)
{
m_RequestedDestinations.erase (dest->GetDestination ());
delete dest;
}
}
const RouterInfo * NetDb::GetRandomNTCPRouter (bool floodfillOnly) const
{
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
@ -626,5 +666,33 @@ namespace data @@ -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);
}
}
}
}
}

6
NetDb.h

@ -63,6 +63,8 @@ namespace data @@ -63,6 +63,8 @@ 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 @@ -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

@ -98,17 +98,18 @@ namespace util @@ -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 @@ -117,6 +118,7 @@ namespace util
private:
std::thread m_Thread;
volatile int running;
};
}
}

72
RouterContext.cpp

@ -25,25 +25,32 @@ namespace i2p @@ -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));
UpdateRouterInfo ();
}
void RouterContext::UpdateRouterInfo ()
{
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 ();
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 @@ -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 @@ -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::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, std::ofstream::binary | std::ofstream::out);
std::ofstream fi (router_info.c_str (), std::ofstream::binary | std::ofstream::out);
fi.write ((char *)m_RouterInfo.GetBuffer (), m_RouterInfo.GetBufferLen ());
}
}

4
RouterContext.h

@ -28,12 +28,14 @@ namespace i2p @@ -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:

79
RouterInfo.cpp

@ -129,6 +129,27 @@ namespace data @@ -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 @@ -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 @@ -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,6 +306,20 @@ namespace data @@ -264,6 +306,20 @@ 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)
@ -295,17 +351,32 @@ namespace data @@ -295,17 +351,32 @@ namespace data
return m_SupportedTransports & (eNTCPV4 | eNTCPV6);
}
RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only)
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 ();
}
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

@ -32,6 +32,14 @@ namespace data @@ -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 @@ -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 @@ -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 @@ -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:

495
SSU.cpp

@ -14,8 +14,8 @@ namespace i2p @@ -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 @@ -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 @@ -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 @@ -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");
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 @@ -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);
}
}
@ -146,8 +206,9 @@ namespace ssu @@ -146,8 +206,9 @@ namespace ssu
{
m_State = eSessionStateConfirmedReceived;
LogPrint ("Session confirmed received");
// TODO:
m_State = eSessionStateEstablised;
m_State = eSessionStateEstablished;
SendI2NPMessage (CreateDeliveryStatusMsg (0));
Established ();
}
else
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
@ -158,10 +219,10 @@ namespace ssu @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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,9 +485,58 @@ namespace ssu @@ -357,9 +485,58 @@ namespace ssu
SendSessionRequest ();
}
void SSUSession::ConnectThroughIntroducer (const i2p::data::RouterInfo::Introducer& introducer)
{
SendRelayRequest (introducer);
}
void SSUSession::Close ()
{
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)
{
// TODO:
if (msg)
{
if (m_State == eSessionStateEstablished)
Send (msg);
else
m_DelayedMessages.push_back (msg);
}
}
void SSUSession::ProcessData (uint8_t * buf, size_t len)
@ -368,6 +545,28 @@ namespace ssu @@ -368,6 +545,28 @@ namespace ssu
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++)
@ -383,13 +582,156 @@ namespace ssu @@ -383,13 +582,156 @@ namespace ssu
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");
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 @@ -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 @@ -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)
@ -456,11 +800,26 @@ namespace ssu @@ -456,11 +800,26 @@ namespace ssu
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 ();
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 @@ -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 ());
}
}
}
}

52
SSU.h

@ -3,6 +3,7 @@ @@ -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 @@ -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 @@ -46,7 +55,10 @@ namespace ssu
eSessionStateCreatedReceived,
eSessionStateConfirmedSent,
eSessionStateConfirmedReceived,
eSessionStateEstablised
eSessionRelayRequestSent,
eSessionRelayRequestReceived,
eSessionRelayResponseReceived,
eSessionStateEstablished
};
class SSUServer;
@ -54,11 +66,15 @@ namespace ssu @@ -54,11 +66,15 @@ 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:
@ -68,26 +84,35 @@ namespace ssu @@ -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);
void SendMsgAck (uint32_t msgID);
void SendSesionDestroyed ();
void Send (i2p::I2NPMessage * msg);
bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, i2p::data::RouterInfo& r, uint8_t * buf, size_t len);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, uint8_t * aesKey, uint8_t * iv, uint8_t * macKey);
void Decrypt (uint8_t * buf, size_t len, uint8_t * aesKey);
bool Validate (uint8_t * buf, size_t len, uint8_t * macKey);
bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey);
void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey);
bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey);
const uint8_t * GetIntroKey () const;
private:
SSUServer * m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
i2p::data::RouterInfo * m_RemoteRouter;
const i2p::data::RouterInfo * m_RemoteRouter;
SessionState m_State;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption;
uint8_t m_SessionKey[32], m_MacKey[32];
std::map<uint32_t, I2NPMessage *> m_IncomleteMessages;
std::list<i2p::I2NPMessage *> m_DelayedMessages;
};
class SSUServer
@ -98,10 +123,13 @@ namespace ssu @@ -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 @@ -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
Streaming.cpp

@ -14,7 +14,7 @@ namespace i2p @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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

@ -65,11 +65,11 @@ namespace stream @@ -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 @@ -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 @@ -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 @@ -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

9
TransitTunnel.cpp

@ -47,15 +47,18 @@ namespace tunnel @@ -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)

4
TransitTunnel.h

@ -22,7 +22,7 @@ namespace tunnel @@ -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 @@ -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:

54
Transports.cpp

@ -25,6 +25,7 @@ namespace i2p @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 ();
}
}
}

5
Transports.h

@ -39,6 +39,9 @@ namespace i2p @@ -39,6 +39,9 @@ namespace i2p
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:
bool m_IsRunning;
@ -49,11 +52,13 @@ namespace i2p @@ -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

@ -130,34 +130,35 @@ namespace tunnel @@ -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 @@ -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 @@ -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 @@ -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 @@ -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

@ -21,7 +21,7 @@ namespace i2p @@ -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;

2
TunnelEndpoint.cpp

@ -147,7 +147,7 @@ namespace tunnel @@ -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));

27
TunnelGateway.cpp

@ -10,7 +10,7 @@ namespace i2p @@ -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 @@ -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 @@ -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 @@ -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 ()

6
TunnelGateway.h

@ -15,7 +15,7 @@ namespace tunnel @@ -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 @@ -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

@ -0,0 +1,68 @@ @@ -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

@ -0,0 +1,41 @@ @@ -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

@ -13,7 +13,7 @@ namespace crypto @@ -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

@ -22,11 +22,13 @@ @@ -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) @@ -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[] ) @@ -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
logfile.append("/debug.log");
#else
logfile.append("\\debug.log");
#endif
freopen(logfile.c_str(),"a",stdout);
LogPrint("Logging to file enabled.");
}
#ifndef _WIN32
if (i2p::util::config::GetArg("-daemon", 0) == 1)
if (isDaemon == 1)
{
pid_t pid;
pid = fork();
@ -125,18 +142,6 @@ int main( int argc, char* argv[] ) @@ -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[] ) @@ -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[] ) @@ -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

@ -99,11 +99,12 @@ namespace filesystem @@ -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 @@ -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…
Cancel
Save