diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 433a3afb..0ebbe3a6 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -123,7 +123,10 @@ namespace transport payloadSize += CreateI2NPBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize, std::move (msg)); } else if (len > SSU2_MAX_PAYLOAD_SIZE - 32) // message too long - m_SendQueue.pop_front (); // drop it. TODO: fragmentation + { + m_SendQueue.pop_front (); + SendFragmentedMessage (msg); + } else { // send right a way @@ -147,6 +150,31 @@ namespace transport } } + void SSU2Session::SendFragmentedMessage (std::shared_ptr msg) + { + uint32_t msgID; + memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); + uint8_t payload[SSU2_MAX_PAYLOAD_SIZE]; + size_t payloadSize = 0; + payloadSize += CreateAckBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize); + auto size = CreateFirstFragmentBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - 32 - payloadSize, msg); + if (!size) return; + payloadSize += size; + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize); + auto firstPacket = SendData (payload, payloadSize); + if (firstPacket) + m_SentPackets.emplace (firstPacket->packetNum, firstPacket); + uint8_t fragmentNum = 0; + while (msg->offset < msg->len) + { + payloadSize += CreateFollowOnFragmentBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize - 16, msg, fragmentNum, msgID); + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize); + auto followonPacket = SendData (payload, payloadSize); + if (followonPacket) + m_SentPackets.emplace (followonPacket->packetNum, followonPacket); + } + } + void SSU2Session::Resend (uint64_t ts) { if (m_SendQueue.empty ()) return; @@ -1038,6 +1066,42 @@ namespace transport memcpy (buf + 3, msgBuf, msgLen); return msgLen + 3; } + + size_t SSU2Session::CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg) + { + if (len < 12) return 0; + msg->ToNTCP2 (); + auto msgBuf = msg->GetNTCP2Header (); + auto msgLen = msg->GetNTCP2Length (); + if (msgLen + 3 <= len) return 0; + msgLen = len - 3; + buf[0] = eSSU2BlkFirstFragment; + htobe16buf (buf + 1, msgLen); // size + memcpy (buf + 3, msgBuf, msgLen); + msg->offset = (msgBuf - buf) + msgLen; + return msgLen + 3; + } + + size_t SSU2Session::CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg, uint8_t& fragmentNum, uint32_t msgID) + { + if (len < 8) return 0; + bool isLast = true; + auto msgLen = msg->len - msg->offset; + if (msgLen + 8 > len) + { + msgLen = len - 8; + isLast = false; + } + buf[0] = eSSU2BlkFollowOnFragment; + htobe16buf (buf + 1, msgLen); // size + fragmentNum++; + buf[3] = fragmentNum << 1; + if (isLast) buf[3] |= 0x01; + memcpy (buf + 4, &msgID, 4); + memcpy (buf + 8, msg->buf + msg->offset, msgLen); + msg->offset += msgLen; + return msgLen + 8; + } std::shared_ptr SSU2Session::ExtractRouterInfo (const uint8_t * buf, size_t size) { diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index f9d0f848..f0fd50c6 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -153,6 +153,7 @@ namespace transport void Established (); void PostI2NPMessages (std::vector > msgs); void SendQueue (); + void SendFragmentedMessage (std::shared_ptr msg); void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len); void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len); @@ -182,6 +183,8 @@ namespace transport size_t CreateAckBlock (uint8_t * buf, size_t len); size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0); size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr&& msg); + size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg); + size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg, uint8_t& fragmentNum, uint32_t msgID); private: