implement SSU2HolPunchSession

This commit is contained in:
orignal 2024-10-16 08:28:25 -04:00
parent ec1f41b13c
commit 97fdedfbe3
5 changed files with 88 additions and 49 deletions

View File

@ -479,9 +479,11 @@ namespace transport
{
if (session)
{
m_Sessions.emplace (session->GetConnID (), session);
if (session->GetState () != eSSU2SessionStatePeerTest)
AddSessionByRouterHash (session);
if (m_Sessions.emplace (session->GetConnID (), session).second)
{
if (session->GetState () != eSSU2SessionStatePeerTest)
AddSessionByRouterHash (session);
}
}
}
@ -715,6 +717,9 @@ namespace transport
m_LastSession->SetRemoteEndpoint (senderEndpoint);
m_LastSession->ProcessPeerTest (buf, len);
break;
case eSSU2SessionStateHolePunch:
m_LastSession->ProcessFirstIncomingMessage (connID, buf, len); // SessionRequest
break;
case eSSU2SessionStateClosing:
m_LastSession->ProcessData (buf, len, senderEndpoint); // we might receive termintaion block
if (m_LastSession && m_LastSession->GetState () == eSSU2SessionStateClosing)

View File

@ -224,5 +224,63 @@ namespace transport
m_NumResends++;
}
}
SSU2HolePunchSession::SSU2HolePunchSession (SSU2Server& server, uint32_t nonce,
const boost::asio::ip::udp::endpoint& remoteEndpoint,
std::shared_ptr<const i2p::data::RouterInfo::Address> localAddr):
SSU2Session (server), // we create full incoming session
m_Nonce (nonce)
{
// we are Charlie
m_Token = GetServer ().GetIncomingToken (remoteEndpoint);
uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id
uint32_t sourceConnID = ~destConnID;
SetSourceConnID (sourceConnID);
SetDestConnID (destConnID);
SetState (eSSU2SessionStateHolePunch);
SetRemoteEndpoint (remoteEndpoint);
SetAddress (localAddr);
SetTerminationTimeout (SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT);
}
void SSU2HolePunchSession::SendHolePunch ()
{
auto addr = GetAddress ();
if (!addr) return;
auto& ep = GetRemoteEndpoint ();
LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep);
Header header;
uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE];
// fill packet
header.h.connID = GetDestConnID (); // dest id
RAND_bytes (header.buf + 8, 4); // random packet num
header.h.type = eSSU2HolePunch;
header.h.flags[0] = 2; // ver
header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID
header.h.flags[2] = 0; // flag
memcpy (h, header.buf, 16);
htobuf64 (h + 16, GetSourceConnID ()); // source id
RAND_bytes (h + 24, 8); // header token, to be ignored by Alice
// payload
payload[0] = eSSU2BlkDateTime;
htobe16buf (payload + 1, 4);
htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
size_t payloadSize = 7;
payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, ep);
payloadSize += CreateRelayResponseBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize,
eSSU2RelayResponseCodeAccept, m_Nonce, m_Token, ep.address ().is_v4 ());
payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize);
// encrypt
uint8_t n[12];
CreateNonce (be32toh (header.h.packetNum), n);
i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true);
payloadSize += 16;
header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24));
header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12));
memset (n, 0, 12);
i2p::crypto::ChaCha20 (h + 16, 16, addr->i, n, h + 16);
// send
GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep);
}
}
}

View File

@ -51,6 +51,21 @@ namespace transport
std::vector<uint8_t> m_SignedData; // for resends
boost::asio::deadline_timer m_PeerTestResendTimer;
};
class SSU2HolePunchSession: public SSU2Session // Charlie
{
public:
SSU2HolePunchSession (SSU2Server& server, uint32_t nonce, const boost::asio::ip::udp::endpoint& remoteEndpoint,
std::shared_ptr<const i2p::data::RouterInfo::Address> localAddr);
void SendHolePunch ();
private:
uint32_t m_Nonce;
uint64_t m_Token; // for RelayResponse block
};
}
}

View File

@ -1352,46 +1352,6 @@ namespace transport
return true;
}
void SSU2Session::SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep,
const uint8_t * introKey, uint64_t token)
{
// we are Charlie
LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep);
Header header;
uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE];
// fill packet
header.h.connID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id
RAND_bytes (header.buf + 8, 4); // random packet num
header.h.type = eSSU2HolePunch;
header.h.flags[0] = 2; // ver
header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID
header.h.flags[2] = 0; // flag
memcpy (h, header.buf, 16);
uint64_t c = ~header.h.connID;
memcpy (h + 16, &c, 8); // source id
RAND_bytes (h + 24, 8); // token
// payload
payload[0] = eSSU2BlkDateTime;
htobe16buf (payload + 1, 4);
htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
size_t payloadSize = 7;
payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, ep);
payloadSize += CreateRelayResponseBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize,
eSSU2RelayResponseCodeAccept, nonce, token, ep.address ().is_v4 ());
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
// encrypt
uint8_t n[12];
CreateNonce (be32toh (header.h.packetNum), n);
i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, introKey, n, payload, payloadSize + 16, true);
payloadSize += 16;
header.ll[0] ^= CreateHeaderMask (introKey, payload + (payloadSize - 24));
header.ll[1] ^= CreateHeaderMask (introKey, payload + (payloadSize - 12));
memset (n, 0, 12);
i2p::crypto::ChaCha20 (h + 16, 16, introKey, n, h + 16);
// send
m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep);
}
bool SSU2Session::ProcessHolePunch (uint8_t * buf, size_t len)
{
// we are Alice
@ -1984,7 +1944,6 @@ namespace transport
void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts)
{
// we are Charlie
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept;
uint64_t token = 0;
bool isV4 = false;
@ -2011,10 +1970,11 @@ namespace transport
{
if (m_Server.IsSupported (ep.address ()))
{
token = m_Server.GetIncomingToken (ep);
isV4 = ep.address ().is_v4 ();
SendHolePunch (bufbe32toh (buf + 33), ep, addr->i, token);
m_Server.AddConnectedRecently (ep, mts/1000);
auto holePunchSession = std::make_shared<SSU2HolePunchSession>(
m_Server, bufbe32toh (buf + 33), ep, addr);
m_Server.AddSession (holePunchSession);
holePunchSession->SendHolePunch ();
}
else
{

View File

@ -112,6 +112,7 @@ namespace transport
eSSU2SessionStateTerminated,
eSSU2SessionStateFailed,
eSSU2SessionStateIntroduced,
eSSU2SessionStateHolePunch,
eSSU2SessionStatePeerTest,
eSSU2SessionStateTokenRequestReceived
};
@ -296,6 +297,8 @@ namespace transport
size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen);
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4);
private:
void Terminate ();
@ -320,7 +323,6 @@ namespace transport
uint32_t SendData (const uint8_t * buf, size_t len, uint8_t flags = 0); // returns packet num
void SendQuickAck ();
void SendTermination ();
void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey, uint64_t token);
void SendPathResponse (const uint8_t * data, size_t len);
void SendPathChallenge ();
@ -352,7 +354,6 @@ namespace transport
size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg);
size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg, uint8_t& fragmentNum, uint32_t msgID);
size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen);
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice
size_t CreateTerminationBlock (uint8_t * buf, size_t len);