mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
15 Commits
78e27ea63a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d610e6c4c1 | ||
|
|
13604ccbb6 | ||
|
|
e996db03c0 | ||
|
|
f79a2e81ff | ||
|
|
4b1ac7420c | ||
|
|
e518b92a89 | ||
|
|
1a32ed9088 | ||
|
|
b17bbd754a | ||
|
|
7b0ff2850c | ||
|
|
31ff0ff1cb | ||
|
|
fcc70025fd | ||
|
|
56145d0f3c | ||
|
|
35352f1497 | ||
|
|
e695f1e060 | ||
|
|
ecc635e9bc |
2
.github/workflows/build-windows.yml
vendored
2
.github/workflows/build-windows.yml
vendored
@@ -231,7 +231,7 @@ jobs:
|
||||
cd MINGW-packages/mingw-w64-boost
|
||||
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
|
||||
- name: Install boost package
|
||||
run: pacman --noconfirm -U MINGW-packages/mingw-w64-boost/mingw-w64-i686-*-any.pkg.tar.zst
|
||||
run: pacman --noconfirm -U --overwrite MINGW-packages/mingw-w64-boost/mingw-w64-i686-*-any.pkg.tar.zst
|
||||
|
||||
# Building i2pd
|
||||
- name: Build application
|
||||
|
||||
@@ -42,28 +42,29 @@ namespace transport
|
||||
delete[] m_SessionConfirmedBuffer;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub)
|
||||
bool NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub)
|
||||
{
|
||||
i2p::crypto::InitNoiseXKState (*this, rs);
|
||||
// h = SHA256(h || epub)
|
||||
MixHash (epub, 32);
|
||||
// x25519 between pub and priv
|
||||
uint8_t inputKeyMaterial[32];
|
||||
priv.Agree (pub, inputKeyMaterial);
|
||||
if (!priv.Agree (pub, inputKeyMaterial)) return false;
|
||||
MixKey (inputKeyMaterial);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF1Alice ()
|
||||
bool NTCP2Establisher::KDF1Alice ()
|
||||
{
|
||||
KeyDerivationFunction1 (m_RemoteStaticKey, *m_EphemeralKeys, m_RemoteStaticKey, GetPub ());
|
||||
return KeyDerivationFunction1 (m_RemoteStaticKey, *m_EphemeralKeys, m_RemoteStaticKey, GetPub ());
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF1Bob ()
|
||||
bool NTCP2Establisher::KDF1Bob ()
|
||||
{
|
||||
KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ());
|
||||
return KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ());
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub)
|
||||
bool NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub)
|
||||
{
|
||||
MixHash (sessionRequest + 32, 32); // encrypted payload
|
||||
|
||||
@@ -74,33 +75,35 @@ namespace transport
|
||||
|
||||
// x25519 between remote pub and ephemaral priv
|
||||
uint8_t inputKeyMaterial[32];
|
||||
m_EphemeralKeys->Agree (GetRemotePub (), inputKeyMaterial);
|
||||
|
||||
if (!m_EphemeralKeys->Agree (GetRemotePub (), inputKeyMaterial)) return false;
|
||||
MixKey (inputKeyMaterial);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF2Alice ()
|
||||
bool NTCP2Establisher::KDF2Alice ()
|
||||
{
|
||||
KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ());
|
||||
return KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ());
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF2Bob ()
|
||||
bool NTCP2Establisher::KDF2Bob ()
|
||||
{
|
||||
KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ());
|
||||
return KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ());
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF3Alice ()
|
||||
bool NTCP2Establisher::KDF3Alice ()
|
||||
{
|
||||
uint8_t inputKeyMaterial[32];
|
||||
i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial);
|
||||
if (!i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial)) return false;
|
||||
MixKey (inputKeyMaterial);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF3Bob ()
|
||||
bool NTCP2Establisher::KDF3Bob ()
|
||||
{
|
||||
uint8_t inputKeyMaterial[32];
|
||||
m_EphemeralKeys->Agree (m_RemoteStaticKey, inputKeyMaterial);
|
||||
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, inputKeyMaterial)) return false;
|
||||
MixKey (inputKeyMaterial);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::CreateEphemeralKey ()
|
||||
@@ -108,7 +111,7 @@ namespace transport
|
||||
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
|
||||
}
|
||||
|
||||
void NTCP2Establisher::CreateSessionRequestMessage (std::mt19937& rng)
|
||||
bool NTCP2Establisher::CreateSessionRequestMessage (std::mt19937& rng)
|
||||
{
|
||||
// create buffer and fill padding
|
||||
auto paddingLength = rng () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes
|
||||
@@ -121,7 +124,7 @@ namespace transport
|
||||
encryption.Encrypt (GetPub (), 32, m_SessionRequestBuffer); // X
|
||||
encryption.GetIV (m_IV); // save IV for SessionCreated
|
||||
// encryption key for next block
|
||||
KDF1Alice ();
|
||||
if (!KDF1Alice ()) return false;
|
||||
// fill options
|
||||
uint8_t options[32]; // actual options size is 16 bytes
|
||||
memset (options, 0, 16);
|
||||
@@ -147,9 +150,10 @@ namespace transport
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::CreateSessionCreatedMessage (std::mt19937& rng)
|
||||
bool NTCP2Establisher::CreateSessionCreatedMessage (std::mt19937& rng)
|
||||
{
|
||||
auto paddingLen = rng () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64);
|
||||
m_SessionCreatedBufferLen = paddingLen + 64;
|
||||
@@ -160,7 +164,7 @@ namespace transport
|
||||
encryption.SetIV (m_IV);
|
||||
encryption.Encrypt (GetPub (), 32, m_SessionCreatedBuffer); // Y
|
||||
// encryption key for next block (m_K)
|
||||
KDF2Bob ();
|
||||
if (!KDF2Bob ()) return false;
|
||||
uint8_t options[16];
|
||||
memset (options, 0, 16);
|
||||
htobe16buf (options + 2, paddingLen); // padLen
|
||||
@@ -169,7 +173,7 @@ namespace transport
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::CreateSessionConfirmedMessagePart1 (const uint8_t * nonce)
|
||||
@@ -184,17 +188,18 @@ namespace transport
|
||||
i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, GetH (), 32, GetK (), nonce, m_SessionConfirmedBuffer, 48, true); // encrypt
|
||||
}
|
||||
|
||||
void NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce)
|
||||
bool NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce)
|
||||
{
|
||||
// part 2
|
||||
// update AD again
|
||||
MixHash (m_SessionConfirmedBuffer, 48);
|
||||
// encrypt m3p2, it must be filled in SessionRequest
|
||||
KDF3Alice ();
|
||||
if (!KDF3Alice ()) return false;
|
||||
uint8_t * m3p2 = m_SessionConfirmedBuffer + 48;
|
||||
i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2, m3p2Len, true); // encrypt
|
||||
// update h again
|
||||
MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew)
|
||||
@@ -207,7 +212,11 @@ namespace transport
|
||||
decryption.Decrypt (m_SessionRequestBuffer, 32, GetRemotePub ());
|
||||
decryption.GetIV (m_IV); // save IV for SessionCreated
|
||||
// decryption key for next block
|
||||
KDF1Bob ();
|
||||
if (!KDF1Bob ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionRequest KDF failed");
|
||||
return false;
|
||||
}
|
||||
// verify MAC and decrypt options block (32 bytes), use m_H as AD
|
||||
uint8_t nonce[12], options[16];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
@@ -262,7 +271,11 @@ namespace transport
|
||||
decryption.SetIV (m_IV);
|
||||
decryption.Decrypt (m_SessionCreatedBuffer, 32, GetRemotePub ());
|
||||
// decryption key for next block (m_K)
|
||||
KDF2Alice ();
|
||||
if (!KDF2Alice ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionCreated KDF failed");
|
||||
return false;
|
||||
}
|
||||
// decrypt and verify MAC
|
||||
uint8_t payload[16];
|
||||
uint8_t nonce[12];
|
||||
@@ -309,7 +322,11 @@ namespace transport
|
||||
// update AD again
|
||||
MixHash (m_SessionConfirmedBuffer, 48);
|
||||
|
||||
KDF3Bob ();
|
||||
if (!KDF3Bob ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 KDF failed");
|
||||
return false;
|
||||
}
|
||||
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt
|
||||
// calculate new h again for KDF data
|
||||
MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext)
|
||||
@@ -327,6 +344,7 @@ namespace transport
|
||||
m_Server (server), m_Socket (m_Server.GetService ()),
|
||||
m_IsEstablished (false), m_IsTerminated (false),
|
||||
m_Establisher (new NTCP2Establisher),
|
||||
m_SendKey (nullptr), m_ReceiveKey (nullptr),
|
||||
#if OPENSSL_SIPHASH
|
||||
m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr),
|
||||
#else
|
||||
@@ -466,7 +484,12 @@ namespace transport
|
||||
|
||||
void NTCP2Session::SendSessionRequest ()
|
||||
{
|
||||
m_Establisher->CreateSessionRequestMessage (m_Server.GetRng ());
|
||||
if (!m_Establisher->CreateSessionRequestMessage (m_Server.GetRng ()))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Send SessionRequest KDF failed");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
return;
|
||||
}
|
||||
// send message
|
||||
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionRequestBuffer, m_Establisher->m_SessionRequestBufferLen), boost::asio::transfer_all (),
|
||||
@@ -558,7 +581,12 @@ namespace transport
|
||||
|
||||
void NTCP2Session::SendSessionCreated ()
|
||||
{
|
||||
m_Establisher->CreateSessionCreatedMessage (m_Server.GetRng ());
|
||||
if (!m_Establisher->CreateSessionCreatedMessage (m_Server.GetRng ()))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Send SessionCreated KDF failed");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
return;
|
||||
}
|
||||
// send message
|
||||
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionCreatedBuffer, m_Establisher->m_SessionCreatedBufferLen), boost::asio::transfer_all (),
|
||||
@@ -637,7 +665,12 @@ namespace transport
|
||||
CreateNonce (1, nonce); // set nonce to 1
|
||||
m_Establisher->CreateSessionConfirmedMessagePart1 (nonce);
|
||||
memset (nonce, 0, 12); // set nonce back to 0
|
||||
m_Establisher->CreateSessionConfirmedMessagePart2 (nonce);
|
||||
if (!m_Establisher->CreateSessionConfirmedMessagePart2 (nonce))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Send SessionConfirmed Part2 KDF failed");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
return;
|
||||
}
|
||||
// send message
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (),
|
||||
std::bind(&NTCP2Session::HandleSessionConfirmedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
@@ -689,6 +722,7 @@ namespace transport
|
||||
|
||||
void NTCP2Session::HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
(void) bytes_transferred;
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed read error: ", ecode.message ());
|
||||
@@ -697,120 +731,141 @@ namespace transport
|
||||
else
|
||||
{
|
||||
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval;
|
||||
LogPrint (eLogDebug, "NTCP2: SessionConfirmed received");
|
||||
// part 1
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (1, nonce);
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart1 (nonce))
|
||||
{
|
||||
// part 2
|
||||
std::vector<uint8_t> buf(m_Establisher->m3p2Len - 16); // -MAC
|
||||
memset (nonce, 0, 12); // set nonce to 0 again
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart2 (nonce, buf.data ())) // TODO:handle in establisher thread
|
||||
boost::asio::post (m_Server.GetEstablisherService (),
|
||||
[s = shared_from_this ()] ()
|
||||
{
|
||||
KeyDerivationFunctionDataPhase ();
|
||||
// Bob data phase keys
|
||||
m_SendKey = m_Kba;
|
||||
m_ReceiveKey = m_Kab;
|
||||
SetSipKeys (m_Sipkeysba, m_Sipkeysab);
|
||||
memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8);
|
||||
memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8);
|
||||
// payload
|
||||
// process RI
|
||||
if (buf[0] != eNTCP2BlkRouterInfo)
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Unexpected block ", (int)buf[0], " in SessionConfirmed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
auto size = bufbe16toh (buf.data () + 1);
|
||||
if (size > buf.size () - 3 || size > i2p::data::MAX_RI_BUFFER_SIZE + 1)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
// TODO: check flag
|
||||
i2p::data::RouterInfo ri (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag
|
||||
if (ri.IsUnreachable ())
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo verification failed in SessionConfirmed from ", GetRemoteEndpoint ());
|
||||
SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail);
|
||||
return;
|
||||
}
|
||||
LogPrint(eLogDebug, "NTCP2: SessionConfirmed from ", GetRemoteEndpoint (),
|
||||
" (", i2p::data::GetIdentHashAbbreviation (ri.GetIdentHash ()), ")");
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (ts > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed for ", (ts - ri.GetTimestamp ())/1000LL, " seconds");
|
||||
SendTerminationAndTerminate (eNTCP2Message3Error);
|
||||
return;
|
||||
}
|
||||
if (ts + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri.GetTimestamp ()) // 2 minutes
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo is from future for ", (ri.GetTimestamp () - ts)/1000LL, " seconds");
|
||||
SendTerminationAndTerminate (eNTCP2Message3Error);
|
||||
return;
|
||||
}
|
||||
// update RouterInfo in netdb
|
||||
auto ri1 = i2p::data::netdb.AddRouterInfo (ri.GetBuffer (), ri.GetBufferLen ()); // ri1 points to one from netdb now
|
||||
if (!ri1)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Couldn't update RouterInfo from SessionConfirmed in netdb");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<i2p::data::RouterProfile> profile; // not null if older
|
||||
if (ri.GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ())
|
||||
{
|
||||
// received RouterInfo is older than one in netdb
|
||||
profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile
|
||||
if (profile && profile->IsDuplicated ())
|
||||
{
|
||||
SendTerminationAndTerminate (eNTCP2Banned);
|
||||
return;
|
||||
}
|
||||
}
|
||||
s->ProcessSessionConfirmed ();;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri1->GetNTCP2V4Address () :
|
||||
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri1->GetYggdrasilAddress () : ri1->GetNTCP2V6Address ());
|
||||
if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32))
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Wrong static key in SessionConfirmed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
if (addr->IsPublishedNTCP2 () && m_RemoteEndpoint.address () != addr->host &&
|
||||
(!m_RemoteEndpoint.address ().is_v6 () || (i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ?
|
||||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data () + 1, addr->host.to_v6 ().to_bytes ().data () + 1, 7) : // from the same yggdrasil subnet
|
||||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), addr->host.to_v6 ().to_bytes ().data (), 8)))) // temporary address
|
||||
{
|
||||
if (profile) // older router?
|
||||
profile->Duplicated (); // mark router as duplicated in profile
|
||||
else
|
||||
LogPrint (eLogInfo, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ());
|
||||
SendTerminationAndTerminate (eNTCP2Banned);
|
||||
return;
|
||||
}
|
||||
// TODO: process options
|
||||
|
||||
// ready to communicate
|
||||
SetRemoteIdentity (ri1->GetRouterIdentity ());
|
||||
if (m_Server.AddNTCP2Session (shared_from_this (), true))
|
||||
{
|
||||
Established ();
|
||||
ReceiveLength ();
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
void NTCP2Session::ProcessSessionConfirmed ()
|
||||
{
|
||||
// run on establisher thread
|
||||
LogPrint (eLogDebug, "NTCP2: SessionConfirmed received");
|
||||
// part 1
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (1, nonce);
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart1 (nonce))
|
||||
{
|
||||
// part 2
|
||||
auto buf = std::make_shared<std::vector<uint8_t> > (m_Establisher->m3p2Len - 16); // -MAC
|
||||
memset (nonce, 0, 12); // set nonce to 0 again
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart2 (nonce, buf->data ())) // TODO:handle in establisher thread
|
||||
{
|
||||
// payload
|
||||
// RI block must be first
|
||||
if ((*buf)[0] != eNTCP2BlkRouterInfo)
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Unexpected block ", (int)(*buf)[0], " in SessionConfirmed");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
return;
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
auto size = bufbe16toh (buf->data () + 1);
|
||||
if (size > buf->size () - 3 || size > i2p::data::MAX_RI_BUFFER_SIZE + 1)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
return;
|
||||
}
|
||||
boost::asio::post (m_Server.GetService (),
|
||||
[s = shared_from_this (), buf, size] ()
|
||||
{
|
||||
s->EstablishSessionAfterSessionConfirmed (buf, size);
|
||||
});
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
}
|
||||
else
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
}
|
||||
|
||||
void NTCP2Session::EstablishSessionAfterSessionConfirmed (std::shared_ptr<std::vector<uint8_t> > buf, size_t size)
|
||||
{
|
||||
// run on main NTCP2 thread
|
||||
KeyDerivationFunctionDataPhase ();
|
||||
// Bob data phase keys
|
||||
m_SendKey = m_Kba;
|
||||
m_ReceiveKey = m_Kab;
|
||||
SetSipKeys (m_Sipkeysba, m_Sipkeysab);
|
||||
memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8);
|
||||
memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8);
|
||||
// we need to set keys for SendTerminationAndTerminate
|
||||
// TODO: check flag
|
||||
i2p::data::RouterInfo ri (buf->data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag
|
||||
if (ri.IsUnreachable ())
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo verification failed in SessionConfirmed from ", GetRemoteEndpoint ());
|
||||
SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail);
|
||||
return;
|
||||
}
|
||||
LogPrint(eLogDebug, "NTCP2: SessionConfirmed from ", GetRemoteEndpoint (),
|
||||
" (", i2p::data::GetIdentHashAbbreviation (ri.GetIdentHash ()), ")");
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (ts > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed for ", (ts - ri.GetTimestamp ())/1000LL, " seconds");
|
||||
SendTerminationAndTerminate (eNTCP2Message3Error);
|
||||
return;
|
||||
}
|
||||
if (ts + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri.GetTimestamp ()) // 2 minutes
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo is from future for ", (ri.GetTimestamp () - ts)/1000LL, " seconds");
|
||||
SendTerminationAndTerminate (eNTCP2Message3Error);
|
||||
return;
|
||||
}
|
||||
// update RouterInfo in netdb
|
||||
auto ri1 = i2p::data::netdb.AddRouterInfo (ri.GetBuffer (), ri.GetBufferLen ()); // ri1 points to one from netdb now
|
||||
if (!ri1)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Couldn't update RouterInfo from SessionConfirmed in netdb");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<i2p::data::RouterProfile> profile; // not null if older
|
||||
if (ri.GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ())
|
||||
{
|
||||
// received RouterInfo is older than one in netdb
|
||||
profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile
|
||||
if (profile && profile->IsDuplicated ())
|
||||
{
|
||||
SendTerminationAndTerminate (eNTCP2Banned);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri1->GetNTCP2V4Address () :
|
||||
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri1->GetYggdrasilAddress () : ri1->GetNTCP2V6Address ());
|
||||
if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32))
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Wrong static key in SessionConfirmed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
if (addr->IsPublishedNTCP2 () && m_RemoteEndpoint.address () != addr->host &&
|
||||
(!m_RemoteEndpoint.address ().is_v6 () || (i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ?
|
||||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data () + 1, addr->host.to_v6 ().to_bytes ().data () + 1, 7) : // from the same yggdrasil subnet
|
||||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), addr->host.to_v6 ().to_bytes ().data (), 8)))) // temporary address
|
||||
{
|
||||
if (profile) // older router?
|
||||
profile->Duplicated (); // mark router as duplicated in profile
|
||||
else
|
||||
LogPrint (eLogInfo, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ());
|
||||
SendTerminationAndTerminate (eNTCP2Banned);
|
||||
return;
|
||||
}
|
||||
// TODO: process options block
|
||||
|
||||
// ready to communicate
|
||||
SetRemoteIdentity (ri1->GetRouterIdentity ());
|
||||
if (m_Server.AddNTCP2Session (shared_from_this (), true))
|
||||
{
|
||||
Established ();
|
||||
ReceiveLength ();
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
}
|
||||
|
||||
void NTCP2Session::SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey)
|
||||
|
||||
@@ -95,21 +95,21 @@ namespace transport
|
||||
const uint8_t * GetCK () const { return m_CK; };
|
||||
const uint8_t * GetH () const { return m_H; };
|
||||
|
||||
void KDF1Alice ();
|
||||
void KDF1Bob ();
|
||||
void KDF2Alice ();
|
||||
void KDF2Bob ();
|
||||
void KDF3Alice (); // for SessionConfirmed part 2
|
||||
void KDF3Bob ();
|
||||
bool KDF1Alice ();
|
||||
bool KDF1Bob ();
|
||||
bool KDF2Alice ();
|
||||
bool KDF2Bob ();
|
||||
bool KDF3Alice (); // for SessionConfirmed part 2
|
||||
bool KDF3Bob ();
|
||||
|
||||
void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
|
||||
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
|
||||
bool KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
|
||||
bool KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
|
||||
void CreateEphemeralKey ();
|
||||
|
||||
void CreateSessionRequestMessage (std::mt19937& rng);
|
||||
void CreateSessionCreatedMessage (std::mt19937& rng);
|
||||
bool CreateSessionRequestMessage (std::mt19937& rng);
|
||||
bool CreateSessionCreatedMessage (std::mt19937& rng);
|
||||
void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce);
|
||||
void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
|
||||
bool CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
|
||||
|
||||
bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew);
|
||||
bool ProcessSessionCreatedMessage (uint16_t& paddingLen);
|
||||
@@ -180,6 +180,8 @@ namespace transport
|
||||
void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void ProcessSessionConfirmed ();
|
||||
void EstablishSessionAfterSessionConfirmed (std::shared_ptr<std::vector<uint8_t> > buf, size_t size);
|
||||
|
||||
// data
|
||||
void ReceiveLength ();
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace stream
|
||||
m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed
|
||||
m_Status (eStreamStatusNew), m_IsIncoming (false), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false),
|
||||
m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false),
|
||||
m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), m_LocalDestination (local),
|
||||
m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), m_DoubleWinIncCounter (false), m_LocalDestination (local),
|
||||
m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service),
|
||||
m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port),
|
||||
m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_SlowRTT2 (INITIAL_RTT), m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0),
|
||||
@@ -101,7 +101,7 @@ namespace stream
|
||||
m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed
|
||||
m_Status (eStreamStatusNew), m_IsIncoming (true), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false),
|
||||
m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false),
|
||||
m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), m_LocalDestination (local),
|
||||
m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), m_DoubleWinIncCounter (false), m_LocalDestination (local),
|
||||
m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service),
|
||||
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_SlowRTT2 (INITIAL_RTT),
|
||||
m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowDropTargetSize (0), m_WindowIncCounter (0),
|
||||
@@ -495,6 +495,7 @@ namespace stream
|
||||
return;
|
||||
}
|
||||
int rttSample = INT_MAX;
|
||||
int incCounter = 0;
|
||||
m_IsNAcked = false;
|
||||
m_IsResendNeeded = false;
|
||||
int nackCount = packet->GetNACKCount ();
|
||||
@@ -537,8 +538,7 @@ namespace stream
|
||||
m_LocalDestination.DeletePacket (sentPacket);
|
||||
acknowledged = true;
|
||||
if (m_WindowSize < MAX_WINDOW_SIZE && !m_IsFirstACK)
|
||||
if (m_RTT < m_LocalDestination.GetRandom () % INITIAL_RTO) // dirty
|
||||
m_WindowIncCounter++;
|
||||
incCounter++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
@@ -552,7 +552,7 @@ namespace stream
|
||||
m_SlowRTT2 = rttSample;
|
||||
m_PrevRTTSample = rttSample;
|
||||
m_Jitter = rttSample / 10; // 10%
|
||||
m_Jitter += 5; // for low-latency connections
|
||||
m_Jitter += 15; // for low-latency connections
|
||||
m_IsFirstRttSample = false;
|
||||
}
|
||||
else
|
||||
@@ -569,9 +569,23 @@ namespace stream
|
||||
jitter = m_PrevRTTSample - rttSample;
|
||||
else
|
||||
jitter = rttSample / 10; // 10%
|
||||
jitter += 5; // for low-latency connections
|
||||
jitter += 15; // for low-latency connections
|
||||
m_Jitter = (0.05 * jitter) + (1.0 - 0.05) * m_Jitter;
|
||||
}
|
||||
if (rttSample > m_SlowRTT)
|
||||
{
|
||||
incCounter = 0;
|
||||
m_DoubleWinIncCounter = 1;
|
||||
}
|
||||
else if (rttSample < m_SlowRTT)
|
||||
{
|
||||
if (m_DoubleWinIncCounter)
|
||||
{
|
||||
incCounter = incCounter * 2;
|
||||
m_DoubleWinIncCounter = 0;
|
||||
}
|
||||
}
|
||||
m_WindowIncCounter = m_WindowIncCounter + incCounter;
|
||||
//
|
||||
// delay-based CC
|
||||
if ((m_SlowRTT2 > m_SlowRTT + m_Jitter && rttSample > m_SlowRTT2 && rttSample > m_PrevRTTSample) && !m_IsWinDropped) // Drop window if RTT grows too fast, late detection
|
||||
@@ -1297,8 +1311,6 @@ namespace stream
|
||||
else if (m_IsResendNeeded) // resend packets
|
||||
ResendPacket ();
|
||||
// delay-based CC
|
||||
else if (!m_IsWinDropped && int(m_SentPackets.size ()) == m_WindowSize) // we sending packets too fast, early detection
|
||||
ProcessWindowDrop ();
|
||||
else if (m_WindowSize > int(m_SentPackets.size ())) // send packets
|
||||
SendBuffer ();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <mutex>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Base.h"
|
||||
#include "Gzip.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
@@ -54,7 +55,7 @@ namespace stream
|
||||
const size_t COMPRESSION_THRESHOLD_SIZE = 66;
|
||||
const int MAX_NUM_RESEND_ATTEMPTS = 10;
|
||||
const int INITIAL_WINDOW_SIZE = 10;
|
||||
const int MIN_WINDOW_SIZE = 2;
|
||||
const int MIN_WINDOW_SIZE = 3;
|
||||
const int MAX_WINDOW_SIZE = 512;
|
||||
const double RTT_EWMA_ALPHA = 0.25;
|
||||
const double SLOWRTT_EWMA_ALPHA = 0.05;
|
||||
@@ -274,6 +275,7 @@ namespace stream
|
||||
bool m_IsTimeOutResend;
|
||||
bool m_IsImmediateAckRequested;
|
||||
bool m_IsRemoteLeaseChangeInProgress;
|
||||
bool m_DoubleWinIncCounter;
|
||||
StreamingDestination& m_LocalDestination;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
|
||||
std::shared_ptr<const i2p::crypto::Verifier> m_TransientVerifier; // in case of offline key
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace tunnel
|
||||
const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
|
||||
const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey):
|
||||
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
|
||||
layerKey, ivKey), m_Gateway(this) {};
|
||||
layerKey, ivKey), m_Gateway(*this) {};
|
||||
|
||||
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) override;
|
||||
void FlushTunnelDataMsgs () override;
|
||||
|
||||
@@ -447,28 +447,29 @@ namespace transport
|
||||
return std::max (bwCongestionLevel, tbwCongestionLevel);
|
||||
}
|
||||
|
||||
void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
std::future<std::shared_ptr<TransportSession> > Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
{
|
||||
if (m_IsOnline)
|
||||
SendMessages (ident, { msg });
|
||||
return SendMessages (ident, { msg });
|
||||
return {}; // invalid future
|
||||
}
|
||||
|
||||
void Transports::SendMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs)
|
||||
std::future<std::shared_ptr<TransportSession> > Transports::SendMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs)
|
||||
{
|
||||
std::list<std::shared_ptr<i2p::I2NPMessage> > msgs1;
|
||||
msgs.swap (msgs1);
|
||||
SendMessages (ident, std::move (msgs1));
|
||||
return SendMessages (ident, std::move (msgs1));
|
||||
}
|
||||
|
||||
void Transports::SendMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >&& msgs)
|
||||
std::future<std::shared_ptr<TransportSession> > Transports::SendMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >&& msgs)
|
||||
{
|
||||
boost::asio::post (*m_Service, [this, ident, msgs = std::move(msgs)] () mutable
|
||||
return boost::asio::post (*m_Service, boost::asio::use_future ([this, ident, msgs = std::move(msgs)] () mutable
|
||||
{
|
||||
PostMessages (ident, msgs);
|
||||
});
|
||||
return PostMessages (ident, msgs);
|
||||
}));
|
||||
}
|
||||
|
||||
void Transports::PostMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs)
|
||||
std::shared_ptr<TransportSession> Transports::PostMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs)
|
||||
{
|
||||
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
|
||||
{
|
||||
@@ -476,9 +477,9 @@ namespace transport
|
||||
for (auto& it: msgs)
|
||||
m_LoopbackHandler.PutNextMessage (std::move (it));
|
||||
m_LoopbackHandler.Flush ();
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
if(RoutesRestricted() && !IsRestrictedPeer(ident)) return;
|
||||
if(RoutesRestricted() && !IsRestrictedPeer(ident)) return nullptr;
|
||||
std::shared_ptr<Peer> peer;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
@@ -489,13 +490,13 @@ namespace transport
|
||||
if (!peer)
|
||||
{
|
||||
// check if not banned
|
||||
if (i2p::data::IsRouterBanned (ident)) return; // don't create peer to unreachable router
|
||||
if (i2p::data::IsRouterBanned (ident)) return nullptr; // don't create peer to unreachable router
|
||||
// try to connect
|
||||
bool connected = false;
|
||||
try
|
||||
{
|
||||
auto r = netdb.FindRouter (ident);
|
||||
if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable
|
||||
if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return nullptr; // router found but non-reachable
|
||||
|
||||
peer = std::make_shared<Peer>(r, i2p::util::GetSecondsSinceEpoch ());
|
||||
{
|
||||
@@ -509,12 +510,16 @@ namespace transport
|
||||
{
|
||||
LogPrint (eLogError, "Transports: PostMessages exception:", ex.what ());
|
||||
}
|
||||
if (!connected) return;
|
||||
if (!connected) return nullptr;
|
||||
}
|
||||
|
||||
if (!peer) return;
|
||||
if (!peer) return nullptr;
|
||||
if (peer->IsConnected ())
|
||||
peer->sessions.front ()->SendI2NPMessages (msgs);
|
||||
{
|
||||
auto session = peer->sessions.front ();
|
||||
if (session) session->SendI2NPMessages (msgs);
|
||||
return session;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto sz = peer->delayedMessages.size ();
|
||||
@@ -527,7 +532,7 @@ namespace transport
|
||||
LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped");
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (ident);
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (sz > MAX_NUM_DELAYED_MESSAGES/2)
|
||||
@@ -549,6 +554,7 @@ namespace transport
|
||||
m_Peers.erase (ident);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr<Peer> peer)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <future>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
@@ -143,9 +144,9 @@ namespace transport
|
||||
std::shared_ptr<i2p::crypto::X25519Keys> GetNextX25519KeysPair ();
|
||||
void ReuseX25519KeysPair (std::shared_ptr<i2p::crypto::X25519Keys> pair);
|
||||
|
||||
void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
void SendMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs);
|
||||
void SendMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >&& msgs);
|
||||
std::future<std::shared_ptr<TransportSession> > SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
std::future<std::shared_ptr<TransportSession> > SendMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs);
|
||||
std::future<std::shared_ptr<TransportSession> > SendMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >&& msgs);
|
||||
|
||||
void PeerConnected (std::shared_ptr<TransportSession> session);
|
||||
void PeerDisconnected (std::shared_ptr<TransportSession> session);
|
||||
@@ -189,7 +190,7 @@ namespace transport
|
||||
void Run ();
|
||||
void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
|
||||
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
|
||||
void PostMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs);
|
||||
std::shared_ptr<TransportSession> PostMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs);
|
||||
bool ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr<Peer> peer);
|
||||
void SetPriority (std::shared_ptr<Peer> peer) const;
|
||||
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace tunnel
|
||||
public:
|
||||
|
||||
OutboundTunnel (std::shared_ptr<const TunnelConfig> config):
|
||||
Tunnel (config), m_Gateway (this), m_EndpointIdentHash (config->GetLastIdentHash ()) {};
|
||||
Tunnel (config), m_Gateway (*this), m_EndpointIdentHash (config->GetLastIdentHash ()) {};
|
||||
|
||||
void SendTunnelDataMsgTo (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
virtual void SendTunnelDataMsgs (const std::vector<TunnelMessageBlock>& msgs); // multiple messages
|
||||
|
||||
@@ -220,21 +220,55 @@ namespace tunnel
|
||||
|
||||
void TunnelGateway::SendBuffer ()
|
||||
{
|
||||
// create list or tunnel messages
|
||||
m_Buffer.CompleteCurrentTunnelDataMessage ();
|
||||
std::list<std::shared_ptr<I2NPMessage> > newTunnelMsgs;
|
||||
const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs ();
|
||||
for (auto& tunnelMsg : tunnelDataMsgs)
|
||||
{
|
||||
auto newMsg = CreateEmptyTunnelDataMsg (false);
|
||||
m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg);
|
||||
htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ());
|
||||
m_Tunnel.EncryptTunnelMsg (tunnelMsg, newMsg);
|
||||
htobe32buf (newMsg->GetPayload (), m_Tunnel.GetNextTunnelID ());
|
||||
newMsg->FillI2NPMessageHeader (eI2NPTunnelData);
|
||||
if (tunnelMsg->onDrop) newMsg->onDrop = tunnelMsg->onDrop;
|
||||
newTunnelMsgs.push_back (newMsg);
|
||||
m_NumSentBytes += TUNNEL_DATA_MSG_SIZE;
|
||||
}
|
||||
m_Buffer.ClearTunnelDataMsgs ();
|
||||
i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), std::move (newTunnelMsgs));
|
||||
|
||||
// send
|
||||
auto currentTransport = m_CurrentTransport.lock ();
|
||||
if (!currentTransport)
|
||||
{
|
||||
// try to obtain transport from peding reequest or send thought transport is not complete
|
||||
if (m_PendingTransport.valid ()) // pending request?
|
||||
{
|
||||
if (m_PendingTransport.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
|
||||
{
|
||||
// pending request complete
|
||||
currentTransport = m_PendingTransport.get (); // take tarnsports used in pending request
|
||||
if (currentTransport)
|
||||
{
|
||||
if (currentTransport->IsEstablished ())
|
||||
m_CurrentTransport = currentTransport;
|
||||
else
|
||||
currentTransport = nullptr;
|
||||
}
|
||||
}
|
||||
else // still pending
|
||||
{
|
||||
// send through transports, but don't update pedning transport
|
||||
i2p::transport::transports.SendMessages (m_Tunnel.GetNextIdentHash (), std::move (newTunnelMsgs));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentTransport) // session is good
|
||||
// send to session directly
|
||||
currentTransport->SendI2NPMessages (newTunnelMsgs);
|
||||
else // no session yet
|
||||
// send through transports
|
||||
m_PendingTransport = i2p::transport::transports.SendMessages (m_Tunnel.GetNextIdentHash (), std::move (newTunnelMsgs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -12,7 +12,9 @@
|
||||
#include <inttypes.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include "I2NPProtocol.h"
|
||||
#include "TransportSession.h"
|
||||
#include "TunnelBase.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -45,7 +47,7 @@ namespace tunnel
|
||||
{
|
||||
public:
|
||||
|
||||
TunnelGateway (TunnelBase * tunnel):
|
||||
TunnelGateway (TunnelBase& tunnel):
|
||||
m_Tunnel (tunnel), m_NumSentBytes (0) {};
|
||||
void SendTunnelDataMsg (const TunnelMessageBlock& block);
|
||||
void PutTunnelDataMsg (const TunnelMessageBlock& block);
|
||||
@@ -54,9 +56,11 @@ namespace tunnel
|
||||
|
||||
private:
|
||||
|
||||
TunnelBase * m_Tunnel;
|
||||
TunnelBase& m_Tunnel;
|
||||
TunnelGatewayBuffer m_Buffer;
|
||||
size_t m_NumSentBytes;
|
||||
std::weak_ptr<i2p::transport::TransportSession> m_CurrentTransport;
|
||||
std::future<std::shared_ptr<i2p::transport::TransportSession> > m_PendingTransport;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,10 +375,10 @@ namespace client
|
||||
}
|
||||
|
||||
I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host, const std::string& XI2P,
|
||||
std::shared_ptr<boost::asio::ssl::context> sslCtx):
|
||||
I2PTunnelConnection (owner, stream, target, true, sslCtx), m_Host (host),
|
||||
m_HeaderSent (false), m_ResponseHeaderSent (false), m_From (stream->GetRemoteIdentity ())
|
||||
I2PTunnelConnection (owner, stream, target, true, sslCtx), m_Host (host), m_XI2P (XI2P),
|
||||
m_HeaderSent (false), m_ResponseHeaderSent (false)
|
||||
{
|
||||
if (sslCtx)
|
||||
SSL_set_tlsext_host_name(GetSSL ()->native_handle(), host.c_str ());
|
||||
@@ -448,17 +448,12 @@ namespace client
|
||||
if (!connection)
|
||||
m_OutHeader << "Connection: close\r\n";
|
||||
// add X-I2P fields
|
||||
if (m_From)
|
||||
{
|
||||
m_OutHeader << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(m_From->GetIdentHash ()) << "\r\n";
|
||||
m_OutHeader << X_I2P_DEST_HASH << ": " << m_From->GetIdentHash ().ToBase64 () << "\r\n";
|
||||
m_OutHeader << X_I2P_DEST_B64 << ": " << m_From->ToBase64 () << "\r\n";
|
||||
}
|
||||
m_OutHeader << m_XI2P;
|
||||
// end of header
|
||||
m_OutHeader << "\r\n";
|
||||
|
||||
m_OutHeader << "\r\n"; // end of header
|
||||
m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header
|
||||
m_InHeader.str ("");
|
||||
m_From = nullptr;
|
||||
m_HeaderSent = true;
|
||||
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
|
||||
}
|
||||
@@ -876,7 +871,17 @@ namespace client
|
||||
|
||||
std::shared_ptr<I2PTunnelConnection> I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream, GetEndpoint (), m_Host, GetSSLCtx ());
|
||||
if (m_XI2P.empty () || stream->GetRemoteIdentity () != m_From.lock ())
|
||||
{
|
||||
auto from = stream->GetRemoteIdentity ();
|
||||
m_From = from;
|
||||
std::stringstream ss;
|
||||
ss << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(from->GetIdentHash ()) << "\r\n";
|
||||
ss << X_I2P_DEST_HASH << ": " << from->GetIdentHash ().ToBase64 () << "\r\n";
|
||||
ss << X_I2P_DEST_B64 << ": " << from->ToBase64 () << "\r\n";
|
||||
m_XI2P = ss.str ();
|
||||
}
|
||||
return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream, GetEndpoint (), m_Host, m_XI2P, GetSSLCtx ());
|
||||
}
|
||||
|
||||
I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address,
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace client
|
||||
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
|
||||
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
|
||||
// for HTTP tunnels
|
||||
const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64
|
||||
const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64
|
||||
const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address
|
||||
constexpr char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64
|
||||
constexpr char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64
|
||||
constexpr char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address
|
||||
const int I2P_TUNNEL_HTTP_MAX_HEADER_SIZE = 8192;
|
||||
|
||||
class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this<I2PTunnelConnection>
|
||||
@@ -107,7 +107,7 @@ namespace client
|
||||
public:
|
||||
|
||||
I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host, const std::string& XI2P,
|
||||
std::shared_ptr<boost::asio::ssl::context> sslCtx = nullptr);
|
||||
|
||||
protected:
|
||||
@@ -117,10 +117,9 @@ namespace client
|
||||
|
||||
private:
|
||||
|
||||
std::string m_Host;
|
||||
std::string m_Host, m_XI2P;
|
||||
std::stringstream m_InHeader, m_OutHeader;
|
||||
bool m_HeaderSent, m_ResponseHeaderSent;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> m_From;
|
||||
};
|
||||
|
||||
class I2PTunnelConnectionIRC: public I2PTunnelConnection
|
||||
@@ -242,7 +241,8 @@ namespace client
|
||||
|
||||
private:
|
||||
|
||||
std::string m_Host;
|
||||
std::string m_Host, m_XI2P;
|
||||
std::weak_ptr<const i2p::data::IdentityEx> m_From;
|
||||
};
|
||||
|
||||
class I2PServerTunnelIRC: public I2PServerTunnel
|
||||
|
||||
Reference in New Issue
Block a user