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. 34
      I2NPProtocol.cpp
  7. 40
      I2NPProtocol.h
  8. 16
      Identity.h
  9. 3
      Makefile
  10. 2
      NTCPSession.cpp
  11. 2
      NTCPSession.h
  12. 102
      NetDb.cpp
  13. 8
      NetDb.h
  14. 8
      Queue.h
  15. 78
      RouterContext.cpp
  16. 4
      RouterContext.h
  17. 79
      RouterInfo.cpp
  18. 23
      RouterInfo.h
  19. 505
      SSU.cpp
  20. 56
      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
{ {
namespace crypto 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; public:
CryptoPP::Integer y(key, 256), k(rnd, CryptoPP::Integer::One(), elgp-1);
ElGamalEncryption (const uint8_t * key):
if (zeroPadding) 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))
encrypted[0] = 0; {
encrypted[257] = 0; }
}
a_exp_b_mod_c (elgg, k, elgp).Encode (zeroPadding ? encrypted + 1 : encrypted, 256); void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false)
uint8_t m[255]; {
m[0] = 0xFF; // calculate b = b1*m mod p
memcpy (m+33, data, len); uint8_t m[255];
CryptoPP::SHA256().CalculateDigest(m+1, m+33, 222); m[0] = 0xFF;
a_times_b_mod_c (a_exp_b_mod_c (y, k, elgp), memcpy (m+33, data, len);
CryptoPP::Integer (m, 255), elgp).Encode (zeroPadding ? encrypted + 258 : encrypted + 256, 256); 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, inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
uint8_t * data, bool zeroPadding = false) uint8_t * data, bool zeroPadding = false)

72
Garlic.cpp

@ -2,7 +2,6 @@
#include "I2PEndian.h" #include "I2PEndian.h"
#include <map> #include <map>
#include <string> #include <string>
#include "ElGamal.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Tunnel.h" #include "Tunnel.h"
@ -14,7 +13,7 @@ namespace i2p
{ {
namespace garlic 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_Destination (destination), m_FirstMsgID (0), m_IsAcknowledged (false),
m_NumTags (numTags), m_NextTag (-1), m_SessionTags (0) m_NumTags (numTags), m_NextTag (-1), m_SessionTags (0)
{ {
@ -56,7 +55,7 @@ namespace garlic
m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); 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); m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv);
buf += 514; buf += 514;
len += 514; len += 514;
@ -140,7 +139,7 @@ namespace garlic
} }
if (msg) // clove message ifself if presented if (msg) // clove message ifself if presented
{ {
size += CreateGarlicClove (payload + size, msg, m_Destination->IsDestination ()); size += CreateGarlicClove (payload + size, msg, m_Destination.IsDestination ());
(*numCloves)++; (*numCloves)++;
} }
@ -161,7 +160,7 @@ namespace garlic
{ {
buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination
size++; size++;
memcpy (buf + size, m_Destination->GetIdentHash (), 32); memcpy (buf + size, m_Destination.GetIdentHash (), 32);
size += 32; size += 32;
} }
else else
@ -230,33 +229,31 @@ namespace garlic
m_Sessions.clear (); 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 ()) if (it != m_Sessions.end ())
{ {
delete it->second; delete it->second;
m_Sessions.erase (it); m_Sessions.erase (it);
} }
GarlicRoutingSession * session = new GarlicRoutingSession (destination, 0); // not follow-on messages expected 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); 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) 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; GarlicRoutingSession * session = nullptr;
if (it != m_Sessions.end ()) if (it != m_Sessions.end ())
session = it->second; session = it->second;
if (!session) if (!session)
{ {
session = new GarlicRoutingSession (destination, 4); // TODO: change it later 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); I2NPMessage * ret = session->WrapSingleMessage (msg, leaseSet);
@ -265,8 +262,14 @@ namespace garlic
return ret; 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); uint32_t length = be32toh (*(uint32_t *)buf);
buf += 4; buf += 4;
std::string sessionTag((const char *)buf, 32); std::string sessionTag((const char *)buf, 32);
@ -287,7 +290,7 @@ namespace garlic
// new session // new session
ElGamalBlock elGamal; ElGamalBlock elGamal;
if (i2p::crypto::ElGamalDecrypt ( if (i2p::crypto::ElGamalDecrypt (
isFromTunnel ? i2p::context.GetLeaseSetPrivateKey () : i2p::context.GetPrivateKey (), msg->from ? i2p::context.GetLeaseSetPrivateKey () : i2p::context.GetPrivateKey (),
buf, (uint8_t *)&elGamal, true)) buf, (uint8_t *)&elGamal, true))
{ {
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
@ -299,7 +302,7 @@ namespace garlic
else else
LogPrint ("Failed to decrypt garlic"); LogPrint ("Failed to decrypt garlic");
} }
DeleteI2NPMessage (msg);
} }
void GarlicRouting::HandleAESBlock (uint8_t * buf, size_t len, uint8_t * sessionKey) void GarlicRouting::HandleAESBlock (uint8_t * buf, size_t len, uint8_t * sessionKey)
@ -354,7 +357,7 @@ namespace garlic
{ {
case eGarlicDeliveryTypeLocal: case eGarlicDeliveryTypeLocal:
LogPrint ("Garlic type local"); LogPrint ("Garlic type local");
i2p::HandleI2NPMessage (buf, len, false); i2p::HandleI2NPMessage (buf, len);
break; break;
case eGarlicDeliveryTypeDestination: case eGarlicDeliveryTypeDestination:
{ {
@ -413,5 +416,40 @@ namespace garlic
LogPrint ("Garlic message ", be32toh (msg->msgID), " acknowledged"); 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 @@
#include <inttypes.h> #include <inttypes.h>
#include <map> #include <map>
#include <string> #include <string>
#include <thread>
#include <cryptopp/modes.h> #include <cryptopp/modes.h>
#include <cryptopp/aes.h> #include <cryptopp/aes.h>
#include <cryptopp/osrng.h> #include <cryptopp/osrng.h>
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "LeaseSet.h" #include "LeaseSet.h"
#include "Tunnel.h"
#include "Queue.h"
namespace i2p namespace i2p
{ {
@ -37,7 +40,7 @@ namespace garlic
{ {
public: public:
GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags); GarlicRoutingSession (const i2p::data::RoutingDestination& destination, int numTags);
~GarlicRoutingSession (); ~GarlicRoutingSession ();
I2NPMessage * WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet); I2NPMessage * WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet);
int GetNextTag () const { return m_NextTag; }; int GetNextTag () const { return m_NextTag; };
@ -57,7 +60,7 @@ namespace garlic
private: private:
const i2p::data::RoutingDestination * m_Destination; const i2p::data::RoutingDestination& m_Destination;
uint8_t m_SessionKey[32]; uint8_t m_SessionKey[32];
uint32_t m_FirstMsgID; // first message ID uint32_t m_FirstMsgID; // first message ID
bool m_IsAcknowledged; bool m_IsAcknowledged;
@ -75,20 +78,28 @@ namespace garlic
GarlicRouting (); GarlicRouting ();
~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); void HandleDeliveryStatusMessage (uint8_t * buf, size_t len);
I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * msg); I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg);
I2NPMessage * WrapMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * WrapMessage (const i2p::data::RoutingDestination& destination,
I2NPMessage * msg, I2NPMessage * leaseSet = nullptr); I2NPMessage * msg, I2NPMessage * leaseSet = nullptr);
private: private:
void Run ();
void ProcessGarlicMessage (I2NPMessage * msg);
void HandleAESBlock (uint8_t * buf, size_t len, uint8_t * sessionKey); void HandleAESBlock (uint8_t * buf, size_t len, uint8_t * sessionKey);
void HandleGarlicPayload (uint8_t * buf, size_t len); void HandleGarlicPayload (uint8_t * buf, size_t len);
private: private:
bool m_IsRunning;
std::thread * m_Thread;
i2p::util::Queue<I2NPMessage> m_Queue;
// outgoing sessions // outgoing sessions
std::map<i2p::data::IdentHash, GarlicRoutingSession *> m_Sessions; std::map<i2p::data::IdentHash, GarlicRoutingSession *> m_Sessions;
std::map<uint32_t, GarlicRoutingSession *> m_CreatedSessions; // msgID -> session std::map<uint32_t, GarlicRoutingSession *> m_CreatedSessions; // msgID -> session

54
HTTPServer.cpp

@ -60,9 +60,20 @@ namespace util
{ {
m_Buffer[bytes_transferred] = 0; m_Buffer[bytes_transferred] = 0;
auto address = ExtractAddress (); auto address = ExtractAddress ();
LogPrint (address);
if (address.length () > 1) // not just '/' 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 else
HandleRequest (); HandleRequest ();
boost::asio::async_write (*m_Socket, m_Reply.to_buffers(), boost::asio::async_write (*m_Socket, m_Reply.to_buffers(),
@ -133,6 +144,7 @@ namespace util
} }
s << "<P>Transports</P>"; s << "<P>Transports</P>";
s << "NTCP<BR>";
for (auto it: i2p::transports.GetNTCPSessions ()) for (auto it: i2p::transports.GetNTCPSessions ())
{ {
// RouterInfo of incoming connection doesn't have address // RouterInfo of incoming connection doesn't have address
@ -146,17 +158,36 @@ namespace util
s << "<BR>"; 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>"; 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]; 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); auto leaseSet = i2p::data::netdb.FindLeaseSet (destination);
if (!leaseSet || !leaseSet->HasNonExpiredLeases ()) 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 std::this_thread::sleep_for (std::chrono::seconds(10)); // wait for 10 seconds
leaseSet = i2p::data::netdb.FindLeaseSet (destination); leaseSet = i2p::data::netdb.FindLeaseSet (destination);
if (!leaseSet || !leaseSet->HasNonExpiredLeases ()) // still no LeaseSet if (!leaseSet || !leaseSet->HasNonExpiredLeases ()) // still no LeaseSet
@ -170,17 +201,10 @@ namespace util
return; return;
} }
} }
// we found LeaseSet auto s = i2p::stream::CreateStream (*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);
if (s) 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); s->Send ((uint8_t *)request.c_str (), request.length (), 10);
std::stringstream ss; std::stringstream ss;
uint8_t buf[8192]; uint8_t buf[8192];
@ -200,7 +224,7 @@ namespace util
else // nothing received else // nothing received
ss << "<html>Not responding</html>"; ss << "<html>Not responding</html>";
s->Close (); s->Close ();
//DeleteStream (s); DeleteStream (s);
m_Reply.content = ss.str (); m_Reply.content = ss.str ();
m_Reply.headers.resize(2); m_Reply.headers.resize(2);

