I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort) { I2NPMessage * msg = NewI2NPMessage (); CryptoPP::Gzip compressor; // default level compressor.Put (payload, len); compressor.MessageEnd(); int size = compressor.MaxRetrievable (); uint8_t * buf = msg->GetPayload (); htobe32buf (buf, size); // length buf += 4; compressor.Get (buf, size); htobe16buf (buf + 4, fromPort); // source port htobe16buf (buf + 6, toPort); // destination port buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol msg->len += size + 4; FillI2NPMessageHeader (msg, eI2NPData); return msg; }
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len) { auto msg = NewI2NPMessage (len); uint8_t * payload = msg->GetPayload (); htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); msg->len += TUNNEL_GATEWAY_HEADER_SIZE; if (msg->Concat (buf, len) < len) LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen); msg->FillI2NPMessageHeader (eI2NPTunnelGateway); return msg; }
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg) { if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE) { // message is capable to be used without copying uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE; htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); int len = msg->GetLength (); htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE); msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len; msg->FillI2NPMessageHeader (eI2NPTunnelGateway); return msg; } else return CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ()); }
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID) { auto msg = NewI2NPMessage (len); size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; msg->offset += gatewayMsgOffset; msg->len += gatewayMsgOffset; if (msg->Concat (buf, len) < len) LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen); msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message len = msg->GetLength (); msg->offset -= gatewayMsgOffset; uint8_t * payload = msg->GetPayload (); htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); msg->FillI2NPMessageHeader (eI2NPTunnelGateway); // gateway message return msg; }
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers) { auto m = excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage (); uint8_t * buf = m->GetPayload (); memcpy (buf, key, 32); // key buf += 32; memcpy (buf, from, 32); // from buf += 32; uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP; if (replyTunnelID) { *buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag htobe32buf (buf+1, replyTunnelID); buf += 5; } else { *buf = flag; // flag buf++; } if (excludedPeers) { int cnt = excludedPeers->size (); htobe16buf (buf, cnt); buf += 2; for (auto& it: *excludedPeers) { memcpy (buf, it, 32); buf += 32; } } else { // nothing to exclude htobuf16 (buf, 0); buf += 2; } m->len += (buf - m->GetPayload ()); m->FillI2NPMessageHeader (eI2NPDatabaseLookup); return m; }
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router, uint32_t replyToken) { if (!router) // we send own RouterInfo router = context.GetSharedRouterInfo (); auto m = NewI2NPShortMessage (); uint8_t * payload = m->GetPayload (); memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32); payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE; if (replyToken) { memset (buf, 0, 4); // zero tunnelID means direct reply buf += 4; memcpy (buf, router->GetIdentHash (), 32); buf += 32; } uint8_t * sizePtr = buf; buf += 2; m->len += (buf - payload); // payload size i2p::data::GzipDeflator deflator; size_t size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len); if (size) { htobe16buf (sizePtr, size); // size m->len += size; } else m = nullptr; if (m) m->FillI2NPMessageHeader (eI2NPDatabaseStore); return m; }
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, const std::set<i2p::data::IdentHash>& excludedFloodfills, const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag) { int cnt = excludedFloodfills.size (); auto m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage (); uint8_t * buf = m->GetPayload (); memcpy (buf, dest, 32); // key buf += 32; memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW buf += 32; *buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID buf += 5; // excluded htobe16buf (buf, cnt); buf += 2; if (cnt > 0) { for (auto& it: excludedFloodfills) { memcpy (buf, it, 32); buf += 32; } } // encryption memcpy (buf, replyKey, 32); buf[32] = 1; // 1 tag memcpy (buf + 33, replyTag, 32); buf += 65; m->len += (buf - m->GetPayload ()); m->FillI2NPMessageHeader (eI2NPDatabaseLookup); return m; }
void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block) { bool messageCreated = false; if (!m_CurrentTunnelDataMsg) { CreateCurrentTunnelDataMessage (); messageCreated = true; } // create delivery instructions uint8_t di[43]; // max delivery instruction length is 43 for tunnel size_t diLen = 1;// flag if (block.deliveryType != eDeliveryTypeLocal) // tunnel or router { if (block.deliveryType == eDeliveryTypeTunnel) { htobe32buf (di + diLen, block.tunnelID); diLen += 4; // tunnelID } memcpy (di + diLen, block.hash, 32); diLen += 32; //len } di[0] = block.deliveryType << 5; // set delivery type // create fragments std::shared_ptr<I2NPMessage> msg = block.data; auto fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length if (fullMsgLen <= m_RemainingSize) { // message fits. First and last fragment htobe16buf (di + diLen, msg->GetLength ()); diLen += 2; // size memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), msg->GetLength ()); m_CurrentTunnelDataMsg->len += diLen + msg->GetLength (); m_RemainingSize -= diLen + msg->GetLength (); if (!m_RemainingSize) CompleteCurrentTunnelDataMessage (); } else { if (!messageCreated) // check if we should complete previous message { auto numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE; // length of bytes don't fit full tunnel message // every follow-on fragment adds 7 bytes auto nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE; if (!nonFit || nonFit > m_RemainingSize) { CompleteCurrentTunnelDataMessage (); CreateCurrentTunnelDataMessage (); } } if (diLen + 6 <= m_RemainingSize) { // delivery instructions fit uint32_t msgID; memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); // in network bytes order size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size) // first fragment di[0] |= 0x08; // fragmented htobuf32 (di + diLen, msgID); diLen += 4; // Message ID htobe16buf (di + diLen, size); diLen += 2; // size memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), size); m_CurrentTunnelDataMsg->len += diLen + size; CompleteCurrentTunnelDataMessage (); // follow on fragments int fragmentNumber = 1; while (size < msg->GetLength ()) { CreateCurrentTunnelDataMessage (); uint8_t * buf = m_CurrentTunnelDataMsg->GetBuffer (); buf[0] = 0x80 | (fragmentNumber << 1); // frag bool isLastFragment = false; size_t s = msg->GetLength () - size; if (s > TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7) // 7 follow on instructions s = TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7; else // last fragment { buf[0] |= 0x01; isLastFragment = true; } htobuf32 (buf + 1, msgID); //Message ID htobe16buf (buf + 5, s); // size memcpy (buf + 7, msg->GetBuffer () + size, s); m_CurrentTunnelDataMsg->len += s+7; if (isLastFragment) { m_RemainingSize -= s+7; if (!m_RemainingSize) CompleteCurrentTunnelDataMessage (); } else CompleteCurrentTunnelDataMessage (); size += s; fragmentNumber++; } } else { // delivery instructions don't fit. Create new message CompleteCurrentTunnelDataMessage (); PutI2NPMsg (block); // don't delete msg because it's taken care inside } } }