mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-04-30 20:52:30 +02:00
Reformat code
This commit is contained in:
parent
3ddb370718
commit
55534ea002
140 changed files with 46068 additions and 48277 deletions
|
@ -13,504 +13,436 @@
|
|||
#include "SSU.h"
|
||||
#include "SSUData.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize)
|
||||
{
|
||||
if (msg->len + fragmentSize > msg->maxLen)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
|
||||
auto newMsg = NewI2NPMessage ();
|
||||
*newMsg = *msg;
|
||||
msg = newMsg;
|
||||
}
|
||||
if (msg->Concat (fragment, fragmentSize) < fragmentSize)
|
||||
LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
|
||||
nextFragmentNum++;
|
||||
}
|
||||
namespace i2p {
|
||||
namespace transport {
|
||||
void IncompleteMessage::AttachNextFragment(const uint8_t *fragment, size_t fragmentSize) {
|
||||
if (msg->len + fragmentSize > msg->maxLen) {
|
||||
LogPrint(eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
|
||||
auto newMsg = NewI2NPMessage();
|
||||
*newMsg = *msg;
|
||||
msg = newMsg;
|
||||
}
|
||||
if (msg->Concat(fragment, fragmentSize) < fragmentSize)
|
||||
LogPrint(eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
|
||||
nextFragmentNum++;
|
||||
}
|
||||
|
||||
SSUData::SSUData (SSUSession& session):
|
||||
m_Session (session), m_ResendTimer (session.GetService ()),
|
||||
m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
|
||||
m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0)
|
||||
{
|
||||
}
|
||||
SSUData::SSUData(SSUSession &session) :
|
||||
m_Session(session), m_ResendTimer(session.GetService()),
|
||||
m_MaxPacketSize(session.IsV6() ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
|
||||
m_PacketSize(m_MaxPacketSize), m_LastMessageReceivedTime(0) {
|
||||
}
|
||||
|
||||
SSUData::~SSUData ()
|
||||
{
|
||||
}
|
||||
SSUData::~SSUData() {
|
||||
}
|
||||
|
||||
void SSUData::Start ()
|
||||
{
|
||||
}
|
||||
void SSUData::Start() {
|
||||
}
|
||||
|
||||
void SSUData::Stop ()
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
m_IncompleteMessages.clear ();
|
||||
m_SentMessages.clear ();
|
||||
m_ReceivedMessages.clear ();
|
||||
}
|
||||
void SSUData::Stop() {
|
||||
m_ResendTimer.cancel();
|
||||
m_IncompleteMessages.clear();
|
||||
m_SentMessages.clear();
|
||||
m_ReceivedMessages.clear();
|
||||
}
|
||||
|
||||
void SSUData::AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter)
|
||||
{
|
||||
if (!remoteRouter) return;
|
||||
auto ssuAddress = remoteRouter->GetSSUAddress ();
|
||||
if (ssuAddress && ssuAddress->ssu->mtu)
|
||||
{
|
||||
if (m_Session.IsV6 ())
|
||||
m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
|
||||
else
|
||||
m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
|
||||
if (m_PacketSize > 0)
|
||||
{
|
||||
// make sure packet size multiple of 16
|
||||
m_PacketSize >>= 4;
|
||||
m_PacketSize <<= 4;
|
||||
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
|
||||
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
|
||||
m_PacketSize = m_MaxPacketSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
void SSUData::AdjustPacketSize(std::shared_ptr<const i2p::data::RouterInfo> remoteRouter) {
|
||||
if (!remoteRouter) return;
|
||||
auto ssuAddress = remoteRouter->GetSSUAddress();
|
||||
if (ssuAddress && ssuAddress->ssu->mtu) {
|
||||
if (m_Session.IsV6())
|
||||
m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
|
||||
else
|
||||
m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
|
||||
if (m_PacketSize > 0) {
|
||||
// make sure packet size multiple of 16
|
||||
m_PacketSize >>= 4;
|
||||
m_PacketSize <<= 4;
|
||||
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
|
||||
LogPrint(eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize);
|
||||
} else {
|
||||
LogPrint(eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
|
||||
m_PacketSize = m_MaxPacketSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent)
|
||||
{
|
||||
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
|
||||
if (routerInfo)
|
||||
AdjustPacketSize (routerInfo);
|
||||
}
|
||||
void SSUData::UpdatePacketSize(const i2p::data::IdentHash &remoteIdent) {
|
||||
auto routerInfo = i2p::data::netdb.FindRouter(remoteIdent);
|
||||
if (routerInfo)
|
||||
AdjustPacketSize(routerInfo);
|
||||
}
|
||||
|
||||
void SSUData::ProcessSentMessageAck (uint32_t msgID)
|
||||
{
|
||||
auto it = m_SentMessages.find (msgID);
|
||||
if (it != m_SentMessages.end ())
|
||||
{
|
||||
m_SentMessages.erase (it);
|
||||
if (m_SentMessages.empty ())
|
||||
m_ResendTimer.cancel ();
|
||||
}
|
||||
}
|
||||
void SSUData::ProcessSentMessageAck(uint32_t msgID) {
|
||||
auto it = m_SentMessages.find(msgID);
|
||||
if (it != m_SentMessages.end()) {
|
||||
m_SentMessages.erase(it);
|
||||
if (m_SentMessages.empty())
|
||||
m_ResendTimer.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag)
|
||||
{
|
||||
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
|
||||
{
|
||||
// explicit ACKs
|
||||
uint8_t numAcks =*buf;
|
||||
buf++;
|
||||
for (int i = 0; i < numAcks; i++)
|
||||
ProcessSentMessageAck (bufbe32toh (buf+i*4));
|
||||
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++)
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (buf);
|
||||
buf += 4; // msgID
|
||||
auto it = m_SentMessages.find (msgID);
|
||||
// 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 ())
|
||||
{
|
||||
int numSentFragments = it->second->fragments.size ();
|
||||
// process bits
|
||||
uint8_t mask = 0x01;
|
||||
for (int j = 0; j < 7; j++)
|
||||
{
|
||||
if (bitfield & mask)
|
||||
{
|
||||
if (fragment < numSentFragments)
|
||||
it->second->fragments[fragment] = nullptr;
|
||||
}
|
||||
fragment++;
|
||||
mask <<= 1;
|
||||
}
|
||||
}
|
||||
buf++;
|
||||
}
|
||||
while (isNonLast);
|
||||
}
|
||||
}
|
||||
}
|
||||
void SSUData::ProcessAcks(uint8_t *&buf, uint8_t flag) {
|
||||
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) {
|
||||
// explicit ACKs
|
||||
uint8_t numAcks = *buf;
|
||||
buf++;
|
||||
for (int i = 0; i < numAcks; i++)
|
||||
ProcessSentMessageAck(bufbe32toh(buf + i * 4));
|
||||
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++) {
|
||||
uint32_t msgID = bufbe32toh(buf);
|
||||
buf += 4; // msgID
|
||||
auto it = m_SentMessages.find(msgID);
|
||||
// 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()) {
|
||||
int numSentFragments = it->second->fragments.size();
|
||||
// process bits
|
||||
uint8_t mask = 0x01;
|
||||
for (int j = 0; j < 7; j++) {
|
||||
if (bitfield & mask) {
|
||||
if (fragment < numSentFragments)
|
||||
it->second->fragments[fragment] = nullptr;
|
||||
}
|
||||
fragment++;
|
||||
mask <<= 1;
|
||||
}
|
||||
}
|
||||
buf++;
|
||||
} while (isNonLast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::ProcessFragments (uint8_t * buf)
|
||||
{
|
||||
uint8_t numFragments = *buf; // number of fragments
|
||||
buf++;
|
||||
for (int i = 0; i < numFragments; i++)
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (buf); // message ID
|
||||
buf += 4;
|
||||
uint8_t frag[4] = {0};
|
||||
memcpy (frag + 1, buf, 3);
|
||||
buf += 3;
|
||||
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
|
||||
uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13
|
||||
bool isLast = fragmentInfo & 0x010000; // bit 16
|
||||
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
|
||||
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
|
||||
return;
|
||||
}
|
||||
void SSUData::ProcessFragments(uint8_t *buf) {
|
||||
uint8_t numFragments = *buf; // number of fragments
|
||||
buf++;
|
||||
for (int i = 0; i < numFragments; i++) {
|
||||
uint32_t msgID = bufbe32toh(buf); // message ID
|
||||
buf += 4;
|
||||
uint8_t frag[4] = {0};
|
||||
memcpy(frag + 1, buf, 3);
|
||||
buf += 3;
|
||||
uint32_t fragmentInfo = bufbe32toh(frag); // fragment info
|
||||
uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13
|
||||
bool isLast = fragmentInfo & 0x010000; // bit 16
|
||||
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
|
||||
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) {
|
||||
LogPrint(eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
|
||||
return;
|
||||
}
|
||||
|
||||
// find message with msgID
|
||||
auto it = m_IncompleteMessages.find (msgID);
|
||||
if (it == m_IncompleteMessages.end ())
|
||||
{
|
||||
// create new message
|
||||
auto msg = NewI2NPShortMessage ();
|
||||
msg->len -= I2NP_SHORT_HEADER_SIZE;
|
||||
it = m_IncompleteMessages.insert (std::make_pair (msgID,
|
||||
m_Session.GetServer ().GetIncompleteMessagesPool ().AcquireShared (std::move (msg)))).first;
|
||||
}
|
||||
auto& incompleteMessage = it->second;
|
||||
// mark fragment as received
|
||||
if (fragmentNum < 64)
|
||||
incompleteMessage->receivedFragmentsBits |= (uint64_t(0x01) << fragmentNum);
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
|
||||
// find message with msgID
|
||||
auto it = m_IncompleteMessages.find(msgID);
|
||||
if (it == m_IncompleteMessages.end()) {
|
||||
// create new message
|
||||
auto msg = NewI2NPShortMessage();
|
||||
msg->len -= I2NP_SHORT_HEADER_SIZE;
|
||||
it = m_IncompleteMessages.insert(std::make_pair(msgID,
|
||||
m_Session.GetServer().GetIncompleteMessagesPool().AcquireShared(
|
||||
std::move(msg)))).first;
|
||||
}
|
||||
auto &incompleteMessage = it->second;
|
||||
// mark fragment as received
|
||||
if (fragmentNum < 64)
|
||||
incompleteMessage->receivedFragmentsBits |= (uint64_t(0x01) << fragmentNum);
|
||||
else
|
||||
LogPrint(eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
|
||||
|
||||
// handle current fragment
|
||||
if (fragmentNum == incompleteMessage->nextFragmentNum)
|
||||
{
|
||||
// expected fragment
|
||||
incompleteMessage->AttachNextFragment (buf, fragmentSize);
|
||||
if (!isLast && !incompleteMessage->savedFragments.empty ())
|
||||
{
|
||||
// try saved fragments
|
||||
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
|
||||
{
|
||||
auto& savedFragment = *it1;
|
||||
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
|
||||
{
|
||||
incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len);
|
||||
isLast = savedFragment->isLast;
|
||||
incompleteMessage->savedFragments.erase (it1++);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (isLast)
|
||||
LogPrint (eLogDebug, "SSU: Message ", msgID, " complete");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fragmentNum < incompleteMessage->nextFragmentNum)
|
||||
// duplicate fragment
|
||||
LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored");
|
||||
else
|
||||
{
|
||||
// missing fragment
|
||||
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
|
||||
auto savedFragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (fragmentNum, buf, fragmentSize, isLast);
|
||||
if (incompleteMessage->savedFragments.insert (savedFragment).second)
|
||||
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
|
||||
}
|
||||
isLast = false;
|
||||
}
|
||||
// handle current fragment
|
||||
if (fragmentNum == incompleteMessage->nextFragmentNum) {
|
||||
// expected fragment
|
||||
incompleteMessage->AttachNextFragment(buf, fragmentSize);
|
||||
if (!isLast && !incompleteMessage->savedFragments.empty()) {
|
||||
// try saved fragments
|
||||
for (auto it1 = incompleteMessage->savedFragments.begin();
|
||||
it1 != incompleteMessage->savedFragments.end();) {
|
||||
auto &savedFragment = *it1;
|
||||
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) {
|
||||
incompleteMessage->AttachNextFragment(savedFragment->buf, savedFragment->len);
|
||||
isLast = savedFragment->isLast;
|
||||
incompleteMessage->savedFragments.erase(it1++);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (isLast)
|
||||
LogPrint(eLogDebug, "SSU: Message ", msgID, " complete");
|
||||
}
|
||||
} else {
|
||||
if (fragmentNum < incompleteMessage->nextFragmentNum)
|
||||
// duplicate fragment
|
||||
LogPrint(eLogWarning, "SSU: Duplicate fragment ", (int) fragmentNum, " of message ", msgID,
|
||||
", ignored");
|
||||
else {
|
||||
// missing fragment
|
||||
LogPrint(eLogWarning, "SSU: Missing fragments from ", (int) incompleteMessage->nextFragmentNum,
|
||||
" to ", fragmentNum - 1, " of message ", msgID);
|
||||
auto savedFragment = m_Session.GetServer().GetFragmentsPool().AcquireShared(fragmentNum, buf,
|
||||
fragmentSize,
|
||||
isLast);
|
||||
if (incompleteMessage->savedFragments.insert(savedFragment).second)
|
||||
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch();
|
||||
else
|
||||
LogPrint(eLogWarning, "SSU: Fragment ", (int) fragmentNum, " of message ", msgID,
|
||||
" already saved");
|
||||
}
|
||||
isLast = false;
|
||||
}
|
||||
|
||||
if (isLast)
|
||||
{
|
||||
// delete incomplete message
|
||||
auto msg = incompleteMessage->msg;
|
||||
incompleteMessage->msg = nullptr;
|
||||
m_IncompleteMessages.erase (msgID);
|
||||
// process message
|
||||
SendMsgAck (msgID);
|
||||
msg->FromSSU (msgID);
|
||||
if (m_Session.GetState () == eSessionStateEstablished)
|
||||
{
|
||||
if (!m_ReceivedMessages.count (msgID))
|
||||
{
|
||||
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
m_ReceivedMessages.emplace (msgID, m_LastMessageReceivedTime);
|
||||
if (!msg->IsExpired ())
|
||||
{
|
||||
m_Handler.PutNextMessage (std::move (msg));
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "SSU: message expired");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
|
||||
}
|
||||
else
|
||||
{
|
||||
// we expect DeliveryStatus
|
||||
if (msg->GetTypeID () == eI2NPDeliveryStatus)
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: session established");
|
||||
m_Session.Established ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ());
|
||||
}
|
||||
}
|
||||
else
|
||||
SendFragmentAck (msgID, incompleteMessage->receivedFragmentsBits);
|
||||
buf += fragmentSize;
|
||||
}
|
||||
}
|
||||
if (isLast) {
|
||||
// delete incomplete message
|
||||
auto msg = incompleteMessage->msg;
|
||||
incompleteMessage->msg = nullptr;
|
||||
m_IncompleteMessages.erase(msgID);
|
||||
// process message
|
||||
SendMsgAck(msgID);
|
||||
msg->FromSSU(msgID);
|
||||
if (m_Session.GetState() == eSessionStateEstablished) {
|
||||
if (!m_ReceivedMessages.count(msgID)) {
|
||||
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch();
|
||||
m_ReceivedMessages.emplace(msgID, m_LastMessageReceivedTime);
|
||||
if (!msg->IsExpired()) {
|
||||
m_Handler.PutNextMessage(std::move(msg));
|
||||
} else
|
||||
LogPrint(eLogDebug, "SSU: message expired");
|
||||
} else
|
||||
LogPrint(eLogWarning, "SSU: Message ", msgID, " already received");
|
||||
} else {
|
||||
// we expect DeliveryStatus
|
||||
if (msg->GetTypeID() == eI2NPDeliveryStatus) {
|
||||
LogPrint(eLogDebug, "SSU: session established");
|
||||
m_Session.Established();
|
||||
} else
|
||||
LogPrint(eLogError, "SSU: unexpected message ", (int) msg->GetTypeID());
|
||||
}
|
||||
} else
|
||||
SendFragmentAck(msgID, incompleteMessage->receivedFragmentsBits);
|
||||
buf += fragmentSize;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::FlushReceivedMessage ()
|
||||
{
|
||||
m_Handler.Flush ();
|
||||
}
|
||||
void SSUData::FlushReceivedMessage() {
|
||||
m_Handler.Flush();
|
||||
}
|
||||
|
||||
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
|
||||
{
|
||||
//uint8_t * start = buf;
|
||||
uint8_t flag = *buf;
|
||||
buf++;
|
||||
LogPrint (eLogDebug, "SSU: Process data, flags=", (int)flag, ", len=", len);
|
||||
// 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
|
||||
LogPrint (eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
|
||||
buf += extendedDataSize;
|
||||
}
|
||||
// process data
|
||||
ProcessFragments (buf);
|
||||
}
|
||||
void SSUData::ProcessMessage(uint8_t *buf, size_t len) {
|
||||
//uint8_t * start = buf;
|
||||
uint8_t flag = *buf;
|
||||
buf++;
|
||||
LogPrint(eLogDebug, "SSU: Process data, flags=", (int) flag, ", len=", len);
|
||||
// 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
|
||||
LogPrint(eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
|
||||
buf += extendedDataSize;
|
||||
}
|
||||
// process data
|
||||
ProcessFragments(buf);
|
||||
}
|
||||
|
||||
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
{
|
||||
uint32_t msgID = msg->ToSSU ();
|
||||
if (m_SentMessages.find (msgID) != m_SentMessages.end())
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent");
|
||||
return;
|
||||
}
|
||||
if (m_SentMessages.empty ()) // schedule resend at first message only
|
||||
ScheduleResend ();
|
||||
void SSUData::Send(std::shared_ptr<i2p::I2NPMessage> msg) {
|
||||
uint32_t msgID = msg->ToSSU();
|
||||
if (m_SentMessages.find(msgID) != m_SentMessages.end()) {
|
||||
LogPrint(eLogWarning, "SSU: message ", msgID, " already sent");
|
||||
return;
|
||||
}
|
||||
if (m_SentMessages.empty()) // schedule resend at first message only
|
||||
ScheduleResend();
|
||||
|
||||
auto ret = m_SentMessages.emplace (msgID, m_Session.GetServer ().GetSentMessagesPool ().AcquireShared ());
|
||||
auto& sentMessage = ret.first->second;
|
||||
if (ret.second)
|
||||
{
|
||||
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
|
||||
sentMessage->numResends = 0;
|
||||
}
|
||||
auto& fragments = sentMessage->fragments;
|
||||
size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
|
||||
size_t len = msg->GetLength ();
|
||||
uint8_t * msgBuf = msg->GetSSUHeader ();
|
||||
auto ret = m_SentMessages.emplace(msgID, m_Session.GetServer().GetSentMessagesPool().AcquireShared());
|
||||
auto &sentMessage = ret.first->second;
|
||||
if (ret.second) {
|
||||
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch() + RESEND_INTERVAL;
|
||||
sentMessage->numResends = 0;
|
||||
}
|
||||
auto &fragments = sentMessage->fragments;
|
||||
size_t payloadSize =
|
||||
m_PacketSize - sizeof(SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
|
||||
size_t len = msg->GetLength();
|
||||
uint8_t *msgBuf = msg->GetSSUHeader();
|
||||
|
||||
uint32_t fragmentNum = 0;
|
||||
while (len > 0 && fragmentNum <= 127)
|
||||
{
|
||||
auto fragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared ();
|
||||
fragment->fragmentNum = fragmentNum;
|
||||
uint8_t * payload = fragment->buf + sizeof (SSUHeader);
|
||||
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
|
||||
payload++;
|
||||
*payload = 1; // always 1 message fragment per message
|
||||
payload++;
|
||||
htobe32buf (payload, msgID);
|
||||
payload += 4;
|
||||
bool isLast = (len <= payloadSize) || fragmentNum == 127; // 127 fragments max
|
||||
size_t size = isLast ? len : payloadSize;
|
||||
uint32_t fragmentInfo = (fragmentNum << 17);
|
||||
if (isLast)
|
||||
fragmentInfo |= 0x010000;
|
||||
uint32_t fragmentNum = 0;
|
||||
while (len > 0 && fragmentNum <= 127) {
|
||||
auto fragment = m_Session.GetServer().GetFragmentsPool().AcquireShared();
|
||||
fragment->fragmentNum = fragmentNum;
|
||||
uint8_t *payload = fragment->buf + sizeof(SSUHeader);
|
||||
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
|
||||
payload++;
|
||||
*payload = 1; // always 1 message fragment per message
|
||||
payload++;
|
||||
htobe32buf(payload, msgID);
|
||||
payload += 4;
|
||||
bool isLast = (len <= payloadSize) || fragmentNum == 127; // 127 fragments max
|
||||
size_t size = isLast ? len : payloadSize;
|
||||
uint32_t fragmentInfo = (fragmentNum << 17);
|
||||
if (isLast)
|
||||
fragmentInfo |= 0x010000;
|
||||
|
||||
fragmentInfo |= size;
|
||||
fragmentInfo = htobe32 (fragmentInfo);
|
||||
memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3);
|
||||
payload += 3;
|
||||
memcpy (payload, msgBuf, size);
|
||||
fragmentInfo |= size;
|
||||
fragmentInfo = htobe32(fragmentInfo);
|
||||
memcpy(payload, (uint8_t * )(&fragmentInfo) + 1, 3);
|
||||
payload += 3;
|
||||
memcpy(payload, msgBuf, size);
|
||||
|
||||
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;
|
||||
}
|
||||
fragment->len = size;
|
||||
fragments.push_back (fragment);
|
||||
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;
|
||||
}
|
||||
fragment->len = size;
|
||||
fragments.push_back(fragment);
|
||||
|
||||
// encrypt message with session key
|
||||
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
|
||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, fragment->buf, size, buf);
|
||||
try
|
||||
{
|
||||
m_Session.Send (buf, size);
|
||||
}
|
||||
catch (boost::system::system_error& ec)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Can't send data fragment ", ec.what ());
|
||||
}
|
||||
if (!isLast)
|
||||
{
|
||||
len -= payloadSize;
|
||||
msgBuf += payloadSize;
|
||||
}
|
||||
else
|
||||
len = 0;
|
||||
fragmentNum++;
|
||||
}
|
||||
}
|
||||
// encrypt message with session key
|
||||
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
|
||||
m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, fragment->buf, size, buf);
|
||||
try {
|
||||
m_Session.Send(buf, size);
|
||||
}
|
||||
catch (boost::system::system_error &ec) {
|
||||
LogPrint(eLogWarning, "SSU: Can't send data fragment ", ec.what());
|
||||
}
|
||||
if (!isLast) {
|
||||
len -= payloadSize;
|
||||
msgBuf += payloadSize;
|
||||
} else
|
||||
len = 0;
|
||||
fragmentNum++;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::SendMsgAck (uint32_t msgID)
|
||||
{
|
||||
uint8_t buf[48 + 18] = {0}; // actual length is 44 = 37 + 7 but pad it to multiple of 16
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
|
||||
payload++;
|
||||
*payload = 1; // number of ACKs
|
||||
payload++;
|
||||
htobe32buf (payload, msgID); // msgID
|
||||
payload += 4;
|
||||
*payload = 0; // number of fragments
|
||||
void SSUData::SendMsgAck(uint32_t msgID) {
|
||||
uint8_t buf[48 + 18] = {0}; // actual length is 44 = 37 + 7 but pad it to multiple of 16
|
||||
uint8_t *payload = buf + sizeof(SSUHeader);
|
||||
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
|
||||
payload++;
|
||||
*payload = 1; // number of ACKs
|
||||
payload++;
|
||||
htobe32buf(payload, msgID); // msgID
|
||||
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);
|
||||
}
|
||||
// encrypt message with session key
|
||||
m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, buf, 48);
|
||||
m_Session.Send(buf, 48);
|
||||
}
|
||||
|
||||
void SSUData::SendFragmentAck (uint32_t msgID, uint64_t bits)
|
||||
{
|
||||
if (!bits) return;
|
||||
uint8_t buf[64 + 18] = {0};
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
|
||||
payload++;
|
||||
*payload = 1; // number of ACK bitfields
|
||||
payload++;
|
||||
// one ack
|
||||
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
|
||||
payload += 4;
|
||||
size_t len = 0;
|
||||
while (bits)
|
||||
{
|
||||
*payload = (bits & 0x7F); // next 7 bits
|
||||
bits >>= 7;
|
||||
if (bits) *payload &= 0x80; // 0x80 means non-last
|
||||
payload++; len++;
|
||||
}
|
||||
*payload = 0; // number of fragments
|
||||
len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4
|
||||
// encrypt message with session key
|
||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len);
|
||||
m_Session.Send (buf, len);
|
||||
}
|
||||
void SSUData::SendFragmentAck(uint32_t msgID, uint64_t bits) {
|
||||
if (!bits) return;
|
||||
uint8_t buf[64 + 18] = {0};
|
||||
uint8_t *payload = buf + sizeof(SSUHeader);
|
||||
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
|
||||
payload++;
|
||||
*payload = 1; // number of ACK bitfields
|
||||
payload++;
|
||||
// one ack
|
||||
*(uint32_t * )(payload) = htobe32(msgID); // msgID
|
||||
payload += 4;
|
||||
size_t len = 0;
|
||||
while (bits) {
|
||||
*payload = (bits & 0x7F); // next 7 bits
|
||||
bits >>= 7;
|
||||
if (bits) *payload &= 0x80; // 0x80 means non-last
|
||||
payload++;
|
||||
len++;
|
||||
}
|
||||
*payload = 0; // number of fragments
|
||||
len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4
|
||||
// encrypt message with session key
|
||||
m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, buf, len);
|
||||
m_Session.Send(buf, len);
|
||||
}
|
||||
|
||||
void SSUData::ScheduleResend()
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
|
||||
auto s = m_Session.shared_from_this();
|
||||
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
|
||||
{ s->m_Data.HandleResendTimer (ecode); });
|
||||
}
|
||||
void SSUData::ScheduleResend() {
|
||||
m_ResendTimer.cancel();
|
||||
m_ResendTimer.expires_from_now(boost::posix_time::seconds(RESEND_INTERVAL));
|
||||
auto s = m_Session.shared_from_this();
|
||||
m_ResendTimer.async_wait(
|
||||
[s](const boost::system::error_code &ecode) { s->m_Data.HandleResendTimer(ecode); });
|
||||
}
|
||||
|
||||
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
int numResent = 0;
|
||||
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
|
||||
{
|
||||
if (ts >= it->second->nextResendTime)
|
||||
{
|
||||
if (it->second->numResends < MAX_NUM_RESENDS)
|
||||
{
|
||||
for (auto& f: it->second->fragments)
|
||||
if (f)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, f->buf, f->len, buf);
|
||||
m_Session.Send (buf, f->len); // resend
|
||||
numResent++;
|
||||
}
|
||||
catch (boost::system::system_error& ec)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Can't resend message ", it->first, " data fragment: ", ec.what ());
|
||||
}
|
||||
}
|
||||
void SSUData::HandleResendTimer(const boost::system::error_code &ecode) {
|
||||
if (ecode != boost::asio::error::operation_aborted) {
|
||||
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch();
|
||||
int numResent = 0;
|
||||
for (auto it = m_SentMessages.begin(); it != m_SentMessages.end();) {
|
||||
if (ts >= it->second->nextResendTime) {
|
||||
if (it->second->numResends < MAX_NUM_RESENDS) {
|
||||
for (auto &f: it->second->fragments)
|
||||
if (f) {
|
||||
try {
|
||||
m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, f->buf, f->len, buf);
|
||||
m_Session.Send(buf, f->len); // resend
|
||||
numResent++;
|
||||
}
|
||||
catch (boost::system::system_error &ec) {
|
||||
LogPrint(eLogWarning, "SSU: Can't resend message ", it->first,
|
||||
" data fragment: ", ec.what());
|
||||
}
|
||||
}
|
||||
|
||||
it->second->numResends++;
|
||||
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU: message ", it->first, " has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted");
|
||||
it = m_SentMessages.erase (it);
|
||||
}
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (m_SentMessages.empty ()) return; // nothing to resend
|
||||
if (numResent < MAX_OUTGOING_WINDOW_SIZE)
|
||||
ScheduleResend ();
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated");
|
||||
m_Session.Close ();
|
||||
}
|
||||
}
|
||||
}
|
||||
it->second->numResends++;
|
||||
it->second->nextResendTime += it->second->numResends * RESEND_INTERVAL;
|
||||
++it;
|
||||
} else {
|
||||
LogPrint(eLogInfo, "SSU: message ", it->first, " has not been ACKed after ",
|
||||
MAX_NUM_RESENDS, " attempts, deleted");
|
||||
it = m_SentMessages.erase(it);
|
||||
}
|
||||
} else
|
||||
++it;
|
||||
}
|
||||
if (m_SentMessages.empty()) return; // nothing to resend
|
||||
if (numResent < MAX_OUTGOING_WINDOW_SIZE)
|
||||
ScheduleResend();
|
||||
else {
|
||||
LogPrint(eLogError, "SSU: resend window exceeds max size. Session terminated");
|
||||
m_Session.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::CleanUp (uint64_t ts)
|
||||
{
|
||||
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
|
||||
{
|
||||
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
|
||||
it = m_IncompleteMessages.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
void SSUData::CleanUp(uint64_t ts) {
|
||||
for (auto it = m_IncompleteMessages.begin(); it != m_IncompleteMessages.end();) {
|
||||
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) {
|
||||
LogPrint(eLogWarning, "SSU: message ", it->first, " was not completed in ",
|
||||
INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
|
||||
it = m_IncompleteMessages.erase(it);
|
||||
} else
|
||||
++it;
|
||||
}
|
||||
|
||||
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES || ts > m_LastMessageReceivedTime + DECAY_INTERVAL)
|
||||
// decay
|
||||
m_ReceivedMessages.clear ();
|
||||
else
|
||||
{
|
||||
// delete old received messages
|
||||
for (auto it = m_ReceivedMessages.begin (); it != m_ReceivedMessages.end ();)
|
||||
{
|
||||
if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT)
|
||||
it = m_ReceivedMessages.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_ReceivedMessages.size() > MAX_NUM_RECEIVED_MESSAGES ||
|
||||
ts > m_LastMessageReceivedTime + DECAY_INTERVAL)
|
||||
// decay
|
||||
m_ReceivedMessages.clear();
|
||||
else {
|
||||
// delete old received messages
|
||||
for (auto it = m_ReceivedMessages.begin(); it != m_ReceivedMessages.end();) {
|
||||
if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT)
|
||||
it = m_ReceivedMessages.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue