I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken) { if (!leaseSet) return nullptr; I2NPMessage * m = NewI2NPShortMessage (); uint8_t * payload = m->GetPayload (); I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)payload; memcpy (msg->key, leaseSet->GetIdentHash (), 32); msg->type = 1; // LeaseSet msg->replyToken = htobe32 (replyToken); size_t size = sizeof (I2NPDatabaseStoreMsg); if (replyToken) { auto leases = leaseSet->GetNonExpiredLeases (); if (leases.size () > 0) { *(uint32_t *)(payload + size) = htobe32 (leases[0].tunnelID); size += 4; // reply tunnelID memcpy (payload + size, leases[0].tunnelGateway, 32); size += 32; // reply tunnel gateway } else msg->replyToken = 0; } memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ()); size += leaseSet->GetBufferLen (); m->len += size; FillI2NPMessageHeader (m, eI2NPDatabaseStore); return m; }
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router) { if (!router) // we send own RouterInfo router = &context.GetRouterInfo (); I2NPMessage * m = NewI2NPShortMessage (); I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)m->GetPayload (); memcpy (msg->key, router->GetIdentHash (), 32); msg->type = 0; msg->replyToken = 0; CryptoPP::Gzip compressor; compressor.Put (router->GetBuffer (), router->GetBufferLen ()); compressor.MessageEnd(); auto size = compressor.MaxRetrievable (); uint8_t * buf = m->GetPayload () + sizeof (I2NPDatabaseStoreMsg); *(uint16_t *)buf = htobe16 (size); // size buf += 2; // TODO: check if size doesn't exceed buffer compressor.Get (buf, size); m->len += sizeof (I2NPDatabaseStoreMsg) + 2 + size; // payload size FillI2NPMessageHeader (m, eI2NPDatabaseStore); return m; }
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from) { I2NPMessage * msg = NewI2NPMessage (); memcpy (msg->GetBuffer (), buf, len); msg->len = msg->offset + len; msg->from = from; return msg; }
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID) { I2NPMessage * msg = NewI2NPMessage (len); memcpy (msg->GetPayload (), buf, len); msg->len += len; FillI2NPMessageHeader (msg, msgType, replyMsgID); return msg; }
I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf) { I2NPMessage * msg = NewI2NPMessage (); memcpy (msg->GetPayload (), buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE); msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; FillI2NPMessageHeader (msg, eI2NPTunnelData); return msg; }
I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload) { I2NPMessage * msg = NewI2NPMessage (); memcpy (msg->GetPayload () + 4, payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4); *(uint32_t *)(msg->GetPayload ()) = htobe32 (tunnelID); msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; FillI2NPMessageHeader (msg, eI2NPTunnelData); return msg; }
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len) { I2NPMessage * msg = NewI2NPMessage (len); TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload (); header->tunnelID = htobe32 (tunnelID); header->length = htobe16 (len); memcpy (msg->GetPayload () + sizeof (TunnelGatewayHeader), buf, len); msg->len += sizeof (TunnelGatewayHeader) + len; FillI2NPMessageHeader (msg, eI2NPTunnelGateway); return msg; }
I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len) { I2NPMessage * msg = NewI2NPMessage (); CryptoPP::Gzip compressor; // default level compressor.Put (payload, len); compressor.MessageEnd(); int size = compressor.MaxRetrievable (); uint8_t * buf = msg->GetPayload (); *(uint32_t *)buf = htobe32 (size); // length buf += 4; compressor.Get (buf, size); memset (buf + 4, 0, 4); // source and destination are zeroes buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol msg->len += size + 4; FillI2NPMessageHeader (msg, eI2NPData); return msg; }
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID) { I2NPMessage * msg = NewI2NPMessage (len); size_t gatewayMsgOffset = sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader); msg->offset += gatewayMsgOffset; msg->len += gatewayMsgOffset; memcpy (msg->GetPayload (), buf, len); msg->len += len; FillI2NPMessageHeader (msg, msgType, replyMsgID); // create content message len = msg->GetLength (); msg->offset -= gatewayMsgOffset; TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload (); header->tunnelID = htobe32 (tunnelID); header->length = htobe16 (len); FillI2NPMessageHeader (msg, eI2NPTunnelGateway); // gateway message return msg; }
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; }
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, const i2p::data::RouterInfo * floodfill) { I2NPMessage * m = NewI2NPShortMessage (); uint8_t * buf = m->GetPayload (); size_t len = 0; memcpy (buf, ident, 32); len += 32; buf[len] = floodfill ? 1 : 0; // 1 router for now len++; if (floodfill) { memcpy (buf + len, floodfill->GetIdentHash (), 32); len += 32; } memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32); len += 32; m->len += len; FillI2NPMessageHeader (m, eI2NPDatabaseSearchReply); return m; }
void NetDb::Run () { uint32_t lastTs = 0; m_IsRunning = true; while (m_IsRunning) { try { I2NPMessage * msg = m_Queue.GetNextWithTimeout (10000); // 10 sec if (msg) { while (msg) { if (msg->GetHeader ()->typeID == eI2NPDatabaseStore) { HandleDatabaseStoreMsg (msg->GetPayload (), msg->GetLength ()); // TODO i2p::DeleteI2NPMessage (msg); } else if (msg->GetHeader ()->typeID == eI2NPDatabaseSearchReply) HandleDatabaseSearchReplyMsg (msg); else // WTF? { LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID); i2p::HandleI2NPMessage (msg); } msg = m_Queue.Get (); } } else // if no new DatabaseStore coming, explore it Explore (); uint32_t ts = i2p::util::GetSecondsSinceEpoch (); if (ts - lastTs >= 60) // save routers every minute { if (lastTs) SaveUpdated ("netDb"); lastTs = ts; } } catch (std::exception& ex) { LogPrint ("NetDb: ", ex.what ()); } } }
I2NPMessage * TunnelGatewayBuffer::CreateNextTunnelMessage (int& ind) { int cnt = m_I2NPMsgs.size (); if (ind > cnt - 1) return nullptr; // no more messages // calculate payload size size_t size = 0; int i = ind; if (m_NextOffset) { size = m_I2NPMsgs[i]->data->GetLength () - m_NextOffset + 7; // including follow-on header i++; } while (i < cnt) { auto msg = m_I2NPMsgs[i]; size += msg->totalLen; if (size >= TUNNEL_DATA_MAX_PAYLOAD_SIZE) { size = TUNNEL_DATA_MAX_PAYLOAD_SIZE; break; } if (msg->isFragmented) break; i++; } I2NPMessage * tunnelMsg = NewI2NPMessage (); uint8_t * buf = tunnelMsg->GetPayload (); *(uint32_t *)(buf) = htobe32 (m_TunnelID); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); rnd.GenerateBlock (buf + 4, 16); // original IV memcpy (buf + TUNNEL_DATA_MSG_SIZE, buf + 4, 16); // copy IV for checksum size_t zero = TUNNEL_DATA_MSG_SIZE - size -1; buf[zero] = 0; // zero size_t s = 0; while (ind < cnt) { auto msg = m_I2NPMsgs[ind]; if (m_NextOffset) { s += CreateFollowOnFragment (msg, buf + zero + 1 + s, size - s); m_NextOffset = 0; // TODO: } else { s += CreateFirstFragment (msg, buf + zero + 1 + s, size - s); if (msg->isFragmented) break; // payload is full, but we stay at the same message } ind++; if (s >= size) break; // payload is full but we moved to next message } if (s != size) { LogPrint ("TunnelData payload size mismatch ", s, "!=", size); return nullptr; } uint8_t hash[32]; CryptoPP::SHA256().CalculateDigest(hash, buf+zero+1, size+16); memcpy (buf+20, hash, 4); // checksum if (zero > 24) memset (buf+24, 1, zero-24); // padding TODO: fill with random data tunnelMsg->len += TUNNEL_DATA_MSG_SIZE; // we can't fill message header yet because encryption is required return tunnelMsg; }
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers, bool encryption, i2p::tunnel::TunnelPool * pool) { I2NPMessage * m = NewI2NPMessage (); uint8_t * buf = m->GetPayload (); memcpy (buf, key, 32); // key buf += 32; memcpy (buf, from, 32); // from buf += 32; if (replyTunnelID) { *buf = encryption ? 0x03: 0x01; // set delivery flag *(uint32_t *)(buf+1) = htobe32 (replyTunnelID); buf += 5; } else { encryption = false; // encryption can we set for tunnels only *buf = 0; // flag buf++; } if (exploratory) { *(uint16_t *)buf = htobe16 (1); // one exlude record buf += 2; // reply with non-floodfill routers only memset (buf, 0, 32); buf += 32; } else { if (excludedPeers) { int cnt = excludedPeers->size (); *(uint16_t *)buf = htobe16 (cnt); buf += 2; for (auto& it: *excludedPeers) { memcpy (buf, it, 32); buf += 32; } } else { // nothing to exclude *(uint16_t *)buf = htobe16 (0); buf += 2; } } if (encryption) { // session key and tag for reply auto& rnd = i2p::context.GetRandomNumberGenerator (); rnd.GenerateBlock (buf, 32); // key buf[32] = 1; // 1 tag rnd.GenerateBlock (buf + 33, 32); // tag if (pool) pool->GetLocalDestination ().SubmitSessionKey (buf, buf + 33); // introduce new key-tag to garlic engine else LogPrint ("Destination for encrypteed reply not specified"); buf += 65; } m->len += (buf - m->GetPayload ()); FillI2NPMessageHeader (m, eI2NPDatabaseLookup); return m; }
void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block) { if (!m_CurrentTunnelDataMsg) CreateCurrentTunnelDataMessage (); // 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) { *(uint32_t *)(di + diLen) = htobe32 (block.tunnelID); diLen += 4; // tunnelID } memcpy (di + diLen, block.hash, 32); diLen += 32; //len } di[0] = block.deliveryType << 5; // set delivery type // create fragments I2NPMessage * msg = block.data; if (diLen + msg->GetLength () + 2<= m_RemainingSize) { // message fits. First and last fragment *(uint16_t *)(di + diLen) = htobe16 (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 (); DeleteI2NPMessage (msg); } else { if (diLen + 6 <= m_RemainingSize) { // delivery instructions fit uint32_t msgID = msg->GetHeader ()->msgID; size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size) // first fragment di[0] |= 0x08; // fragmented *(uint32_t *)(di + diLen) = htobe32 (msgID); diLen += 4; // Message ID *(uint16_t *)(di + diLen) = htobe16 (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; } *(uint32_t *)(buf + 1) = htobe32 (msgID); //Message ID *(uint16_t *)(buf + 5) = htobe16 (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++; } DeleteI2NPMessage (msg); } else { // delivery instructions don't fit. Create new message CompleteCurrentTunnelDataMessage (); PutI2NPMsg (block); // don't delete msg because it's taken care inside } } }