mirror of
https://github.com/PurpleI2P/i2pd
synced 2024-11-10 08:00:38 +03:00
Merge pull request #28 from orignal/master
Merge pull request from orignal/master
This commit is contained in:
commit
b6b9c61217
@ -80,7 +80,7 @@ void AddressBook::LoadHosts ()
|
||||
getline(f, s);
|
||||
|
||||
if (!s.length())
|
||||
break;
|
||||
continue; // skip empty line
|
||||
|
||||
size_t pos = s.find('=');
|
||||
|
||||
@ -90,8 +90,11 @@ void AddressBook::LoadHosts ()
|
||||
std::string addr = s.substr(pos);
|
||||
|
||||
Identity ident;
|
||||
Base64ToByteStream (addr.c_str(), addr.length(), (uint8_t *)&ident, sizeof (ident));
|
||||
m_Addresses[name] = CalculateIdentHash (ident);
|
||||
if (!ident.FromBase64(addr)) {
|
||||
LogPrint ("hosts.txt: ignore ", name);
|
||||
continue;
|
||||
}
|
||||
m_Addresses[name] = ident.Hash();
|
||||
numAddresses++;
|
||||
}
|
||||
}
|
||||
|
21
Identity.cpp
21
Identity.cpp
@ -6,6 +6,7 @@
|
||||
#include <cryptopp/dsa.h>
|
||||
#include "CryptoConst.h"
|
||||
#include "Identity.h"
|
||||
#include "base64.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@ -19,6 +20,19 @@ namespace data
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Identity::FromBase64 (const std::string& s)
|
||||
{
|
||||
size_t count = Base64ToByteStream (s.c_str(), s.length(), reinterpret_cast<uint8_t*> (this), sizeof (Identity));
|
||||
return count == sizeof(Identity);
|
||||
}
|
||||
|
||||
IdentHash Identity::Hash()
|
||||
{
|
||||
IdentHash hash;
|
||||
CryptoPP::SHA256().CalculateDigest(reinterpret_cast<uint8_t*>(&hash), reinterpret_cast<uint8_t*> (this), sizeof (Identity));
|
||||
return hash;
|
||||
}
|
||||
|
||||
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
|
||||
{
|
||||
pub = keys;
|
||||
@ -26,13 +40,6 @@ namespace data
|
||||
return *this;
|
||||
}
|
||||
|
||||
IdentHash CalculateIdentHash (const Identity& identity)
|
||||
{
|
||||
IdentHash hash;
|
||||
CryptoPP::SHA256().CalculateDigest((uint8_t *)hash, (uint8_t *)&identity, sizeof (Identity));
|
||||
return hash;
|
||||
}
|
||||
|
||||
Keys CreateRandomKeys ()
|
||||
{
|
||||
Keys keys;
|
||||
|
@ -9,6 +9,8 @@ namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
class IdentHash;
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
struct DHKeysPair // transient keys for transport sessions
|
||||
@ -32,6 +34,8 @@ namespace data
|
||||
uint8_t certificate[3];
|
||||
|
||||
Identity& operator=(const Keys& keys);
|
||||
bool FromBase64(const std::string&);
|
||||
IdentHash Hash();
|
||||
};
|
||||
|
||||
struct PrivateKeys // for eepsites
|
||||
@ -75,7 +79,6 @@ namespace data
|
||||
uint8_t m_Hash[32];
|
||||
};
|
||||
|
||||
IdentHash CalculateIdentHash (const Identity& identity);
|
||||
Keys CreateRandomKeys ();
|
||||
void CreateRandomDHKeysPair (DHKeysPair * keys); // for transport sessions
|
||||
|
||||
@ -103,7 +106,7 @@ namespace data
|
||||
public:
|
||||
|
||||
RoutingDestination (): m_ElGamalEncryption (nullptr) {};
|
||||
virtual ~RoutingDestination () { delete m_ElGamalEncryption; };
|
||||
virtual ~RoutingDestination () { if (m_ElGamalEncryption) delete m_ElGamalEncryption; };
|
||||
|
||||
virtual const IdentHash& GetIdentHash () const = 0;
|
||||
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
|
||||
@ -125,6 +128,7 @@ namespace data
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~LocalDestination() {};
|
||||
virtual const IdentHash& GetIdentHash () const = 0;
|
||||
virtual const uint8_t * GetEncryptionPrivateKey () const = 0;
|
||||
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
|
||||
|
@ -25,7 +25,7 @@ namespace data
|
||||
|
||||
const H * header = (const H *)buf;
|
||||
m_Identity = header->destination;
|
||||
m_IdentHash = CalculateIdentHash (m_Identity);
|
||||
m_IdentHash = m_Identity.Hash();
|
||||
memcpy (m_EncryptionKey, header->encryptionKey, 256);
|
||||
LogPrint ("LeaseSet num=", (int)header->num);
|
||||
|
||||
|
@ -519,7 +519,7 @@ namespace ntcp
|
||||
void NTCPSession::ScheduleTermination ()
|
||||
{
|
||||
m_TerminationTimer.cancel ();
|
||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(TERMINATION_TIMEOUT));
|
||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_TIMEOUT));
|
||||
m_TerminationTimer.async_wait (boost::bind (&NTCPSession::HandleTerminationTimer,
|
||||
this, boost::asio::placeholders::error));
|
||||
}
|
||||
@ -528,7 +528,7 @@ namespace ntcp
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint ("No activity fo ", TERMINATION_TIMEOUT, " seconds");
|
||||
LogPrint ("No activity fo ", NTCP_TERMINATION_TIMEOUT, " seconds");
|
||||
m_Socket.close ();
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ namespace ntcp
|
||||
|
||||
#pragma pack()
|
||||
|
||||
const int TERMINATION_TIMEOUT = 120; // 2 minutes
|
||||
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
|
||||
class NTCPSession
|
||||
{
|
||||
public:
|
||||
|
@ -34,7 +34,7 @@ namespace data
|
||||
void RouterInfo::SetRouterIdentity (const Identity& identity)
|
||||
{
|
||||
m_RouterIdentity = identity;
|
||||
m_IdentHash = CalculateIdentHash (m_RouterIdentity);
|
||||
m_IdentHash = m_RouterIdentity.Hash ();
|
||||
UpdateIdentHashBase64 ();
|
||||
UpdateRoutingKey ();
|
||||
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
@ -129,6 +129,8 @@ namespace data
|
||||
address.port = boost::lexical_cast<int>(value);
|
||||
else if (!strcmp (key, "key"))
|
||||
Base64ToByteStream (value, strlen (value), address.key, 32);
|
||||
else if (!strcmp (key, "caps"))
|
||||
ExtractCaps (value);
|
||||
else if (key[0] == 'i')
|
||||
{
|
||||
// introducers
|
||||
@ -191,7 +193,6 @@ namespace data
|
||||
|
||||
void RouterInfo::ExtractCaps (const char * value)
|
||||
{
|
||||
m_Caps = 0;
|
||||
const char * cap = value;
|
||||
while (*cap)
|
||||
{
|
||||
@ -208,6 +209,12 @@ namespace data
|
||||
case 'R':
|
||||
m_Caps |= Caps::eReachable;
|
||||
break;
|
||||
case 'B':
|
||||
m_Caps |= Caps::eSSUTesting;
|
||||
break;
|
||||
case 'C':
|
||||
m_Caps |= Caps::eSSUIntroducer;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
cap++;
|
||||
@ -249,7 +256,10 @@ namespace data
|
||||
// caps
|
||||
WriteString ("caps", properties);
|
||||
properties << '=';
|
||||
WriteString ("B", properties); // TODO: should be 'BC' for introducers
|
||||
std::string caps;
|
||||
if (IsPeerTesting ()) caps += 'B';
|
||||
if (IsIntroducer ()) caps += 'C';
|
||||
WriteString (caps, properties);
|
||||
properties << ';';
|
||||
}
|
||||
else
|
||||
@ -344,11 +354,12 @@ namespace data
|
||||
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.cost = 10; // NTCP should have priority over SSU
|
||||
addr.date = 0;
|
||||
memcpy (addr.key, key, 32);
|
||||
m_Addresses.push_back(addr);
|
||||
m_SupportedTransports |= eSSUV4;
|
||||
m_Caps |= eSSUTesting; // TODO
|
||||
}
|
||||
|
||||
void RouterInfo::SetProperty (const char * key, const char * value)
|
||||
|
@ -29,7 +29,9 @@ namespace data
|
||||
{
|
||||
eFloodfill = 0x01,
|
||||
eHighBandwidth = 0x02,
|
||||
eReachable = 0x04
|
||||
eReachable = 0x04,
|
||||
eSSUTesting = 0x08,
|
||||
eSSUIntroducer = 0x10
|
||||
};
|
||||
|
||||
enum TransportStyle
|
||||
@ -84,6 +86,8 @@ namespace data
|
||||
bool IsSSU (bool v4only = true) const;
|
||||
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
|
||||
bool UsesIntroducer () const;
|
||||
bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
|
||||
bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
|
||||
uint8_t GetCaps () const { return m_Caps; };
|
||||
|
||||
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };
|
||||
|
274
SSU.cpp
274
SSU.cpp
@ -16,8 +16,10 @@ namespace ssu
|
||||
{
|
||||
|
||||
SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
|
||||
const i2p::data::RouterInfo * router): m_Server (server), m_RemoteEndpoint (remoteEndpoint),
|
||||
m_RemoteRouter (router), m_Timer (m_Server.GetService ()), m_State (eSessionStateUnknown)
|
||||
const i2p::data::RouterInfo * router, bool peerTest ):
|
||||
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_RemoteRouter (router),
|
||||
m_Timer (m_Server.GetService ()), m_PeerTest (peerTest), m_State (eSessionStateUnknown),
|
||||
m_RelayTag (0)
|
||||
{
|
||||
m_DHKeysPair = i2p::transports.GetNextDHKeysPair ();
|
||||
}
|
||||
@ -57,16 +59,14 @@ namespace ssu
|
||||
case eSessionStateConfirmedSent:
|
||||
case eSessionStateEstablished:
|
||||
// most common case
|
||||
ProcessMessage (buf, len);
|
||||
ScheduleTermination ();
|
||||
ProcessMessage (buf, len, senderEndpoint);
|
||||
break;
|
||||
// establishing
|
||||
// establishing or testing
|
||||
case eSessionStateUnknown:
|
||||
// session request
|
||||
ProcessSessionRequest (buf, len, senderEndpoint);
|
||||
break;
|
||||
case eSessionStateRequestSent:
|
||||
// session created
|
||||
ProcessSessionCreated (buf, len);
|
||||
// we must use intro key
|
||||
ProcessIntroKeyMessage (buf, len, senderEndpoint);
|
||||
break;
|
||||
case eSessionStateCreatedSent:
|
||||
// session confirmed
|
||||
@ -92,21 +92,21 @@ namespace ssu
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::ProcessMessage (uint8_t * buf, size_t len)
|
||||
void SSUSession::ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
|
||||
{
|
||||
if (Validate (buf, len, m_MacKey))
|
||||
{
|
||||
Decrypt (buf, len, m_SessionKey);
|
||||
SSUHeader * header = (SSUHeader *)buf;
|
||||
uint8_t payloadType = header->flag >> 4;
|
||||
switch (payloadType)
|
||||
switch (header->GetPayloadType ())
|
||||
{
|
||||
case PAYLOAD_TYPE_DATA:
|
||||
LogPrint ("SSU data received");
|
||||
ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader));
|
||||
break;
|
||||
case PAYLOAD_TYPE_TEST:
|
||||
LogPrint ("SSU test received");
|
||||
case PAYLOAD_TYPE_PEER_TEST:
|
||||
LogPrint ("SSU peer test received");
|
||||
ProcessPeerTest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint);
|
||||
break;
|
||||
case PAYLOAD_TYPE_SESSION_DESTROYED:
|
||||
{
|
||||
@ -116,10 +116,10 @@ namespace ssu
|
||||
}
|
||||
case PAYLOAD_TYPE_RELAY_INTRO:
|
||||
LogPrint ("SSU relay intro received");
|
||||
// TODO:
|
||||
ProcessRelayIntro (buf + sizeof (SSUHeader), len - sizeof (SSUHeader));
|
||||
break;
|
||||
default:
|
||||
LogPrint ("Unexpected SSU payload type ", (int)payloadType);
|
||||
LogPrint ("Unexpected SSU payload type ", (int)header->GetPayloadType ());
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -139,31 +139,57 @@ namespace ssu
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
|
||||
void SSUSession::ProcessIntroKeyMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
|
||||
{
|
||||
LogPrint ("Process session request");
|
||||
// use our intro key
|
||||
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, buf, len))
|
||||
auto introKey = GetIntroKey ();
|
||||
if (!introKey)
|
||||
{
|
||||
LogPrint ("SSU is not supported");
|
||||
return;
|
||||
}
|
||||
// use intro key for verification and decryption
|
||||
if (!Validate (buf, len, introKey))
|
||||
{
|
||||
LogPrint ("MAC verification intro key failed");
|
||||
Failed ();
|
||||
return;
|
||||
}
|
||||
|
||||
Decrypt (buf, len, introKey);
|
||||
CreateAESandMacKey (buf + sizeof (SSUHeader), m_SessionKey, m_MacKey);
|
||||
SSUHeader * header = (SSUHeader *)buf;
|
||||
switch (header->GetPayloadType ())
|
||||
{
|
||||
case PAYLOAD_TYPE_SESSION_REQUEST:
|
||||
ProcessSessionRequest (buf, len, senderEndpoint);
|
||||
break;
|
||||
case PAYLOAD_TYPE_SESSION_CREATED:
|
||||
ProcessSessionCreated (buf, len);
|
||||
break;
|
||||
case PAYLOAD_TYPE_PEER_TEST:
|
||||
LogPrint ("SSU peer test received");
|
||||
// TODO:
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
|
||||
{
|
||||
m_State = eSessionStateRequestReceived;
|
||||
LogPrint ("Session request received");
|
||||
m_RemoteEndpoint = senderEndpoint;
|
||||
SendSessionCreated (buf + sizeof (SSUHeader));
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len)
|
||||
{
|
||||
LogPrint ("Process session created");
|
||||
if (!m_RemoteRouter)
|
||||
{
|
||||
LogPrint ("Unsolicited session created message");
|
||||
return;
|
||||
}
|
||||
|
||||
// use remote intro key
|
||||
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, buf, len))
|
||||
{
|
||||
m_State = eSessionStateCreatedReceived;
|
||||
LogPrint ("Session created received");
|
||||
m_Timer.cancel (); // connect timer
|
||||
@ -185,7 +211,7 @@ namespace ssu
|
||||
*(uint32_t *)(signedData + 518) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ()); // remote IP
|
||||
*(uint16_t *)(signedData + 522) = htobe16 (m_RemoteEndpoint.port ()); // remote port
|
||||
memcpy (signedData + 524, payload, 8); // relayTag and signed on time
|
||||
uint32_t relayTag = be32toh (*(uint32_t *)payload);
|
||||
m_RelayTag = be32toh (*(uint32_t *)payload);
|
||||
payload += 4; // relayTag
|
||||
payload += 4; // signed on time
|
||||
// decrypt DSA signature
|
||||
@ -198,8 +224,7 @@ namespace ssu
|
||||
if (!verifier.VerifyMessage (signedData, 532, payload, 40))
|
||||
LogPrint ("SSU signature verification failed");
|
||||
|
||||
SendSessionConfirmed (y, ourAddress, relayTag);
|
||||
}
|
||||
SendSessionConfirmed (y, ourAddress);
|
||||
}
|
||||
|
||||
void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len)
|
||||
@ -209,7 +234,7 @@ namespace ssu
|
||||
{
|
||||
Decrypt (buf, len, m_SessionKey);
|
||||
SSUHeader * header = (SSUHeader *)buf;
|
||||
if ((header->flag >> 4) == PAYLOAD_TYPE_SESSION_CONFIRMED)
|
||||
if (header->GetPayloadType () == PAYLOAD_TYPE_SESSION_CONFIRMED)
|
||||
{
|
||||
m_State = eSessionStateConfirmedReceived;
|
||||
LogPrint ("Session confirmed received");
|
||||
@ -326,7 +351,7 @@ namespace ssu
|
||||
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)
|
||||
{
|
||||
uint8_t buf[480 + 18];
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
@ -352,7 +377,7 @@ namespace ssu
|
||||
memcpy (signedData + 512, ourAddress, 6); // our address/port as seem by party
|
||||
*(uint32_t *)(signedData + 518) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ()); // remote IP
|
||||
*(uint16_t *)(signedData + 522) = htobe16 (m_RemoteEndpoint.port ()); // remote port
|
||||
*(uint32_t *)(signedData + 524) = htobe32 (relayTag); // relay tag
|
||||
*(uint32_t *)(signedData + 524) = htobe32 (m_RelayTag); // relay tag
|
||||
*(uint32_t *)(signedData + 528) = htobe32 (signedOnTime); // signed on time
|
||||
i2p::context.Sign (signedData, 532, payload); // DSA signature
|
||||
|
||||
@ -406,33 +431,20 @@ namespace ssu
|
||||
}
|
||||
}
|
||||
|
||||
bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len)
|
||||
void SSUSession::ProcessRelayIntro (uint8_t * buf, size_t len)
|
||||
{
|
||||
auto introKey = GetIntroKey ();
|
||||
if (introKey)
|
||||
uint8_t size = *buf;
|
||||
if (size == 4)
|
||||
{
|
||||
// use intro key for verification and decryption
|
||||
if (Validate (buf, len, introKey))
|
||||
{
|
||||
Decrypt (buf, len, introKey);
|
||||
SSUHeader * header = (SSUHeader *)buf;
|
||||
if ((header->flag >> 4) == expectedPayloadType)
|
||||
{
|
||||
CreateAESandMacKey (buf + sizeof (SSUHeader), m_SessionKey, m_MacKey);
|
||||
return true;
|
||||
buf++; // size
|
||||
boost::asio::ip::address_v4 address (be32toh (*(uint32_t* )buf));
|
||||
buf += 4; // address
|
||||
uint16_t port = be16toh (*(uint16_t *)buf);
|
||||
// send hole punch of 1 byte
|
||||
m_Server.Send (buf, 1, boost::asio::ip::udp::endpoint (address, port));
|
||||
}
|
||||
else
|
||||
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("MAC verification failed");
|
||||
Failed ();
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint ("SSU is not supported");
|
||||
return false;
|
||||
LogPrint ("Address size ", size, " is not supported");
|
||||
}
|
||||
|
||||
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len,
|
||||
@ -544,6 +556,9 @@ namespace ssu
|
||||
Send (it);
|
||||
m_DelayedMessages.clear ();
|
||||
}
|
||||
if (m_PeerTest)
|
||||
SendPeerTest ();
|
||||
ScheduleTermination ();
|
||||
}
|
||||
|
||||
void SSUSession::Failed ()
|
||||
@ -556,6 +571,23 @@ namespace ssu
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::ScheduleTermination ()
|
||||
{
|
||||
m_Timer.cancel ();
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_TIMEOUT));
|
||||
m_Timer.async_wait (boost::bind (&SSUSession::HandleTerminationTimer,
|
||||
this, boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void SSUSession::HandleTerminationTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint ("SSU no activity fo ", SSU_TERMINATION_TIMEOUT, " seconds");
|
||||
Failed ();
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t * SSUSession::GetIntroKey () const
|
||||
{
|
||||
if (m_RemoteRouter)
|
||||
@ -678,6 +710,87 @@ namespace ssu
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SSUSession::ProcessPeerTest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
|
||||
{
|
||||
uint8_t * buf1 = buf;
|
||||
uint32_t nonce = be32toh (*(uint32_t *)buf);
|
||||
buf += 4; // nonce
|
||||
uint8_t size = *buf;
|
||||
buf++; // size
|
||||
uint8_t * address = (size == 4) ? buf : nullptr;
|
||||
buf += size; // address
|
||||
uint16_t port = *(uint16_t *)buf; // use it as is
|
||||
buf += 2; // port
|
||||
uint8_t * introKey = buf;
|
||||
if (port)
|
||||
{
|
||||
LogPrint ("SSU peer test. We are Charlie");
|
||||
Send (PAYLOAD_TYPE_PEER_TEST, buf1, len); // back to Bob
|
||||
if (address)
|
||||
SendPeerTest (nonce, be32toh (*(uint32_t *)address), be16toh (port), introKey); // to Alice
|
||||
else
|
||||
LogPrint ("Address of ", size, " bytes not supported");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("SSU peer test. We are Bob");
|
||||
// TODO:
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, uint8_t * introKey)
|
||||
{
|
||||
uint8_t buf[80 + 18];
|
||||
uint8_t iv[16];
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*(uint32_t *)payload = htobe32 (nonce);
|
||||
payload += 4; // nonce
|
||||
*payload = 4;
|
||||
payload++; // size
|
||||
*(uint32_t *)payload = htobe32 (address);
|
||||
payload += 4; // address
|
||||
*(uint16_t *)payload = htobe32 (port);
|
||||
payload += 2; // port
|
||||
memcpy (payload, introKey, 32); // intro key
|
||||
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
rnd.GenerateBlock (iv, 16); // random iv
|
||||
// encrypt message with specified intro key
|
||||
FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey);
|
||||
boost::asio::ip::udp::endpoint e (boost::asio::ip::address_v4 (address), port);
|
||||
m_Server.Send (buf, 80, e);
|
||||
}
|
||||
|
||||
void SSUSession::SendPeerTest ()
|
||||
{
|
||||
LogPrint ("SSU sending peer test");
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
|
||||
if (!address)
|
||||
{
|
||||
LogPrint ("SSU is not supported. Can't send peer test");
|
||||
return;
|
||||
}
|
||||
auto introKey = address->key;
|
||||
uint8_t buf[80 + 18];
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
uint32_t nonce = 0;
|
||||
rnd.GenerateWord32 (nonce);
|
||||
*(uint32_t *)payload = htobe32 (nonce);
|
||||
payload += 4; // nonce
|
||||
*payload = 4;
|
||||
payload++; // size
|
||||
memset (payload, 0, 6); // address and port always zero for Alice
|
||||
payload += 6; // address and port
|
||||
memcpy (payload, introKey, 32); // intro key
|
||||
uint8_t iv[16];
|
||||
rnd.GenerateBlock (iv, 16); // random iv
|
||||
// encrypt message with session key
|
||||
FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, m_SessionKey, iv, m_MacKey);
|
||||
m_Server.Send (buf, 80, m_RemoteEndpoint);
|
||||
}
|
||||
|
||||
void SSUSession::SendMsgAck (uint32_t msgID)
|
||||
{
|
||||
uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16
|
||||
@ -704,22 +817,12 @@ namespace ssu
|
||||
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)
|
||||
{
|
||||
@ -771,6 +874,24 @@ namespace ssu
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len)
|
||||
{
|
||||
uint8_t buf[SSU_MTU + 18];
|
||||
uint8_t iv[16];
|
||||
size_t msgSize = len + sizeof (SSUHeader);
|
||||
if (msgSize > SSU_MTU)
|
||||
{
|
||||
LogPrint ("SSU payload size ", msgSize, " exceeds MTU");
|
||||
return;
|
||||
}
|
||||
memcpy (buf + sizeof (SSUHeader), payload, len);
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
rnd.GenerateBlock (iv, 16); // random iv
|
||||
// encrypt message with session key
|
||||
FillHeaderAndEncrypt (type, buf, msgSize, m_SessionKey, iv, m_MacKey);
|
||||
m_Server.Send (buf, msgSize, m_RemoteEndpoint);
|
||||
}
|
||||
|
||||
SSUServer::SSUServer (boost::asio::io_service& service, int port):
|
||||
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (service, m_Endpoint)
|
||||
{
|
||||
@ -841,7 +962,7 @@ namespace ssu
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SSUSession * SSUServer::GetSession (const i2p::data::RouterInfo * router)
|
||||
SSUSession * SSUServer::GetSession (const i2p::data::RouterInfo * router, bool peerTest)
|
||||
{
|
||||
SSUSession * session = nullptr;
|
||||
if (router)
|
||||
@ -859,10 +980,10 @@ namespace ssu
|
||||
if (!router->UsesIntroducer ())
|
||||
{
|
||||
// connect directly
|
||||
session = new SSUSession (*this, remoteEndpoint, router);
|
||||
session = new SSUSession (*this, remoteEndpoint, router, peerTest);
|
||||
m_Sessions[remoteEndpoint] = session;
|
||||
LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (), "] ",
|
||||
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port (), " created");
|
||||
LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ",
|
||||
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
|
||||
session->Connect ();
|
||||
}
|
||||
else
|
||||
@ -872,12 +993,21 @@ namespace ssu
|
||||
{
|
||||
auto& introducer = address->introducers[0]; // TODO:
|
||||
boost::asio::ip::udp::endpoint introducerEndpoint (introducer.iHost, introducer.iPort);
|
||||
it = m_Sessions.find (introducerEndpoint);
|
||||
if (it == m_Sessions.end ())
|
||||
{
|
||||
session = new SSUSession (*this, introducerEndpoint, router);
|
||||
m_Sessions[introducerEndpoint] = session;
|
||||
LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (),
|
||||
LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (),
|
||||
"] through introducer ", introducerEndpoint.address ().to_string (), ":", introducerEndpoint.port ());
|
||||
session->ConnectThroughIntroducer (introducer);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Session to introducer already exists");
|
||||
// TODO:
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint ("Router is unreachable, but not introducers presentd. Ignored");
|
||||
}
|
||||
@ -917,7 +1047,7 @@ namespace ssu
|
||||
auto session = it->second;
|
||||
m_Sessions.erase (it);
|
||||
m_Sessions[newEndpoint] = session;
|
||||
LogPrint ("SSU session ressigned from ", oldEndpoint.address ().to_string (), ":", oldEndpoint.port (),
|
||||
LogPrint ("SSU session reassigned from ", oldEndpoint.address ().to_string (), ":", oldEndpoint.port (),
|
||||
" to ", newEndpoint.address ().to_string (), ":", newEndpoint.port ());
|
||||
}
|
||||
}
|
||||
|
28
SSU.h
28
SSU.h
@ -23,11 +23,14 @@ namespace ssu
|
||||
uint8_t iv[16];
|
||||
uint8_t flag;
|
||||
uint32_t time;
|
||||
|
||||
uint8_t GetPayloadType () const { return flag >> 4; };
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
const int SSU_MTU = 1484;
|
||||
const size_t SSU_MTU = 1484;
|
||||
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
|
||||
|
||||
// payload types (4 bits)
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
|
||||
@ -37,7 +40,7 @@ namespace ssu
|
||||
const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4;
|
||||
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
|
||||
const uint8_t PAYLOAD_TYPE_DATA = 6;
|
||||
const uint8_t PAYLOAD_TYPE_TEST = 7;
|
||||
const uint8_t PAYLOAD_TYPE_PEER_TEST = 7;
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
|
||||
|
||||
// data flags
|
||||
@ -70,7 +73,7 @@ namespace ssu
|
||||
public:
|
||||
|
||||
SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
|
||||
const i2p::data::RouterInfo * router = nullptr);
|
||||
const i2p::data::RouterInfo * router = nullptr, bool peerTest = false);
|
||||
void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
~SSUSession ();
|
||||
|
||||
@ -80,34 +83,43 @@ namespace ssu
|
||||
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
|
||||
const i2p::data::RouterInfo * GetRemoteRouter () const { return m_RemoteRouter; };
|
||||
void SendI2NPMessage (I2NPMessage * msg);
|
||||
void SendPeerTest (); // Alice
|
||||
|
||||
private:
|
||||
|
||||
void CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey);
|
||||
|
||||
void ProcessMessage (uint8_t * buf, size_t len); // call for established session
|
||||
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
|
||||
void ProcessIntroKeyMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for non-established session
|
||||
|
||||
void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
void SendSessionRequest ();
|
||||
void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer);
|
||||
void ProcessSessionCreated (uint8_t * buf, size_t len);
|
||||
void SendSessionCreated (const uint8_t * x);
|
||||
void ProcessSessionConfirmed (uint8_t * buf, size_t len);
|
||||
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag);
|
||||
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress);
|
||||
void ProcessRelayResponse (uint8_t * buf, size_t len);
|
||||
void ProcessRelayIntro (uint8_t * buf, size_t len);
|
||||
void Established ();
|
||||
void Failed ();
|
||||
void HandleConnectTimer (const boost::system::error_code& ecode);
|
||||
void ProcessPeerTest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, uint8_t * introKey); // Charlie to Alice
|
||||
void ProcessData (uint8_t * buf, size_t len);
|
||||
void SendMsgAck (uint32_t msgID);
|
||||
void SendSesionDestroyed ();
|
||||
void Send (i2p::I2NPMessage * msg);
|
||||
void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key
|
||||
|
||||
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;
|
||||
|
||||
void ScheduleTermination ();
|
||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
SSUServer& m_Server;
|
||||
@ -115,7 +127,9 @@ namespace ssu
|
||||
const i2p::data::RouterInfo * m_RemoteRouter;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
i2p::data::DHKeysPair * m_DHKeysPair; // X - for client and Y - for server
|
||||
bool m_PeerTest;
|
||||
SessionState m_State;
|
||||
uint32_t m_RelayTag;
|
||||
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption;
|
||||
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption;
|
||||
uint8_t m_SessionKey[32], m_MacKey[32];
|
||||
@ -131,7 +145,7 @@ namespace ssu
|
||||
~SSUServer ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
SSUSession * GetSession (const i2p::data::RouterInfo * router);
|
||||
SSUSession * GetSession (const i2p::data::RouterInfo * router, bool peerTest = false);
|
||||
SSUSession * FindSession (const i2p::data::RouterInfo * router);
|
||||
void DeleteSession (SSUSession * session);
|
||||
void DeleteAllSessions ();
|
||||
|
@ -345,7 +345,7 @@ namespace stream
|
||||
StreamingDestination::StreamingDestination (): m_LeaseSet (nullptr)
|
||||
{
|
||||
m_Keys = i2p::data::CreateRandomKeys ();
|
||||
m_IdentHash = i2p::data::CalculateIdentHash (m_Keys.pub);
|
||||
m_IdentHash = m_Keys.pub.Hash ();
|
||||
m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
|
||||
CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
|
@ -92,7 +92,6 @@ namespace i2p
|
||||
m_DHKeysPairSupplier.Start ();
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&Transports::Run, this));
|
||||
m_Timer = new boost::asio::deadline_timer (m_Service);
|
||||
// create acceptors
|
||||
auto addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (auto& address : addresses)
|
||||
@ -124,23 +123,17 @@ namespace i2p
|
||||
|
||||
void Transports::Stop ()
|
||||
{
|
||||
for (auto session: m_NTCPSessions)
|
||||
delete session.second;
|
||||
m_NTCPSessions.clear ();
|
||||
delete m_NTCPAcceptor;
|
||||
|
||||
if (m_Timer)
|
||||
{
|
||||
m_Timer->cancel ();
|
||||
delete m_Timer;
|
||||
}
|
||||
|
||||
if (m_SSUServer)
|
||||
{
|
||||
m_SSUServer->Stop ();
|
||||
delete m_SSUServer;
|
||||
}
|
||||
|
||||
for (auto session: m_NTCPSessions)
|
||||
delete session.second;
|
||||
m_NTCPSessions.clear ();
|
||||
delete m_NTCPAcceptor;
|
||||
|
||||
m_DHKeysPairSupplier.Stop ();
|
||||
m_IsRunning = false;
|
||||
m_Service.stop ();
|
||||
@ -270,24 +263,10 @@ namespace i2p
|
||||
{
|
||||
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));
|
||||
m_SSUServer->GetSession (router, true); // peer test
|
||||
}
|
||||
}
|
||||
|
||||
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 ();
|
||||
}
|
||||
}
|
||||
|
||||
i2p::data::DHKeysPair * Transports::GetNextDHKeysPair ()
|
||||
{
|
||||
|
@ -71,7 +71,6 @@ namespace i2p
|
||||
void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
|
||||
|
||||
void DetectExternalIP ();
|
||||
void HandleTimer (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
@ -83,7 +82,6 @@ namespace i2p
|
||||
|
||||
std::map<i2p::data::IdentHash, i2p::ntcp::NTCPSession *> m_NTCPSessions;
|
||||
i2p::ssu::SSUServer * m_SSUServer;
|
||||
boost::asio::deadline_timer * m_Timer;
|
||||
|
||||
DHKeysPairSupplier m_DHKeysPairSupplier;
|
||||
|
||||
|
@ -169,14 +169,14 @@ namespace tunnel
|
||||
*m_OutboundTunnels.begin () : tunnels.GetNextOutboundTunnel ();
|
||||
LogPrint ("Creating destination inbound tunnel...");
|
||||
auto firstHop = i2p::data::netdb.GetRandomRouter (outboundTunnel ? outboundTunnel->GetEndpointRouter () : nullptr);
|
||||
auto secondHop = i2p::data::netdb.GetRandomRouter (firstHop);
|
||||
auto secondHop = outboundTunnel ? outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router : nullptr;
|
||||
if (!secondHop || secondHop->GetIdentHash () == i2p::context.GetIdentHash ())
|
||||
secondHop = i2p::data::netdb.GetRandomRouter (firstHop);
|
||||
auto * tunnel = tunnels.CreateTunnel<InboundTunnel> (
|
||||
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
|
||||
{
|
||||
firstHop,
|
||||
secondHop
|
||||
// TODO: switch to 3-hops later
|
||||
/*i2p::data::netdb.GetRandomRouter (secondHop) */
|
||||
}),
|
||||
outboundTunnel);
|
||||
tunnel->SetTunnelPool (this);
|
||||
|
5
util.cpp
5
util.cpp
@ -249,6 +249,11 @@ namespace http
|
||||
}
|
||||
}
|
||||
|
||||
std::string url::portstr_ = "80";
|
||||
unsigned int url::port_ = 80;
|
||||
std::string url::user_ = "";
|
||||
std::string url::pass_ = "";
|
||||
|
||||
url::url(const std::string& url_s)
|
||||
{
|
||||
parse(url_s);
|
||||
|
8
util.h
8
util.h
@ -41,10 +41,10 @@ namespace util
|
||||
void parse(const std::string& url_s);
|
||||
public:
|
||||
std::string protocol_, host_, path_, query_;
|
||||
std::string portstr_ = "80";
|
||||
unsigned int port_ = 80;
|
||||
std::string user_ = "";
|
||||
std::string pass_ = "";
|
||||
static std::string portstr_;
|
||||
static unsigned int port_;
|
||||
static std::string user_;
|
||||
static std::string pass_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user