2
HTTPServer.h

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

34
I2NPProtocol.cpp

@ -22,6 +22,7 @@ namespace i2p
I2NPMessage * msg = new I2NPMessage; I2NPMessage * msg = new I2NPMessage;
msg->offset = 2; // reserve 2 bytes for NTCP header, should reserve more for SSU in future msg->offset = 2; // reserve 2 bytes for NTCP header, should reserve more for SSU in future
msg->len = sizeof (I2NPHeader) + 2; msg->len = sizeof (I2NPHeader) + 2;
msg->from = nullptr;
return msg; return msg;
} }
@ -70,8 +71,16 @@ namespace i2p
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID) I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
{ {
I2NPDeliveryStatusMsg msg; I2NPDeliveryStatusMsg msg;
msg.msgID = htobe32 (msgID); if (msgID)
msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ()); {
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)); return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg));
} }
@ -212,7 +221,7 @@ namespace i2p
const I2NPBuildRequestRecordClearText& clearText, const I2NPBuildRequestRecordClearText& clearText,
I2NPBuildRequestRecordElGamalEncrypted& record) 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); 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); 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); i2p::tunnel::TransitTunnel * tunnel = i2p::tunnel::tunnels.GetTransitTunnel (tunnelID);
if (tunnel) if (tunnel)
tunnel->SendTunnelDataMsg (nullptr, 0, msg); tunnel->SendTunnelDataMsg (msg);
else else
{ {
LogPrint ("Tunnel ", (unsigned int)tunnelID, " not found"); LogPrint ("Tunnel ", (unsigned int)tunnelID, " not found");
@ -406,7 +415,7 @@ namespace i2p
return be16toh (header->size) + sizeof (I2NPHeader); 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; I2NPHeader * header = (I2NPHeader *)msg;
uint32_t msgID = be32toh (header->msgID); uint32_t msgID = be32toh (header->msgID);
@ -415,12 +424,7 @@ namespace i2p
uint8_t * buf = msg + sizeof (I2NPHeader); uint8_t * buf = msg + sizeof (I2NPHeader);
int size = be16toh (header->size); int size = be16toh (header->size);
switch (header->typeID) switch (header->typeID)
{ {
case eI2NPGarlic:
LogPrint ("Garlic");
i2p::garlic::routing.HandleGarlicMessage (buf, size, isFromTunnel);
break;
break;
case eI2NPDeliveryStatus: case eI2NPDeliveryStatus:
LogPrint ("DeliveryStatus"); LogPrint ("DeliveryStatus");
// we assume DeliveryStatusMessage is sent with garlic only // 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) if (msg)
{ {
@ -465,8 +469,12 @@ namespace i2p
LogPrint ("DatabaseSearchReply"); LogPrint ("DatabaseSearchReply");
i2p::data::netdb.PostI2NPMsg (msg); i2p::data::netdb.PostI2NPMsg (msg);
break; break;
case eI2NPGarlic:
LogPrint ("Garlic");
i2p::garlic::routing.HandleGarlicMessage (msg);
break;
default: default:
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength (), isFromTunnel); HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
DeleteI2NPMessage (msg); DeleteI2NPMessage (msg);
} }
} }

40
I2NPProtocol.h

@ -4,6 +4,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <set> #include <set>
#include <string.h> #include <string.h>
#include "I2PEndian.h"
#include "RouterInfo.h" #include "RouterInfo.h"
namespace i2p namespace i2p
@ -19,6 +20,12 @@ namespace i2p
uint8_t chks; uint8_t chks;
}; };
struct I2NPHeaderShort
{
uint8_t typeID;
uint32_t shortExpiration;
};
struct I2NPDatabaseStoreMsg struct I2NPDatabaseStoreMsg
{ {
uint8_t key[32]; uint8_t key[32];
@ -84,11 +91,17 @@ namespace i2p
eI2NPVariableTunnelBuildReply = 24 eI2NPVariableTunnelBuildReply = 24
}; };
namespace tunnel
{
class InboundTunnel;
}
const int NTCP_MAX_MESSAGE_SIZE = 16384; const int NTCP_MAX_MESSAGE_SIZE = 16384;
struct I2NPMessage struct I2NPMessage
{ {
uint8_t buf[NTCP_MAX_MESSAGE_SIZE]; uint8_t buf[NTCP_MAX_MESSAGE_SIZE];
size_t len, offset; size_t len, offset;
i2p::tunnel::InboundTunnel * from;
I2NPHeader * GetHeader () { return (I2NPHeader *)(buf + offset); }; I2NPHeader * GetHeader () { return (I2NPHeader *)(buf + offset); };
uint8_t * GetPayload () { return buf + offset + sizeof(I2NPHeader); }; uint8_t * GetPayload () { return buf + offset + sizeof(I2NPHeader); };
@ -99,8 +112,31 @@ namespace i2p
{ {
memcpy (buf + offset, other.buf + other.offset, other.GetLength ()); memcpy (buf + offset, other.buf + other.offset, other.GetLength ());
len = offset + other.GetLength (); len = offset + other.GetLength ();
from = other.from;
return *this; 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 (); I2NPMessage * NewI2NPMessage ();
void DeleteI2NPMessage (I2NPMessage * msg); void DeleteI2NPMessage (I2NPMessage * msg);
@ -140,8 +176,8 @@ namespace i2p
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg); I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg);
size_t GetI2NPMessageLength (uint8_t * msg); size_t GetI2NPMessageLength (uint8_t * msg);
void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel); void HandleI2NPMessage (uint8_t * msg, size_t len);
void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel); void HandleI2NPMessage (I2NPMessage * msg);
} }
#endif #endif

16
Identity.h

@ -3,6 +3,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <string.h>
#include "ElGamal.h"
namespace i2p namespace i2p
{ {
@ -84,9 +85,24 @@ namespace data
class RoutingDestination class RoutingDestination
{ {
public: public:
RoutingDestination (): m_ElGamalEncryption (nullptr) {};
virtual ~RoutingDestination () { delete m_ElGamalEncryption; };
virtual const IdentHash& GetIdentHash () const = 0; virtual const IdentHash& GetIdentHash () const = 0;
virtual const uint8_t * GetEncryptionPublicKey () const = 0; virtual const uint8_t * GetEncryptionPublicKey () const = 0;
virtual bool IsDestination () const = 0; // for garlic 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
OBJECTS = obj/i2p.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transports.o \ 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/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/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 = INCFLAGS =
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS = LIBS =

2
NTCPSession.cpp

@ -424,7 +424,7 @@ namespace ntcp
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
{ {
// we have a complete I2NP message // we have a complete I2NP message
i2p::HandleI2NPMessage (m_NextMessage, false); i2p::HandleI2NPMessage (m_NextMessage);
m_NextMessage = nullptr; m_NextMessage = nullptr;
} }
} }

2
NTCPSession.h

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

102
NetDb.cpp

@ -25,7 +25,7 @@ namespace data
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination, I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, &m_ExcludedPeers); replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, &m_ExcludedPeers);
if (m_IsLeaseSet) // wrap lookup message into garlic 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_ExcludedPeers.insert (router->GetIdentHash ());
m_LastRouter = router; m_LastRouter = router;
m_LastReplyTunnel = replyTunnel; m_LastReplyTunnel = replyTunnel;
@ -90,7 +90,7 @@ namespace data
void NetDb::Run () void NetDb::Run ()
{ {
uint32_t lastTs = 0; uint32_t lastSave = 0, lastPublish = 0;
m_IsRunning = true; m_IsRunning = true;
while (m_IsRunning) while (m_IsRunning)
{ {
@ -111,7 +111,7 @@ namespace data
else // WTF? else // WTF?
{ {
LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID); LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID);
i2p::HandleI2NPMessage (msg, false); i2p::HandleI2NPMessage (msg);
} }
msg = m_Queue.Get (); msg = m_Queue.Get ();
} }
@ -120,11 +120,19 @@ namespace data
Explore (); Explore ();
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); 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); SaveUpdated (m_NetDbPath);
lastTs = ts; ValidateSubscriptions ();
}
lastSave = ts;
}
if (ts - lastPublish >= 600) // publish every 10 minutes
{
Publish ();
lastPublish = ts;
} }
} }
catch (std::exception& ex) catch (std::exception& ex)
@ -448,10 +456,18 @@ namespace data
{ {
auto r = FindRouter (router); auto r = FindRouter (router);
// do we have that floodfill router in our database? // 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 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 // request destination
auto msg = dest->CreateRequestMessage (r, dest->GetLastReplyTunnel ()); auto msg = dest->CreateRequestMessage (r, dest->GetLastReplyTunnel ());
msgs.push_back (i2p::tunnel::TunnelMessageBlock msgs.push_back (i2p::tunnel::TunnelMessageBlock
@ -478,7 +494,10 @@ namespace data
else // we should send directly else // we should send directly
{ {
if (!dest->IsLeaseSet ()) // if not LeaseSet 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 else
LogPrint ("Can't request LeaseSet"); LogPrint ("Can't request LeaseSet");
} }
@ -506,16 +525,15 @@ namespace data
auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel (); auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (outbound && inbound) 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) if (floodfill)
{ {
LogPrint ("Exploring new routers ..."); 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; std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
msgs.push_back (i2p::tunnel::TunnelMessageBlock msgs.push_back (i2p::tunnel::TunnelMessageBlock
{ {
@ -529,11 +547,24 @@ namespace data
floodfill->GetIdentHash (), 0, floodfill->GetIdentHash (), 0,
dest->CreateRequestMessage (floodfill, inbound) // explore 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, RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest,
bool isLeaseSet, bool isExploratory) bool isLeaseSet, bool isExploratory)
{ {
@ -557,6 +588,15 @@ namespace data
m_RequestedDestinations.erase (it); 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 const RouterInfo * NetDb::GetRandomNTCPRouter (bool floodfillOnly) const
{ {
@ -626,5 +666,33 @@ namespace data
return r; 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

@ -63,7 +63,9 @@ namespace data
void AddLeaseSet (uint8_t * buf, int len); void AddLeaseSet (uint8_t * buf, int len);
RouterInfo * FindRouter (const IdentHash& ident) const; RouterInfo * FindRouter (const IdentHash& ident) const;
LeaseSet * FindLeaseSet (const IdentHash& destination) 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 char * b32); // in base32
void RequestDestination (const IdentHash& destination, bool isLeaseSet = false); void RequestDestination (const IdentHash& destination, bool isLeaseSet = false);
@ -82,17 +84,21 @@ namespace data
void SaveUpdated (const char * directory); void SaveUpdated (const char * directory);
void Run (); // exploratory thread void Run (); // exploratory thread
void Explore (); void Explore ();
void Publish ();
void ValidateSubscriptions ();
const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const; const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
RequestedDestination * CreateRequestedDestination (const IdentHash& dest, RequestedDestination * CreateRequestedDestination (const IdentHash& dest,
bool isLeaseSet, bool isExploratory = false); bool isLeaseSet, bool isExploratory = false);
void DeleteRequestedDestination (const IdentHash& dest); void DeleteRequestedDestination (const IdentHash& dest);
void DeleteRequestedDestination (RequestedDestination * dest);
private: private:
std::map<IdentHash, LeaseSet *> m_LeaseSets; std::map<IdentHash, LeaseSet *> m_LeaseSets;
std::map<IdentHash, RouterInfo *> m_RouterInfos; std::map<IdentHash, RouterInfo *> m_RouterInfos;
std::map<IdentHash, RequestedDestination *> m_RequestedDestinations; std::map<IdentHash, RequestedDestination *> m_RequestedDestinations;
std::set<IdentHash> m_Subscriptions;
bool m_IsRunning; bool m_IsRunning;
int m_ReseedRetries; int m_ReseedRetries;

8
Queue.h

@ -98,17 +98,18 @@ namespace util
{ {
public: public:
MsgQueue (): m_Thread (std::bind (&MsgQueue<Msg>::Run, this)) {}; MsgQueue (): m_Thread (std::bind (&MsgQueue<Msg>::Run, this)) , running(1) {};
void Stop() void Stop()
{ {
m_Thread.detach(); running = 0;
m_Thread.join();
} }
private: private:
void Run () void Run ()
{ {
Msg * msg = nullptr; Msg * msg = nullptr;
while ((msg = Queue<Msg>::GetNext ()) != nullptr) while ((msg = Queue<Msg>::GetNext ()) != nullptr && running)
{ {
msg->Process (); msg->Process ();
delete msg; delete msg;
@ -117,6 +118,7 @@ namespace util
private: private:
std::thread m_Thread; std::thread m_Thread;
volatile int running;
}; };
} }
} }

78
RouterContext.cpp

@ -25,25 +25,32 @@ namespace i2p
m_Keys = i2p::data::CreateRandomKeys (); m_Keys = i2p::data::CreateRandomKeys ();
m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
CryptoPP::Integer (m_Keys.signingPrivateKey, 20)); CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
UpdateRouterInfo ();
}
void RouterContext::UpdateRouterInfo ()
{
i2p::data::Identity ident; i2p::data::Identity ident;
ident = m_Keys; ident = m_Keys;
m_RouterInfo.SetRouterIdentity (ident);
m_RouterInfo.AddNTCPAddress ("127.0.0.1", 17007); // TODO: i2p::data::RouterInfo routerInfo;
m_RouterInfo.SetProperty ("caps", "LR"); routerInfo.SetRouterIdentity (ident);
m_RouterInfo.SetProperty ("coreVersion", "0.9.8.1"); routerInfo.AddSSUAddress ("127.0.0.1", 17007, routerInfo.GetIdentHash ());
m_RouterInfo.SetProperty ("netId", "2"); routerInfo.AddNTCPAddress ("127.0.0.1", 17007); // TODO:
m_RouterInfo.SetProperty ("router.version", "0.9.8.1"); routerInfo.SetProperty ("caps", "LR");
m_RouterInfo.SetProperty ("start_uptime", "90m"); routerInfo.SetProperty ("coreVersion", "0.9.8.1");
routerInfo.SetProperty ("netId", "2");
m_RouterInfo.CreateBuffer (); 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) void RouterContext::OverrideNTCPAddress (const char * host, int port)
{ {
m_RouterInfo.CreateBuffer (); m_RouterInfo.CreateBuffer ();
auto address = m_RouterInfo.GetNTCPAddress (); auto address = const_cast<i2p::data::RouterInfo::Address *>(m_RouterInfo.GetNTCPAddress ());
if (address) if (address)
{ {
address->host = boost::asio::ip::address::from_string (host); address->host = boost::asio::ip::address::from_string (host);
@ -51,6 +58,14 @@ namespace i2p
} }
m_RouterInfo.CreateBuffer (); 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) void RouterContext::Sign (uint8_t * buf, int len, uint8_t * signature)
@ -61,24 +76,49 @@ namespace i2p
bool RouterContext::Load () 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; if (!fk.is_open ()) return false;
fk.read ((char *)&m_Keys, sizeof (m_Keys)); fk.read ((char *)&m_Keys, sizeof (m_Keys));
m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
CryptoPP::Integer (m_Keys.signingPrivateKey, 20)); 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; return true;
} }
void RouterContext::Save () void RouterContext::Save (bool infoOnly)
{ {
std::ofstream fk (ROUTER_KEYS, std::ofstream::binary | std::ofstream::out); std::string dataDir = i2p::util::filesystem::GetDataDir ().string ();
fk.write ((char *)&m_Keys, sizeof (m_Keys)); #ifndef _WIN32
dataDir.append ("/");
std::ofstream fi (ROUTER_INFO, std::ofstream::binary | std::ofstream::out); #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 ()); fi.write ((char *)m_RouterInfo.GetBuffer (), m_RouterInfo.GetBufferLen ());
} }
} }

4
RouterContext.h

@ -28,12 +28,14 @@ namespace i2p
void Sign (uint8_t * buf, int len, uint8_t * signature); void Sign (uint8_t * buf, int len, uint8_t * signature);
void OverrideNTCPAddress (const char * host, int port); // temporary void OverrideNTCPAddress (const char * host, int port); // temporary
void UpdateAddress (const char * host); // called from SSU
private: private:
void CreateNewRouter (); void CreateNewRouter ();
void UpdateRouterInfo ();
bool Load (); bool Load ();
void Save (); void Save (bool infoOnly = false);
private: private:

79
RouterInfo.cpp

@ -129,6 +129,27 @@ namespace data
address.port = boost::lexical_cast<int>(value); address.port = boost::lexical_cast<int>(value);
else if (!strcmp (key, "key")) else if (!strcmp (key, "key"))
Base64ToByteStream (value, strlen (value), address.key, 32); 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); m_Addresses.push_back(address);
} }
@ -159,6 +180,9 @@ namespace data
CryptoPP::SHA256().CalculateDigest(m_IdentHash, (uint8_t *)&m_RouterIdentity, sizeof (m_RouterIdentity)); CryptoPP::SHA256().CalculateDigest(m_IdentHash, (uint8_t *)&m_RouterIdentity, sizeof (m_RouterIdentity));
UpdateIdentHashBase64 (); UpdateIdentHashBase64 ();
UpdateRoutingKey (); UpdateRoutingKey ();
if (!m_SupportedTransports)
SetUnreachable (true);
} }
void RouterInfo::UpdateIdentHashBase64 () void RouterInfo::UpdateIdentHashBase64 ()
@ -187,18 +211,36 @@ namespace data
{ {
s.write ((char *)&address.cost, sizeof (address.cost)); s.write ((char *)&address.cost, sizeof (address.cost));
s.write ((char *)&address.date, sizeof (address.date)); s.write ((char *)&address.date, sizeof (address.date));
std::stringstream properties;
if (address.transportStyle == eTransportNTCP) if (address.transportStyle == eTransportNTCP)
WriteString ("NTCP", s); WriteString ("NTCP", s);
else if (address.transportStyle == eTransportSSU) else if (address.transportStyle == eTransportSSU)
{
WriteString ("SSU", s); WriteString ("SSU", s);
// caps
WriteString ("caps", properties);
properties << '=';
WriteString ("B", properties); // TODO: should be 'BC' for introducers
properties << ';';
}
else else
WriteString ("", s); WriteString ("", s);
std::stringstream properties;
WriteString ("host", properties); WriteString ("host", properties);
properties << '='; properties << '=';
WriteString (address.host.to_string (), properties); WriteString (address.host.to_string (), properties);
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); WriteString ("port", properties);
properties << '='; properties << '=';
WriteString (boost::lexical_cast<std::string>(address.port), properties); WriteString (boost::lexical_cast<std::string>(address.port), properties);
@ -264,8 +306,22 @@ namespace data
addr.cost = 2; addr.cost = 2;
addr.date = 0; addr.date = 0;
m_Addresses.push_back(addr); 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) void RouterInfo::SetProperty (const char * key, const char * value)
{ {
m_Properties[key] = value; m_Properties[key] = value;
@ -294,18 +350,33 @@ namespace data
else else
return m_SupportedTransports & (eNTCPV4 | eNTCPV6); 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); return GetAddress (eTransportNTCP, v4only);
} }
RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) const RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) const
{ {
return GetAddress (eTransportSSU, v4only); 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) for (auto& address : m_Addresses)
{ {

23
RouterInfo.h

@ -32,6 +32,14 @@ namespace data
eTransportSSU eTransportSSU
}; };
struct Introducer
{
boost::asio::ip::address iHost;
int iPort;
uint8_t iKey[32];
uint32_t iTag;
};
struct Address struct Address
{ {
TransportStyle transportStyle; TransportStyle transportStyle;
@ -39,7 +47,9 @@ namespace data
int port; int port;
uint64_t date; uint64_t date;
uint8_t cost; 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); RouterInfo (const char * filename);
@ -53,17 +63,20 @@ namespace data
const char * GetIdentHashBase64 () const { return m_IdentHashBase64; }; const char * GetIdentHashBase64 () const { return m_IdentHashBase64; };
const char * GetIdentHashAbbreviation () const { return m_IdentHashAbbreviation; }; const char * GetIdentHashAbbreviation () const { return m_IdentHashAbbreviation; };
uint64_t GetTimestamp () const { return m_Timestamp; }; uint64_t GetTimestamp () const { return m_Timestamp; };
const std::vector<Address>& GetAddresses () const { return m_Addresses; }; std::vector<Address>& GetAddresses () { return m_Addresses; };
Address * GetNTCPAddress (bool v4only = true); const Address * GetNTCPAddress (bool v4only = true) const;
Address * GetSSUAddress (bool v4only = true); const Address * GetSSUAddress (bool v4only = true) const;
const RoutingKey& GetRoutingKey () const { return m_RoutingKey; }; const RoutingKey& GetRoutingKey () const { return m_RoutingKey; };
void AddNTCPAddress (const char * host, int port); 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); void SetProperty (const char * key, const char * value);
const char * GetProperty (const char * key) const; const char * GetProperty (const char * key) const;
bool IsFloodfill () const; bool IsFloodfill () const;
bool IsNTCP (bool v4only = true) 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 IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool UsesIntroducer () const;
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };
bool IsUnreachable () const { return m_IsUnreachable; }; bool IsUnreachable () const { return m_IsUnreachable; };
@ -90,7 +103,7 @@ namespace data
size_t ReadString (char * str, std::istream& s); size_t ReadString (char * str, std::istream& s);
void WriteString (const std::string& str, std::ostream& s); void WriteString (const std::string& str, std::ostream& s);
void UpdateIdentHashBase64 (); void UpdateIdentHashBase64 ();
Address * GetAddress (TransportStyle s, bool v4only); const Address * GetAddress (TransportStyle s, bool v4only) const;
private: private:

