#include #include #include "Log.h" #include "Timestamp.h" #include "NetDb.h" #include "SSU.h" #include "SSUData.h" #include "Event.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++; } SSUData::SSUData (SSUSession& session): 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), m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0) { } SSUData::~SSUData () { } void SSUData::Start () { ScheduleIncompleteMessagesCleanup (); } void SSUData::Stop () { m_ResendTimer.cancel (); m_IncompleteMessagesCleanupTimer.cancel (); } void SSUData::AdjustPacketSize (std::shared_ptr remoteRouter) { if (remoteRouter) return; auto ssuAddress = remoteRouter->GetSSUAddress (); if (ssuAddress && ssuAddress->mtu) { if (m_Session.IsV6 ()) m_PacketSize = ssuAddress->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; else m_PacketSize = ssuAddress->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->mtu, " packet size=", m_PacketSize); } else { LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->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::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].reset (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]; frag[0] = 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, std::unique_ptr(new IncompleteMessage (msg)))).first; } std::unique_ptr& incompleteMessage = it->second; // 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 = new Fragment (fragmentNum, buf, fragmentSize, isLast); if (incompleteMessage->savedFragments.insert (std::unique_ptr(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_ReceivedMessages.insert (msgID); m_LastMessageReceivedTime = i2p::util::GetSinceEpoch