2020-05-22 13:18:41 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
|
|
|
*
|
|
|
|
* This file is part of Purple i2pd project and licensed under BSD3
|
|
|
|
*
|
|
|
|
* See full license text in LICENSE file at top of project tree
|
|
|
|
*/
|
|
|
|
|
2014-07-15 18:49:54 +00:00
|
|
|
#include <stdlib.h>
|
2014-04-22 15:39:26 +00:00
|
|
|
#include "Log.h"
|
2014-07-20 14:38:39 +00:00
|
|
|
#include "Timestamp.h"
|
2017-04-22 00:04:16 +00:00
|
|
|
#include "NetDb.hpp"
|
2014-04-22 15:39:26 +00:00
|
|
|
#include "SSU.h"
|
|
|
|
#include "SSUData.h"
|
|
|
|
|
|
|
|
namespace i2p
|
|
|
|
{
|
2014-10-21 16:25:53 +00:00
|
|
|
namespace transport
|
2014-04-22 15:39:26 +00:00
|
|
|
{
|
2015-03-11 18:34:39 +00:00
|
|
|
void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize)
|
|
|
|
{
|
|
|
|
if (msg->len + fragmentSize > msg->maxLen)
|
|
|
|
{
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
|
2015-11-24 18:09:12 +00:00
|
|
|
auto newMsg = NewI2NPMessage ();
|
2015-03-11 18:34:39 +00:00
|
|
|
*newMsg = *msg;
|
|
|
|
msg = newMsg;
|
|
|
|
}
|
2016-01-05 19:29:18 +00:00
|
|
|
if (msg->Concat (fragment, fragmentSize) < fragmentSize)
|
|
|
|
LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
|
2015-03-11 18:34:39 +00:00
|
|
|
nextFragmentNum++;
|
|
|
|
}
|
|
|
|
|
2014-04-22 15:39:26 +00:00
|
|
|
SSUData::SSUData (SSUSession& session):
|
2018-01-06 03:48:51 +00:00
|
|
|
m_Session (session), m_ResendTimer (session.GetService ()),
|
|
|
|
m_IncompleteMessagesCleanupTimer (session.GetService ()),
|
|
|
|
m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
|
2016-08-07 20:27:36 +00:00
|
|
|
m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0)
|
2014-04-22 15:39:26 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SSUData::~SSUData ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-02-08 13:50:05 +00:00
|
|
|
void SSUData::Start ()
|
|
|
|
{
|
|
|
|
ScheduleIncompleteMessagesCleanup ();
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
|
|
|
|
2015-02-07 22:58:29 +00:00
|
|
|
void SSUData::Stop ()
|
|
|
|
{
|
|
|
|
m_ResendTimer.cancel ();
|
2015-02-08 13:50:05 +00:00
|
|
|
m_IncompleteMessagesCleanupTimer.cancel ();
|
2017-03-05 22:08:20 +00:00
|
|
|
m_IncompleteMessages.clear ();
|
|
|
|
m_SentMessages.clear ();
|
|
|
|
m_ReceivedMessages.clear ();
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
|
|
|
|
2015-11-03 14:15:49 +00:00
|
|
|
void SSUData::AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter)
|
2014-09-18 18:37:29 +00:00
|
|
|
{
|
2017-01-19 00:59:25 +00:00
|
|
|
if (!remoteRouter) return;
|
2015-11-03 14:15:49 +00:00
|
|
|
auto ssuAddress = remoteRouter->GetSSUAddress ();
|
2017-01-02 21:36:59 +00:00
|
|
|
if (ssuAddress && ssuAddress->ssu->mtu)
|
2014-09-18 18:37:29 +00:00
|
|
|
{
|
2014-10-29 15:17:30 +00:00
|
|
|
if (m_Session.IsV6 ())
|
2017-01-02 21:36:59 +00:00
|
|
|
m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
|
2014-10-29 15:17:30 +00:00
|
|
|
else
|
2017-01-02 21:36:59 +00:00
|
|
|
m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
|
2014-09-18 18:37:29 +00:00
|
|
|
if (m_PacketSize > 0)
|
|
|
|
{
|
|
|
|
// make sure packet size multiple of 16
|
|
|
|
m_PacketSize >>= 4;
|
|
|
|
m_PacketSize <<= 4;
|
2014-10-29 15:17:30 +00:00
|
|
|
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
|
2017-01-02 21:36:59 +00:00
|
|
|
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize);
|
2014-09-18 18:37:29 +00:00
|
|
|
}
|
|
|
|
else
|
2018-01-06 03:48:51 +00:00
|
|
|
{
|
2017-01-02 21:36:59 +00:00
|
|
|
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
|
2014-10-29 15:17:30 +00:00
|
|
|
m_PacketSize = m_MaxPacketSize;
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
|
|
|
}
|
2014-09-18 18:37:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent)
|
|
|
|
{
|
2018-01-06 04:01:44 +00:00
|
|
|
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
|
2014-09-18 18:37:29 +00:00
|
|
|
if (routerInfo)
|
2015-11-03 14:15:49 +00:00
|
|
|
AdjustPacketSize (routerInfo);
|
2014-09-18 18:37:29 +00:00
|
|
|
}
|
|
|
|
|
2014-06-12 15:14:22 +00:00
|
|
|
void SSUData::ProcessSentMessageAck (uint32_t msgID)
|
|
|
|
{
|
|
|
|
auto it = m_SentMessages.find (msgID);
|
|
|
|
if (it != m_SentMessages.end ())
|
|
|
|
{
|
2018-01-06 03:48:51 +00:00
|
|
|
m_SentMessages.erase (it);
|
2014-07-20 14:38:39 +00:00
|
|
|
if (m_SentMessages.empty ())
|
|
|
|
m_ResendTimer.cancel ();
|
2014-06-12 15:14:22 +00:00
|
|
|
}
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-06-12 15:14:22 +00:00
|
|
|
|
2014-07-17 19:22:32 +00:00
|
|
|
void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag)
|
2014-04-22 15:39:26 +00:00
|
|
|
{
|
|
|
|
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
|
|
|
|
{
|
|
|
|
// explicit ACKs
|
|
|
|
uint8_t numAcks =*buf;
|
|
|
|
buf++;
|
2014-06-12 15:14:22 +00:00
|
|
|
for (int i = 0; i < numAcks; i++)
|
2014-12-29 22:04:02 +00:00
|
|
|
ProcessSentMessageAck (bufbe32toh (buf+i*4));
|
2014-04-22 15:39:26 +00:00
|
|
|
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++)
|
|
|
|
{
|
2014-12-29 22:04:02 +00:00
|
|
|
uint32_t msgID = bufbe32toh (buf);
|
2014-04-22 15:39:26 +00:00
|
|
|
buf += 4; // msgID
|
2018-01-06 03:48:51 +00:00
|
|
|
auto it = m_SentMessages.find (msgID);
|
2014-07-17 19:00:33 +00:00
|
|
|
// process individual Ack bitfields
|
|
|
|
bool isNonLast = false;
|
|
|
|
int fragment = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
uint8_t bitfield = *buf;
|
|
|
|
isNonLast = bitfield & 0x80;
|
|
|
|
bitfield &= 0x7F; // clear MSB
|
|
|
|
if (bitfield && it != m_SentMessages.end ())
|
2018-01-06 03:48:51 +00:00
|
|
|
{
|
|
|
|
int numSentFragments = it->second->fragments.size ();
|
2014-07-17 19:00:33 +00:00
|
|
|
// process bits
|
2014-09-12 16:20:17 +00:00
|
|
|
uint8_t mask = 0x01;
|
2014-07-17 19:00:33 +00:00
|
|
|
for (int j = 0; j < 7; j++)
|
2018-01-06 03:48:51 +00:00
|
|
|
{
|
2014-07-17 19:00:33 +00:00
|
|
|
if (bitfield & mask)
|
|
|
|
{
|
|
|
|
if (fragment < numSentFragments)
|
2015-01-31 01:30:39 +00:00
|
|
|
it->second->fragments[fragment].reset (nullptr);
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-07-17 19:00:33 +00:00
|
|
|
fragment++;
|
2014-09-12 16:20:17 +00:00
|
|
|
mask <<= 1;
|
2014-07-17 19:00:33 +00:00
|
|
|
}
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-04-22 15:39:26 +00:00
|
|
|
buf++;
|
2014-07-17 19:00:33 +00:00
|
|
|
}
|
2018-01-06 03:48:51 +00:00
|
|
|
while (isNonLast);
|
|
|
|
}
|
|
|
|
}
|
2014-07-17 19:22:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SSUData::ProcessFragments (uint8_t * buf)
|
|
|
|
{
|
2014-04-22 15:39:26 +00:00
|
|
|
uint8_t numFragments = *buf; // number of fragments
|
|
|
|
buf++;
|
|
|
|
for (int i = 0; i < numFragments; i++)
|
2018-01-06 03:48:51 +00:00
|
|
|
{
|
2014-12-29 22:04:02 +00:00
|
|
|
uint32_t msgID = bufbe32toh (buf); // message ID
|
2014-04-22 15:39:26 +00:00
|
|
|
buf += 4;
|
2017-01-08 16:09:33 +00:00
|
|
|
uint8_t frag[4] = {0};
|
2014-04-22 15:39:26 +00:00
|
|
|
memcpy (frag + 1, buf, 3);
|
|
|
|
buf += 3;
|
2014-12-29 22:04:02 +00:00
|
|
|
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
|
2016-02-02 23:27:52 +00:00
|
|
|
uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13
|
2018-01-06 03:48:51 +00:00
|
|
|
bool isLast = fragmentInfo & 0x010000; // bit 16
|
|
|
|
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
|
2014-11-03 15:15:01 +00:00
|
|
|
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
|
|
|
|
{
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
|
2014-11-03 15:15:01 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-07-15 02:06:58 +00:00
|
|
|
|
|
|
|
// find message with msgID
|
2015-02-15 03:24:10 +00:00
|
|
|
auto it = m_IncompleteMessages.find (msgID);
|
2018-01-06 03:48:51 +00:00
|
|
|
if (it == m_IncompleteMessages.end ())
|
2014-04-22 15:39:26 +00:00
|
|
|
{
|
2014-07-15 02:06:58 +00:00
|
|
|
// create new message
|
2015-11-24 18:09:12 +00:00
|
|
|
auto msg = NewI2NPShortMessage ();
|
2015-01-02 04:00:33 +00:00
|
|
|
msg->len -= I2NP_SHORT_HEADER_SIZE;
|
2018-01-06 03:48:51 +00:00
|
|
|
it = m_IncompleteMessages.insert (std::make_pair (msgID,
|
2015-02-15 03:24:10 +00:00
|
|
|
std::unique_ptr<IncompleteMessage>(new IncompleteMessage (msg)))).first;
|
|
|
|
}
|
|
|
|
std::unique_ptr<IncompleteMessage>& incompleteMessage = it->second;
|
2014-04-22 15:39:26 +00:00
|
|
|
|
2014-07-15 02:06:58 +00:00
|
|
|
// handle current fragment
|
|
|
|
if (fragmentNum == incompleteMessage->nextFragmentNum)
|
|
|
|
{
|
|
|
|
// expected fragment
|
2015-03-11 18:34:39 +00:00
|
|
|
incompleteMessage->AttachNextFragment (buf, fragmentSize);
|
2014-07-15 02:06:58 +00:00
|
|
|
if (!isLast && !incompleteMessage->savedFragments.empty ())
|
2014-04-22 15:39:26 +00:00
|
|
|
{
|
2014-07-15 02:06:58 +00:00
|
|
|
// try saved fragments
|
|
|
|
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
|
2014-04-22 15:39:26 +00:00
|
|
|
{
|
2015-02-05 17:13:37 +00:00
|
|
|
auto& savedFragment = *it1;
|
2014-07-15 02:06:58 +00:00
|
|
|
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
|
2014-04-22 15:39:26 +00:00
|
|
|
{
|
2015-03-11 18:34:39 +00:00
|
|
|
incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len);
|
2014-07-15 02:06:58 +00:00
|
|
|
isLast = savedFragment->isLast;
|
|
|
|
incompleteMessage->savedFragments.erase (it1++);
|
|
|
|
}
|
2014-04-22 15:39:26 +00:00
|
|
|
else
|
2014-07-15 02:06:58 +00:00
|
|
|
break;
|
2014-07-15 12:03:45 +00:00
|
|
|
}
|
|
|
|
if (isLast)
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogDebug, "SSU: Message ", msgID, " complete");
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
|
|
|
}
|
2014-07-15 02:06:58 +00:00
|
|
|
else
|
2018-01-06 03:48:51 +00:00
|
|
|
{
|
2014-07-15 02:06:58 +00:00
|
|
|
if (fragmentNum < incompleteMessage->nextFragmentNum)
|
|
|
|
// duplicate fragment
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored");
|
2014-07-15 02:06:58 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// missing fragment
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
|
2014-07-15 12:03:45 +00:00
|
|
|
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast);
|
2015-02-08 13:50:05 +00:00
|
|
|
if (incompleteMessage->savedFragments.insert (std::unique_ptr<Fragment>(savedFragment)).second)
|
|
|
|
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
|
2018-01-06 03:48:51 +00:00
|
|
|
else
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
|
2014-04-22 15:39:26 +00:00
|
|
|
}
|
2014-07-15 02:06:58 +00:00
|
|
|
isLast = false;
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-07-15 02:06:58 +00:00
|
|
|
|
|
|
|
if (isLast)
|
|
|
|
{
|
|
|
|
// delete incomplete message
|
2015-03-11 18:34:39 +00:00
|
|
|
auto msg = incompleteMessage->msg;
|
2015-02-05 17:13:37 +00:00
|
|
|
incompleteMessage->msg = nullptr;
|
2018-01-06 03:48:51 +00:00
|
|
|
m_IncompleteMessages.erase (msgID);
|
2014-07-15 02:06:58 +00:00
|
|
|
// process message
|
|
|
|
SendMsgAck (msgID);
|
|
|
|
msg->FromSSU (msgID);
|
|
|
|
if (m_Session.GetState () == eSessionStateEstablished)
|
2014-09-30 00:08:26 +00:00
|
|
|
{
|
|
|
|
if (!m_ReceivedMessages.count (msgID))
|
2018-01-06 03:48:51 +00:00
|
|
|
{
|
2014-09-30 00:08:26 +00:00
|
|
|
m_ReceivedMessages.insert (msgID);
|
2016-11-01 14:26:40 +00:00
|
|
|
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
|
2018-01-06 03:48:51 +00:00
|
|
|
if (!msg->IsExpired ())
|
2016-11-01 17:57:25 +00:00
|
|
|
{
|
2016-01-19 02:13:43 +00:00
|
|
|
m_Handler.PutNextMessage (msg);
|
2016-11-01 14:26:40 +00:00
|
|
|
}
|
2016-01-19 02:13:43 +00:00
|
|
|
else
|
2016-06-27 13:00:00 +00:00
|
|
|
LogPrint (eLogDebug, "SSU: message expired");
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-09-30 00:08:26 +00:00
|
|
|
else
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-07-15 02:06:58 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// we expect DeliveryStatus
|
2015-01-02 04:00:33 +00:00
|
|
|
if (msg->GetTypeID () == eI2NPDeliveryStatus)
|
2014-07-15 02:06:58 +00:00
|
|
|
{
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogDebug, "SSU: session established");
|
2014-07-15 02:06:58 +00:00
|
|
|
m_Session.Established ();
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-07-15 02:06:58 +00:00
|
|
|
else
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ());
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
|
|
|
}
|
2014-07-15 18:49:54 +00:00
|
|
|
else
|
2018-01-06 03:48:51 +00:00
|
|
|
SendFragmentAck (msgID, fragmentNum);
|
2014-04-22 15:39:26 +00:00
|
|
|
buf += fragmentSize;
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-04-22 15:39:26 +00:00
|
|
|
}
|
|
|
|
|
2015-02-15 19:17:55 +00:00
|
|
|
void SSUData::FlushReceivedMessage ()
|
|
|
|
{
|
|
|
|
m_Handler.Flush ();
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
|
|
|
|
2014-07-17 19:22:32 +00:00
|
|
|
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
|
|
|
|
{
|
|
|
|
//uint8_t * start = buf;
|
|
|
|
uint8_t flag = *buf;
|
|
|
|
buf++;
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogDebug, "SSU: Process data, flags=", (int)flag, ", len=", len);
|
2014-07-17 19:22:32 +00:00
|
|
|
// process acks if presented
|
|
|
|
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
|
|
|
|
ProcessAcks (buf, flag);
|
|
|
|
// extended data if presented
|
|
|
|
if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED)
|
|
|
|
{
|
|
|
|
uint8_t extendedDataSize = *buf;
|
|
|
|
buf++; // size
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
|
2014-07-17 19:22:32 +00:00
|
|
|
buf += extendedDataSize;
|
|
|
|
}
|
|
|
|
// process data
|
|
|
|
ProcessFragments (buf);
|
|
|
|
}
|
|
|
|
|
2015-06-17 14:47:26 +00:00
|
|
|
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg)
|
2014-06-10 14:39:29 +00:00
|
|
|
{
|
2014-06-12 15:14:22 +00:00
|
|
|
uint32_t msgID = msg->ToSSU ();
|
2020-08-08 17:34:27 +00:00
|
|
|
if (m_SentMessages.find (msgID) != m_SentMessages.end())
|
2014-07-06 21:48:16 +00:00
|
|
|
{
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent");
|
2014-07-06 21:48:16 +00:00
|
|
|
return;
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-07-20 14:38:39 +00:00
|
|
|
if (m_SentMessages.empty ()) // schedule resend at first message only
|
|
|
|
ScheduleResend ();
|
2018-01-06 03:48:51 +00:00
|
|
|
|
|
|
|
auto ret = m_SentMessages.insert (std::make_pair (msgID, std::unique_ptr<SentMessage>(new SentMessage)));
|
2015-02-15 03:24:10 +00:00
|
|
|
std::unique_ptr<SentMessage>& sentMessage = ret.first->second;
|
2018-01-06 03:48:51 +00:00
|
|
|
if (ret.second)
|
2015-02-15 03:24:10 +00:00
|
|
|
{
|
|
|
|
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
|
|
|
|
sentMessage->numResends = 0;
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-07-20 14:38:39 +00:00
|
|
|
auto& fragments = sentMessage->fragments;
|
2018-01-06 03:48:51 +00:00
|
|
|
size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
|
2014-06-10 14:39:29 +00:00
|
|
|
size_t len = msg->GetLength ();
|
|
|
|
uint8_t * msgBuf = msg->GetSSUHeader ();
|
|
|
|
|
|
|
|
uint32_t fragmentNum = 0;
|
2018-10-11 15:17:14 +00:00
|
|
|
while (len > 0 && fragmentNum <= 127)
|
2018-01-06 03:48:51 +00:00
|
|
|
{
|
2014-07-20 14:38:39 +00:00
|
|
|
Fragment * fragment = new Fragment;
|
2014-09-14 15:54:18 +00:00
|
|
|
fragment->fragmentNum = fragmentNum;
|
2020-08-08 23:01:55 +00:00
|
|
|
uint8_t * payload = fragment->buf + sizeof (SSUHeader);
|
2014-06-10 14:39:29 +00:00
|
|
|
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
|
|
|
|
payload++;
|
|
|
|
*payload = 1; // always 1 message fragment per message
|
|
|
|
payload++;
|
2015-02-02 21:08:35 +00:00
|
|
|
htobe32buf (payload, msgID);
|
2014-06-10 14:39:29 +00:00
|
|
|
payload += 4;
|
2018-10-11 15:17:14 +00:00
|
|
|
bool isLast = (len <= payloadSize) || fragmentNum == 127; // 127 fragments max
|
2014-06-10 14:39:29 +00:00
|
|
|
size_t size = isLast ? len : payloadSize;
|
|
|
|
uint32_t fragmentInfo = (fragmentNum << 17);
|
|
|
|
if (isLast)
|
|
|
|
fragmentInfo |= 0x010000;
|
2018-01-06 03:48:51 +00:00
|
|
|
|
2014-06-10 14:39:29 +00:00
|
|
|
fragmentInfo |= size;
|
|
|
|
fragmentInfo = htobe32 (fragmentInfo);
|
|
|
|
memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3);
|
|
|
|
payload += 3;
|
|
|
|
memcpy (payload, msgBuf, size);
|
2018-01-06 03:48:51 +00:00
|
|
|
|
2020-08-08 23:01:55 +00:00
|
|
|
size += payload - fragment->buf;
|
|
|
|
uint8_t rem = size & 0x0F;
|
|
|
|
if (rem) // make sure 16 bytes boundary
|
|
|
|
{
|
|
|
|
auto padding = 16 - rem;
|
|
|
|
memset (fragment->buf + size, 0, padding);
|
|
|
|
size += padding;
|
|
|
|
}
|
2018-01-06 03:48:51 +00:00
|
|
|
fragment->len = size;
|
2015-01-31 01:30:39 +00:00
|
|
|
fragments.push_back (std::unique_ptr<Fragment> (fragment));
|
2018-01-06 03:48:51 +00:00
|
|
|
|
2014-06-10 14:39:29 +00:00
|
|
|
// encrypt message with session key
|
2020-08-08 23:01:55 +00:00
|
|
|
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
|
|
|
|
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, fragment->buf, size, buf);
|
2015-02-14 22:23:15 +00:00
|
|
|
try
|
2018-01-06 03:48:51 +00:00
|
|
|
{
|
2015-02-14 22:23:15 +00:00
|
|
|
m_Session.Send (buf, size);
|
|
|
|
}
|
|
|
|
catch (boost::system::system_error& ec)
|
|
|
|
{
|
2016-01-28 02:54:42 +00:00
|
|
|
LogPrint (eLogWarning, "SSU: Can't send data fragment ", ec.what ());
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-06-10 14:39:29 +00:00
|
|
|
if (!isLast)
|
2018-01-06 03:48:51 +00:00
|
|
|
{
|
2014-06-10 14:39:29 +00:00
|
|
|
len -= payloadSize;
|
|
|
|
msgBuf += payloadSize;
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-06-10 14:39:29 +00:00
|
|
|
else
|
|
|
|
len = 0;
|
|
|
|
fragmentNum++;
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
|
|
|
}
|
2014-06-10 15:19:31 +00:00
|
|
|
|
|
|
|
void SSUData::SendMsgAck (uint32_t msgID)
|
|
|
|
{
|
2017-01-08 16:09:33 +00:00
|
|
|
uint8_t buf[48 + 18] = {0}; // actual length is 44 = 37 + 7 but pad it to multiple of 16
|
2014-06-10 15:19:31 +00:00
|
|
|
uint8_t * payload = buf + sizeof (SSUHeader);
|
|
|
|
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
|
|
|
|
payload++;
|
|
|
|
*payload = 1; // number of ACKs
|
|
|
|
payload++;
|
2015-11-03 14:15:49 +00:00
|
|
|
htobe32buf (payload, msgID); // msgID
|
2014-06-10 15:19:31 +00:00
|
|
|
payload += 4;
|
|
|
|
*payload = 0; // number of fragments
|
|
|
|
|
|
|
|
// encrypt message with session key
|
|
|
|
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
|
|
|
|
m_Session.Send (buf, 48);
|
|
|
|
}
|
2014-07-15 18:49:54 +00:00
|
|
|
|
|
|
|
void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum)
|
|
|
|
{
|
|
|
|
if (fragmentNum > 64)
|
|
|
|
{
|
2015-12-18 14:06:18 +00:00
|
|
|
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
|
2014-07-15 18:49:54 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-01-08 16:09:33 +00:00
|
|
|
uint8_t buf[64 + 18] = {0};
|
2014-07-15 18:49:54 +00:00
|
|
|
uint8_t * payload = buf + sizeof (SSUHeader);
|
|
|
|
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
|
2018-01-06 03:48:51 +00:00
|
|
|
payload++;
|
2014-07-15 18:49:54 +00:00
|
|
|
*payload = 1; // number of ACK bitfields
|
|
|
|
payload++;
|
|
|
|
// one ack
|
2018-01-06 03:48:51 +00:00
|
|
|
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
|
2014-07-15 18:49:54 +00:00
|
|
|
payload += 4;
|
|
|
|
div_t d = div (fragmentNum, 7);
|
|
|
|
memset (payload, 0x80, d.quot); // 0x80 means non-last
|
2018-01-06 03:48:51 +00:00
|
|
|
payload += d.quot;
|
2014-09-12 16:20:17 +00:00
|
|
|
*payload = 0x01 << d.rem; // set corresponding bit
|
2014-07-15 18:49:54 +00:00
|
|
|
payload++;
|
|
|
|
*payload = 0; // number of fragments
|
|
|
|
|
2018-01-06 03:48:51 +00:00
|
|
|
size_t len = d.quot < 4 ? 48 : 64; // 48 = 37 + 7 + 4 (3+1)
|
2014-07-15 18:49:54 +00:00
|
|
|
// encrypt message with session key
|
|
|
|
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len);
|
|
|
|
m_Session.Send (buf, len);
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2014-07-15 18:49:54 +00:00
|
|
|
|
2014-07-20 14:38:39 +00:00
|
|
|
void SSUData::ScheduleResend()
|
2018-01-06 03:48:51 +00:00
|
|
|
{
|
2014-07-20 14:38:39 +00:00
|
|
|
m_ResendTimer.cancel ();
|
|
|
|
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
|
2014-11-24 17:26:11 +00:00
|
|
|
auto s = m_Session.shared_from_this();
|
|
|
|
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
|
|
|
|
{ s->m_Data.HandleResendTimer (ecode); });
|
2014-07-20 14:38:39 +00:00
|
|
|
}
|
2015-02-08 13:50:05 +00:00
|
|
|
|
2014-07-20 14:38:39 +00:00
|
|
|
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
|
|
|
|
{
|
|
|
|
if (ecode != boost::asio::error::operation_aborted)
|
|
|
|
{
|
2020-08-08 23:01:55 +00:00
|
|
|
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
|
2014-07-20 14:38:39 +00:00
|
|
|
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
2016-06-26 21:03:04 +00:00
|
|
|
int numResent = 0;
|
2015-02-08 13:50:05 +00:00
|
|
|
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
|
2014-07-20 14:38:39 +00:00
|
|
|
{
|
2015-02-08 13:50:05 +00:00
|
|
|
if (ts >= it->second->nextResendTime)
|
2018-01-06 03:48:51 +00:00
|
|
|
{
|
2015-02-08 13:50:05 +00:00
|
|
|
if (it->second->numResends < MAX_NUM_RESENDS)
|
2016-08-08 22:53:37 +00:00
|
|
|
{
|
2015-02-08 13:50:05 +00:00
|
|
|
for (auto& f: it->second->fragments)
|
2016-08-08 22:53:37 +00:00
|
|
|
if (f)
|
2015-02-14 22:23:15 +00:00
|
|
|
{
|
|
|
|
try
|
2020-08-08 23:01:55 +00:00
|
|
|
{
|
|
|
|
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, f->buf, f->len, buf);
|
|
|
|
m_Session.Send (buf, f->len); // resend
|
2016-06-26 21:03:04 +00:00
|
|
|
numResent++;
|
2015-02-14 22:23:15 +00:00
|
|
|
}
|
|
|
|
catch (boost::system::system_error& ec)
|
|
|
|
{
|
2020-03-20 14:44:53 +00:00
|
|
|
LogPrint (eLogWarning, "SSU: Can't resend message ", it->first, " data fragment: ", ec.what ());
|
2015-02-14 22:23:15 +00:00
|
|
|
}
|
2016-08-08 22:53:37 +00:00
|
|
|
}
|
2014-07-20 14:38:39 +00:00
|
|
|
|
2015-02-08 13:50:05 +00:00
|
|
|
it->second->numResends++;
|
|
|
|
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
|
2016-08-08 22:53:37 +00:00
|
|
|
++it;
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2015-02-08 13:50:05 +00:00
|
|
|
else
|
|
|
|
{
|
2020-03-20 14:44:53 +00:00
|
|
|
LogPrint (eLogInfo, "SSU: message ", it->first, " has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted");
|
2015-02-08 13:50:05 +00:00
|
|
|
it = m_SentMessages.erase (it);
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-08 13:50:05 +00:00
|
|
|
else
|
2016-08-08 22:53:37 +00:00
|
|
|
++it;
|
2014-07-20 14:38:39 +00:00
|
|
|
}
|
2017-02-11 23:18:37 +00:00
|
|
|
if (m_SentMessages.empty ()) return; // nothing to resend
|
2016-06-26 21:03:04 +00:00
|
|
|
if (numResent < MAX_OUTGOING_WINDOW_SIZE)
|
|
|
|
ScheduleResend ();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated");
|
|
|
|
m_Session.Close ();
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-02-08 13:50:05 +00:00
|
|
|
|
|
|
|
void SSUData::ScheduleIncompleteMessagesCleanup ()
|
|
|
|
{
|
|
|
|
m_IncompleteMessagesCleanupTimer.cancel ();
|
|
|
|
m_IncompleteMessagesCleanupTimer.expires_from_now (boost::posix_time::seconds(INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT));
|
|
|
|
auto s = m_Session.shared_from_this();
|
|
|
|
m_IncompleteMessagesCleanupTimer.async_wait ([s](const boost::system::error_code& ecode)
|
|
|
|
{ s->m_Data.HandleIncompleteMessagesCleanupTimer (ecode); });
|
|
|
|
}
|
2018-01-06 03:48:51 +00:00
|
|
|
|
2015-02-08 13:50:05 +00:00
|
|
|
void SSUData::HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode)
|
|
|
|
{
|
|
|
|
if (ecode != boost::asio::error::operation_aborted)
|
|
|
|
{
|
|
|
|
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
2015-02-15 03:24:10 +00:00
|
|
|
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
|
2015-02-08 13:50:05 +00:00
|
|
|
{
|
|
|
|
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
|
|
|
|
{
|
2020-03-20 14:44:53 +00:00
|
|
|
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
|
2015-02-15 03:24:10 +00:00
|
|
|
it = m_IncompleteMessages.erase (it);
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2015-02-08 13:50:05 +00:00
|
|
|
else
|
2016-08-08 22:53:37 +00:00
|
|
|
++it;
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
2016-08-07 20:27:36 +00:00
|
|
|
// decay
|
|
|
|
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES ||
|
2020-03-01 10:25:50 +00:00
|
|
|
i2p::util::GetSecondsSinceEpoch () > m_LastMessageReceivedTime + DECAY_INTERVAL)
|
2016-08-07 20:27:36 +00:00
|
|
|
m_ReceivedMessages.clear ();
|
2018-01-06 03:48:51 +00:00
|
|
|
|
2015-02-08 13:50:05 +00:00
|
|
|
ScheduleIncompleteMessagesCleanup ();
|
2018-01-06 03:48:51 +00:00
|
|
|
}
|
|
|
|
}
|
2014-04-22 15:39:26 +00:00
|
|
|
}
|
|
|
|
}
|