505
SSU.cpp

@ -14,8 +14,8 @@ namespace i2p
namespace ssu namespace ssu
{ {
SSUSession::SSUSession (SSUServer * server, const boost::asio::ip::udp::endpoint& remoteEndpoint, SSUSession::SSUSession (SSUServer * server, boost::asio::ip::udp::endpoint& remoteEndpoint,
i2p::data::RouterInfo * router): m_Server (server), m_RemoteEndpoint (remoteEndpoint), const i2p::data::RouterInfo * router): m_Server (server), m_RemoteEndpoint (remoteEndpoint),
m_RemoteRouter (router), m_State (eSessionStateUnknown) m_RemoteRouter (router), m_State (eSessionStateUnknown)
{ {
} }
@ -47,7 +47,8 @@ namespace ssu
{ {
switch (m_State) switch (m_State)
{ {
case eSessionStateEstablised: case eSessionStateConfirmedSent:
case eSessionStateEstablished:
// most common case // most common case
ProcessMessage (buf, len); ProcessMessage (buf, len);
break; break;
@ -64,6 +65,21 @@ namespace ssu
// session confirmed // session confirmed
ProcessSessionConfirmed (buf, len); ProcessSessionConfirmed (buf, len);
break; 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: default:
LogPrint ("SSU state not implemented yet"); LogPrint ("SSU state not implemented yet");
} }
@ -85,24 +101,43 @@ namespace ssu
case PAYLOAD_TYPE_TEST: case PAYLOAD_TYPE_TEST:
LogPrint ("SSU test received"); LogPrint ("SSU test received");
break; break;
case PAYLOAD_TYPE_SESSION_DESTROY: case PAYLOAD_TYPE_SESSION_DESTROYED:
{
LogPrint ("SSU session destroy received"); 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: default:
LogPrint ("Unexpected SSU payload type ", (int)payloadType); LogPrint ("Unexpected SSU payload type ", (int)payloadType);
} }
} }
// TODO: try intro key as well
else 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) void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
LogPrint ("Process session request"); LogPrint ("Process session request");
// use our intro key // use our intro key
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, buf, len))
i2p::context.GetRouterInfo (), buf, len))
{ {
m_State = eSessionStateRequestReceived; m_State = eSessionStateRequestReceived;
LogPrint ("Session request received"); LogPrint ("Session request received");
@ -121,17 +156,42 @@ namespace ssu
} }
// use remote intro key // 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; m_State = eSessionStateCreatedReceived;
LogPrint ("Session created received"); LogPrint ("Session created received");
uint8_t * ourAddress = buf + sizeof (SSUHeader) + 257; uint8_t signedData[532]; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time
boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )(ourAddress))); uint8_t * payload = buf + sizeof (SSUHeader);
uint16_t ourPort = be16toh (*(uint16_t *)(ourAddress + 4)); 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); LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort);
uint32_t relayTag = be32toh (*(uint32_t *)(buf + sizeof (SSUHeader) + 263)); i2p::context.UpdateAddress (ourIP.to_string ().c_str ());
SendSessionConfirmed (buf + sizeof (SSUHeader), ourAddress, relayTag); *(uint32_t *)(signedData + 518) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ()); // remote IP
m_State = eSessionStateEstablised; *(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) if ((header->flag >> 4) == PAYLOAD_TYPE_SESSION_CONFIRMED)
{ {
m_State = eSessionStateConfirmedReceived; m_State = eSessionStateConfirmedReceived;
LogPrint ("Session confirmed received"); LogPrint ("Session confirmed received");
// TODO: m_State = eSessionStateEstablished;
m_State = eSessionStateEstablised; SendI2NPMessage (CreateDeliveryStatusMsg (0));
Established ();
} }
else else
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4)); LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
@ -158,10 +219,10 @@ namespace ssu
void SSUSession::SendSessionRequest () void SSUSession::SendSessionRequest ()
{ {
auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr; auto introKey = GetIntroKey ();
if (!address) if (!introKey)
{ {
LogPrint ("Missing remote SSU address"); LogPrint ("SSU is not supported");
return; return;
} }
@ -174,18 +235,50 @@ namespace ssu
uint8_t iv[16]; uint8_t iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv 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_State = eSessionStateRequestSent;
m_Server->Send (buf, 304, m_RemoteEndpoint); 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) 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; return;
} }
uint8_t signedData[532]; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time 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 ()); *(uint16_t *)(payload) = htobe16 (m_RemoteEndpoint.port ());
payload += 2; payload += 2;
memcpy (signedData + 512, payload - 6, 6); // remote endpoint IP and port 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 *(uint32_t *)(signedData + 518) = htobe32 (address->host.to_v4 ().to_ulong ()); // our IP
*(uint16_t *)(signedData + 522) = htobe16 (m_Server->GetEndpoint ().port ()); // our port *(uint16_t *)(signedData + 522) = htobe16 (address->port); // our port
*(uint32_t *)(payload) = 0; // relay tag, always 0 for now *(uint32_t *)(payload) = 0; // relay tag, always 0 for now
payload += 4; payload += 4;
*(uint32_t *)(payload) = htobe32 (i2p::util::GetSecondsSinceEpoch ()); // signed on time *(uint32_t *)(payload) = htobe32 (i2p::util::GetSecondsSinceEpoch ()); // signed on time
@ -221,20 +314,13 @@ namespace ssu
m_Encryption.ProcessData (payload, payload, 48); m_Encryption.ProcessData (payload, payload, 48);
// encrypt message with intro key // encrypt message with intro key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, address->key, iv, address->key); FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, introKey, iv, introKey);
m_State = eSessionStateRequestSent; m_State = eSessionStateCreatedSent;
m_Server->Send (buf, 368, m_RemoteEndpoint); m_Server->Send (buf, 368, m_RemoteEndpoint);
} }
void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag) 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 buf[480 + 18];
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
*payload = 1; // 1 fragment *payload = 1; // 1 fragment
@ -272,15 +358,56 @@ namespace ssu
m_Server->Send (buf, 480, m_RemoteEndpoint); 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 (); LogPrint ("Process relay response");
if (address) 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 // 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; SSUHeader * header = (SSUHeader *)buf;
if ((header->flag >> 4) == expectedPayloadType) if ((header->flag >> 4) == expectedPayloadType)
{ {
@ -291,14 +418,15 @@ namespace ssu
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4)); LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
} }
else else
LogPrint ("MAC verifcation failed"); LogPrint ("MAC verification failed");
} }
else else
LogPrint ("SSU is not supported by ", r.GetIdentHashAbbreviation ()); LogPrint ("SSU is not supported");
return false; 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)) if (len < sizeof (SSUHeader))
{ {
@ -319,7 +447,7 @@ namespace ssu
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac); 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)) if (len < sizeof (SSUHeader))
{ {
@ -334,7 +462,7 @@ namespace ssu
m_Decryption.ProcessData (encrypted, encrypted, encryptedLen); 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)) if (len < sizeof (SSUHeader))
{ {
@ -357,17 +485,88 @@ namespace ssu
SendSessionRequest (); 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) void SSUSession::ProcessData (uint8_t * buf, size_t len)
{ {
//uint8_t * start = buf; //uint8_t * start = buf;
uint8_t flag = *buf; uint8_t flag = *buf;
buf++; buf++;
LogPrint ("Process SSU data flags=", (int)flag); 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 uint8_t numFragments = *buf; // number of fragments
buf++; buf++;
for (int i = 0; i < numFragments; i++) for (int i = 0; i < numFragments; i++)
@ -382,14 +581,157 @@ namespace ssu
uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13 uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
bool isLast = fragmentInfo & 0x010000; // bit 16 bool isLast = fragmentInfo & 0x010000; // bit 16
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17 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; 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): SSUServer::SSUServer (boost::asio::io_service& service, int port):
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (service, m_Endpoint) 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 () SSUServer::~SSUServer ()
@ -405,12 +747,14 @@ namespace ssu
void SSUServer::Stop () void SSUServer::Stop ()
{ {
DeleteAllSessions ();
m_Socket.close (); m_Socket.close ();
} }
void SSUServer::Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to) 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); m_Socket.send_to (boost::asio::buffer (buf, len), to);
LogPrint ("SSU sent ", len, " bytes");
} }
void SSUServer::Receive () void SSUServer::Receive ()
@ -441,7 +785,7 @@ namespace ssu
LogPrint ("SSU receive error: ", ecode.message ()); LogPrint ("SSU receive error: ", ecode.message ());
} }
SSUSession * SSUServer::GetSession (i2p::data::RouterInfo * router) SSUSession * SSUServer::GetSession (const i2p::data::RouterInfo * router)
{ {
SSUSession * session = nullptr; SSUSession * session = nullptr;
if (router) if (router)
@ -455,12 +799,27 @@ namespace ssu
session = it->second; session = it->second;
else else
{ {
// otherwise create new session // otherwise create new session
session = new SSUSession (this, remoteEndpoint, router); if (!router->UsesIntroducer ())
m_Sessions[remoteEndpoint] = session; {
LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (), "] ", // connect directly
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port (), " created"); session = new SSUSession (this, remoteEndpoint, router);
session->Connect (); 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 else
@ -468,6 +827,38 @@ namespace ssu
} }
return session; 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

@ -3,6 +3,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <map> #include <map>
#include <list>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <cryptopp/modes.h> #include <cryptopp/modes.h>
#include <cryptopp/aes.h> #include <cryptopp/aes.h>
@ -35,7 +36,15 @@ namespace ssu
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5; const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
const uint8_t PAYLOAD_TYPE_DATA = 6; const uint8_t PAYLOAD_TYPE_DATA = 6;
const uint8_t PAYLOAD_TYPE_TEST = 7; 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 enum SessionState
{ {
@ -46,7 +55,10 @@ namespace ssu
eSessionStateCreatedReceived, eSessionStateCreatedReceived,
eSessionStateConfirmedSent, eSessionStateConfirmedSent,
eSessionStateConfirmedReceived, eSessionStateConfirmedReceived,
eSessionStateEstablised eSessionRelayRequestSent,
eSessionRelayRequestReceived,
eSessionRelayResponseReceived,
eSessionStateEstablished
}; };
class SSUServer; class SSUServer;
@ -54,13 +66,17 @@ namespace ssu
{ {
public: public:
SSUSession (SSUServer * server, const boost::asio::ip::udp::endpoint& remoteEndpoint, SSUSession (SSUServer * server, boost::asio::ip::udp::endpoint& remoteEndpoint,
i2p::data::RouterInfo * router = nullptr); const i2p::data::RouterInfo * router = nullptr);
void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void Connect (); 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); void SendI2NPMessage (I2NPMessage * msg);
private: private:
void CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey); 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 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 ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendSessionRequest (); void SendSessionRequest ();
void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer);
void ProcessSessionCreated (uint8_t * buf, size_t len); void ProcessSessionCreated (uint8_t * buf, size_t len);
void SendSessionCreated (const uint8_t * x); void SendSessionCreated (const uint8_t * x);
void ProcessSessionConfirmed (uint8_t * buf, size_t len); void ProcessSessionConfirmed (uint8_t * buf, size_t len);
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag); 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 ProcessData (uint8_t * buf, size_t len);
void SendMsgAck (uint32_t msgID);
bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, i2p::data::RouterInfo& r, uint8_t * buf, size_t len); void SendSesionDestroyed ();
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, uint8_t * aesKey, uint8_t * iv, uint8_t * macKey); void Send (i2p::I2NPMessage * msg);
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: private:
SSUServer * m_Server; SSUServer * m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint; boost::asio::ip::udp::endpoint m_RemoteEndpoint;
i2p::data::RouterInfo * m_RemoteRouter; const i2p::data::RouterInfo * m_RemoteRouter;
SessionState m_State; SessionState m_State;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption; CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption; CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption;
uint8_t m_SessionKey[32], m_MacKey[32]; uint8_t m_SessionKey[32], m_MacKey[32];
std::map<uint32_t, I2NPMessage *> m_IncomleteMessages;
std::list<i2p::I2NPMessage *> m_DelayedMessages;
}; };
class SSUServer class SSUServer
@ -98,10 +123,13 @@ namespace ssu
~SSUServer (); ~SSUServer ();
void Start (); void Start ();
void Stop (); 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; }; 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 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: private:
@ -115,6 +143,10 @@ namespace ssu
boost::asio::ip::udp::endpoint m_SenderEndpoint; boost::asio::ip::udp::endpoint m_SenderEndpoint;
uint8_t m_ReceiveBuffer[2*SSU_MTU]; uint8_t m_ReceiveBuffer[2*SSU_MTU];
std::map<boost::asio::ip::udp::endpoint, SSUSession *> m_Sessions; 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
{ {
namespace stream 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_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (0), m_IsOpen (false),
m_LocalDestination (local), m_RemoteLeaseSet (remote), m_OutboundTunnel (nullptr) m_LocalDestination (local), m_RemoteLeaseSet (remote), m_OutboundTunnel (nullptr)
{ {
@ -172,7 +172,7 @@ namespace stream
if (!m_OutboundTunnel) if (!m_OutboundTunnel)
m_OutboundTunnel = i2p::tunnel::tunnels.GetNextOutboundTunnel (); m_OutboundTunnel = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (); auto leases = m_RemoteLeaseSet.GetNonExpiredLeases ();
if (m_OutboundTunnel && !leases.empty ()) if (m_OutboundTunnel && !leases.empty ())
{ {
auto& lease = *leases.begin (); // TODO: auto& lease = *leases.begin (); // TODO:
@ -206,7 +206,7 @@ namespace stream
CreateDataMessage (this, packet, size)); CreateDataMessage (this, packet, size));
if (m_OutboundTunnel) if (m_OutboundTunnel)
{ {
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (); auto leases = m_RemoteLeaseSet.GetNonExpiredLeases ();
if (!leases.empty ()) if (!leases.empty ())
{ {
auto& lease = *leases.begin (); // TODO: auto& lease = *leases.begin (); // TODO:
@ -252,7 +252,7 @@ namespace stream
I2NPMessage * msg = i2p::garlic::routing.WrapSingleMessage (m_RemoteLeaseSet, I2NPMessage * msg = i2p::garlic::routing.WrapSingleMessage (m_RemoteLeaseSet,
CreateDataMessage (this, packet, size)); CreateDataMessage (this, packet, size));
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (); auto leases = m_RemoteLeaseSet.GetNonExpiredLeases ();
if (m_OutboundTunnel && !leases.empty ()) if (m_OutboundTunnel && !leases.empty ())
{ {
auto& lease = *leases.begin (); // TODO: auto& lease = *leases.begin (); // TODO:
@ -318,7 +318,7 @@ namespace stream
void StreamingDestination::HandleNextPacket (Packet * packet) void StreamingDestination::HandleNextPacket (Packet * packet)
{ {
uint32_t sendStreamID = be32toh (*(uint32_t *)(packet->buf)); uint32_t sendStreamID = packet->GetSendStreamID ();
auto it = m_Streams.find (sendStreamID); auto it = m_Streams.find (sendStreamID);
if (it != m_Streams.end ()) if (it != m_Streams.end ())
it->second->HandleNextPacket (packet); 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); Stream * s = new Stream (this, remote);
m_Streams[s->GetRecvStreamID ()] = s; m_Streams[s->GetRecvStreamID ()] = s;
@ -399,7 +399,7 @@ namespace stream
signer.SignMessage (i2p::context.GetRandomNumberGenerator (), buf, len, signature); signer.SignMessage (i2p::context.GetRandomNumberGenerator (), buf, len, signature);
} }
Stream * CreateStream (const i2p::data::LeaseSet * remote) Stream * CreateStream (const i2p::data::LeaseSet& remote)
{ {
if (!sharedLocalDestination) if (!sharedLocalDestination)
sharedLocalDestination = new StreamingDestination (); sharedLocalDestination = new StreamingDestination ();

10
Streaming.h

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

9
TransitTunnel.cpp

@ -47,15 +47,18 @@ namespace tunnel
m_NumTransmittedBytes += tunnelMsg->GetLength (); 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); LogPrint ("We are not a gateway for transit tunnel ", m_TunnelID);
i2p::DeleteI2NPMessage (msg); 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) void TransitTunnelEndpoint::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)

4
TransitTunnel.h

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

54
Transports.cpp

@ -25,6 +25,7 @@ namespace i2p
{ {
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this)); m_Thread = new std::thread (std::bind (&Transports::Run, this));
m_Timer = new boost::asio::deadline_timer (m_Service);
// create acceptors // create acceptors
auto addresses = context.GetRouterInfo ().GetAddresses (); auto addresses = context.GetRouterInfo ().GetAddresses ();
for (auto& address : addresses) for (auto& address : addresses)
@ -46,6 +47,7 @@ namespace i2p
m_SSUServer = new i2p::ssu::SSUServer (m_Service, address.port); m_SSUServer = new i2p::ssu::SSUServer (m_Service, address.port);
LogPrint ("Start listening UDP port ", address.port); LogPrint ("Start listening UDP port ", address.port);
m_SSUServer->Start (); m_SSUServer->Start ();
DetectExternalIP ();
} }
else else
LogPrint ("SSU server already exists"); LogPrint ("SSU server already exists");
@ -60,6 +62,12 @@ namespace i2p
m_NTCPSessions.clear (); m_NTCPSessions.clear ();
delete m_NTCPAcceptor; delete m_NTCPAcceptor;
if (m_Timer)
{
m_Timer->cancel ();
delete m_Timer;
}
if (m_SSUServer) if (m_SSUServer)
{ {
m_SSUServer->Stop (); m_SSUServer->Stop ();
@ -140,7 +148,7 @@ namespace i2p
{ {
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ()) if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
// we send it to ourself // we send it to ourself
i2p::HandleI2NPMessage (msg, false); i2p::HandleI2NPMessage (msg);
else else
m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg)); m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg));
} }
@ -160,7 +168,23 @@ namespace i2p
AddNTCPSession (session); AddNTCPSession (session);
} }
else 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 else
{ {
@ -173,4 +197,30 @@ namespace i2p
else else
LogPrint ("Session not found"); 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

@ -38,6 +38,9 @@ namespace i2p
void Run (); void Run ();
void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error); void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error);
void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg); void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void DetectExternalIP ();
void HandleTimer (const boost::system::error_code& ecode);
private: private:
@ -49,11 +52,13 @@ namespace i2p
std::map<i2p::data::IdentHash, i2p::ntcp::NTCPSession *> m_NTCPSessions; std::map<i2p::data::IdentHash, i2p::ntcp::NTCPSession *> m_NTCPSessions;
i2p::ssu::SSUServer * m_SSUServer; i2p::ssu::SSUServer * m_SSUServer;
boost::asio::deadline_timer * m_Timer;
public: public:
// for HTTP only // for HTTP only
const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; }; const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; };
const i2p::ssu::SSUServer * GetSSUServer () const { return m_SSUServer; };
}; };
extern Transports transports; extern Transports transports;

