void HandleTunnelGatewayMsg (I2NPMessage * msg) { TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload (); uint32_t tunnelID = be32toh(header->tunnelID); uint16_t len = be16toh(header->length); // we make payload as new I2NP message to send msg->offset += sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader); msg->len = msg->offset + len; LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID, ". Msg type ", (int)msg->GetHeader()->typeID); if (msg->GetHeader()->typeID == eI2NPDatabaseStore || msg->GetHeader()->typeID == eI2NPDatabaseSearchReply) { // transit DatabaseStore my contain new/updated RI // or DatabaseSearchReply with new routers auto ds = NewI2NPMessage (); *ds = *msg; i2p::data::netdb.PostI2NPMsg (ds); } i2p::tunnel::TransitTunnel * tunnel = i2p::tunnel::tunnels.GetTransitTunnel (tunnelID); if (tunnel) tunnel->SendTunnelDataMsg (msg); else { LogPrint ("Tunnel ", (unsigned int)tunnelID, " not found"); i2p::DeleteI2NPMessage (msg); } }
void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage () { m_CurrentTunnelDataMsg = NewI2NPMessage (); // we reserve space for padding m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + sizeof (I2NPHeader); m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset; m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE; }
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 * 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 * 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; }
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID) { auto msg = NewI2NPMessage (len); if (msg->Concat (buf, len) < len) LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length ", msg->maxLen); msg->FillI2NPMessageHeader (msgType, replyMsgID); 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; }
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> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) { auto msg = NewI2NPMessage (); if (msg->offset + len < msg->maxLen) { memcpy (msg->GetBuffer (), buf, len); msg->len = msg->offset + len; msg->from = from; } else LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length"); 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; }
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> 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; }
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len) { return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage (); }
I2NPMessage * NewI2NPMessage (size_t len) { return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage (); }
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; }
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) { auto m = NewI2NPMessage (); m->Align (12); // in order to get buf aligned to 16 (12 + 4) size_t len = 0; uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length // find non-expired tag bool tagFound = false; SessionTag tag; if (m_NumTags > 0) { uint32_t ts = i2p::util::GetSecondsSinceEpoch (); while (!m_SessionTags.empty ()) { if (ts < m_SessionTags.front ().creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) { tag = m_SessionTags.front (); m_SessionTags.pop_front (); // use same tag only once tagFound = true; break; } else m_SessionTags.pop_front (); // remove expired tag } } // create message if (!tagFound) // new session { LogPrint (eLogInfo, "Garlic: No tags available, will use ElGamal"); if (!m_Destination) { LogPrint (eLogError, "Garlic: Can't use ElGamal for unknown destination"); return nullptr; } // create ElGamal block ElGamalBlock elGamal; memcpy (elGamal.sessionKey, m_SessionKey, 32); RAND_bytes (elGamal.preIV, 32); // Pre-IV uint8_t iv[32]; // IV is first 16 bytes SHA256(elGamal.preIV, 32, iv); BN_CTX * ctx = BN_CTX_new (); m_Destination->Encrypt ((uint8_t *)&elGamal, buf, ctx); BN_CTX_free (ctx); m_Encryption.SetIV (iv); buf += 514; len += 514; } else // existing session { // session tag memcpy (buf, tag, 32); uint8_t iv[32]; // IV is first 16 bytes SHA256(tag, 32, iv); m_Encryption.SetIV (iv); buf += 32; len += 32; } // AES block len += CreateAESBlock (buf, msg); htobe32buf (m->GetPayload (), len); m->len += len + 4; m->FillI2NPMessageHeader (eI2NPGarlic); return m; }
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; }