46
Tunnel.cpp

@ -130,34 +130,35 @@ namespace tunnel
void InboundTunnel::HandleTunnelDataMsg (I2NPMessage * msg) void InboundTunnel::HandleTunnelDataMsg (I2NPMessage * msg)
{ {
msg->from = this;
EncryptTunnelMsg (msg); EncryptTunnelMsg (msg);
m_Endpoint.HandleDecryptedTunnelDataMsg (msg); m_Endpoint.HandleDecryptedTunnelDataMsg (msg);
} }
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * 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) void OutboundTunnel::SendTunnelDataMsg (std::vector<TunnelMessageBlock> msgs)
{ {
for (auto& it : msgs) for (auto& it : msgs)
{ m_Gateway.PutTunnelDataMsg (it);
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.SendBuffer (); m_Gateway.SendBuffer ();
} }
@ -389,7 +390,7 @@ namespace tunnel
CreateTunnel<OutboundTunnel> ( CreateTunnel<OutboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *> new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{ {
i2p::data::netdb.GetRandomNTCPRouter () i2p::data::netdb.GetRandomRouter ()
}, },
inboundTunnel->GetTunnelConfig ())); inboundTunnel->GetTunnelConfig ()));
} }
@ -397,7 +398,7 @@ namespace tunnel
{ {
LogPrint ("Creating two hops outbound 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> ( CreateTunnel<OutboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *> new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{ {
@ -439,7 +440,7 @@ namespace tunnel
CreateTunnel<InboundTunnel> ( CreateTunnel<InboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *> new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{ {
i2p::data::netdb.GetRandomNTCPRouter () i2p::data::netdb.GetRandomRouter ()
})); }));
} }
else else
@ -447,11 +448,12 @@ namespace tunnel
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel (); OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
LogPrint ("Creating two hops inbound tunnel..."); LogPrint ("Creating two hops inbound tunnel...");
auto router = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router; auto router = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router;
auto firstHop = i2p::data::netdb.GetRandomRouter (outboundTunnel->GetEndpointRouter ());
CreateTunnel<InboundTunnel> ( CreateTunnel<InboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *> new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{ {
i2p::data::netdb.GetRandomRouter (outboundTunnel->GetEndpointRouter ()), firstHop,
router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomNTCPRouter () // last hop must be NTCP router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomRouter (firstHop)
}), }),
outboundTunnel); outboundTunnel);
} }

2
Tunnel.h

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

2
TunnelEndpoint.cpp

@ -147,7 +147,7 @@ namespace tunnel
switch (msg.deliveryType) switch (msg.deliveryType)
{ {
case eDeliveryTypeLocal: case eDeliveryTypeLocal:
i2p::HandleI2NPMessage (msg.data, true); i2p::HandleI2NPMessage (msg.data);
break; break;
case eDeliveryTypeTunnel: case eDeliveryTypeTunnel:
i2p::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); i2p::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));

27
TunnelGateway.cpp

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

6
TunnelGateway.h

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

68
UPnP.cpp

@ -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 @@
#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
const uint64_t IPAD = 0x3636363636363636; const uint64_t IPAD = 0x3636363636363636;
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C; 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 // key is 32 bytes
// digest is 16 bytes // digest is 16 bytes
// block size is 64 bytes // block size is 64 bytes

37
i2p.cpp

@ -22,11 +22,13 @@
#include "Tunnel.h" #include "Tunnel.h"
#include "NetDb.h" #include "NetDb.h"
#include "HTTPServer.h" #include "HTTPServer.h"
#include "Garlic.h"
#include "util.h" #include "util.h"
// Global // Global
int running = 1; volatile int running = 1;
volatile int isDaemon;
#ifndef _WIN32 #ifndef _WIN32
void handle_signal(int sig) void handle_signal(int sig)
@ -59,6 +61,7 @@ void handle_signal(int sig)
int main( int argc, char* argv[] ) int main( int argc, char* argv[] )
{ {
i2p::util::config::OptionParser(argc,argv); i2p::util::config::OptionParser(argc,argv);
volatile int isDaemon = i2p::util::config::GetArg("-daemon", 0);
#ifdef _WIN32 #ifdef _WIN32
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
SetConsoleCP(1251); SetConsoleCP(1251);
@ -71,8 +74,22 @@ int main( int argc, char* argv[] )
LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string()); LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string());
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs); 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 #ifndef _WIN32
if (i2p::util::config::GetArg("-daemon", 0) == 1) if (isDaemon == 1)
{ {
pid_t pid; pid_t pid;
pid = fork(); pid = fork();
@ -125,18 +142,6 @@ int main( int argc, char* argv[] )
sigaction(SIGINT,&sa,0); sigaction(SIGINT,&sa,0);
#endif #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: This is an ugly workaround. fix it.
//TODO: Autodetect public IP. //TODO: Autodetect public IP.
i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"), 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::data::netdb.Start ();
i2p::transports.Start (); i2p::transports.Start ();
i2p::tunnel::tunnels.Start (); i2p::tunnel::tunnels.Start ();
i2p::garlic::routing.Start ();
while (running) while (running)
{ {
@ -156,12 +162,13 @@ int main( int argc, char* argv[] )
} }
LogPrint("Shutdown started."); LogPrint("Shutdown started.");
i2p::garlic::routing.Stop ();
i2p::tunnel::tunnels.Stop (); i2p::tunnel::tunnels.Stop ();
i2p::transports.Stop (); i2p::transports.Stop ();
i2p::data::netdb.Stop (); i2p::data::netdb.Stop ();
httpServer.Stop (); httpServer.Stop ();
if (i2p::util::config::GetArg("-log", 0) == 1) if (isLogging == 1)
{ {
fclose (stdout); fclose (stdout);
} }

10
util.cpp

@ -99,11 +99,12 @@ namespace filesystem
{ {
static boost::filesystem::path path; 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"]); path = boost::filesystem::system_complete(i2p::util::config::mapArgs["-datadir"]);
} else { else */
path = GetDefaultDataDir(); path = GetDefaultDataDir();
}
if (!boost::filesystem::exists( path )) if (!boost::filesystem::exists( path ))
{ {
@ -115,9 +116,8 @@ namespace filesystem
return path; return path;
} }
} }
if (!boost::filesystem::is_directory(path)) { if (!boost::filesystem::is_directory(path))
path = GetDefaultDataDir(); path = GetDefaultDataDir();
}
return path; return path;
} }

Loading…
Cancel
Save