Browse Source

transit tunnel

pull/6/head
orignal 11 years ago
parent
commit
84301daeb8
  1. 85
      TransitTunnel.cpp
  2. 53
      TransitTunnel.h
  3. 26
      TunnelBase.h
  4. 159
      TunnelEndpoint.cpp
  5. 31
      TunnelEndpoint.h
  6. 166
      TunnelGateway.cpp
  7. 41
      TunnelGateway.h

85
TransitTunnel.cpp

@ -0,0 +1,85 @@ @@ -0,0 +1,85 @@
#include <string.h>
#include "Log.h"
#include "RouterContext.h"
#include "I2NPProtocol.h"
#include "Tunnel.h"
#include "Transports.h"
#include "TransitTunnel.h"
namespace i2p
{
namespace tunnel
{
TransitTunnel::TransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey,
bool isGateway, bool isEndpoint)
{
memcpy (m_LayerKey, layerKey, 32);
memcpy (m_IVKey, ivKey, 32);
memcpy (m_NextIdent, nextIdent, 32);
m_IsGateway = isGateway;
m_IsEndpoint = isEndpoint;
m_TunnelID = receiveTunnelID;
m_NextTunnelID = nextTunnelID;
if (m_IsEndpoint)
LogPrint ("TransitTunnel endpoint: ", m_TunnelID, " created");
else if (m_IsGateway)
LogPrint ("TransitTunnel gateway: ", m_TunnelID, " created");
else
LogPrint ("TransitTunnel: ",m_TunnelID,"->", m_NextTunnelID, " created");
}
void TransitTunnel::Encrypt (uint8_t * payload)
{
m_ECBEncryption.SetKey (m_IVKey, 32);
m_ECBEncryption.ProcessData(payload, payload, 16); // iv
m_CBCEncryption.SetKeyWithIV (m_LayerKey, 32, payload);
m_CBCEncryption.ProcessData(payload + 16, payload + 16, 1008); // payload
m_ECBEncryption.SetKey (m_IVKey, 32);
m_ECBEncryption.ProcessData(payload, payload, 16); // double iv encryption
}
void TransitTunnel::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
{
Encrypt (tunnelMsg->GetPayload () + 4);
if (m_IsEndpoint)
{
LogPrint ("TransitTunnel endpoint for ", m_TunnelID);
m_Endpoint.HandleDecryptedTunnelDataMsg (tunnelMsg);
}
else
{
LogPrint ("TransitTunnel: ",m_TunnelID,"->", m_NextTunnelID);
*(uint32_t *)(tunnelMsg->GetPayload ()) = htobe32 (m_NextTunnelID);
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
i2p::transports.SendMessage (m_NextIdent, tunnelMsg);
}
}
void TransitTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
{
if (m_IsGateway)
{
m_Gateway.PutI2NPMsg (gwHash, gwTunnel, msg);
auto tunnelMsgs = m_Gateway.GetTunnelDataMsgs (m_NextTunnelID);
for (auto tunnelMsg : tunnelMsgs)
{
Encrypt (tunnelMsg->GetPayload () + 4);
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
i2p::transports.SendMessage (m_NextIdent, tunnelMsg);
}
}
else
{
LogPrint ("We are not a gateway for transit tunnel ", m_TunnelID);
i2p::DeleteI2NPMessage (msg);
}
}
}
}

53
TransitTunnel.h

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
#ifndef TRANSIT_TUNNEL_H__
#define TRANSIT_TUNNEL_H__
#include <inttypes.h>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
#include "I2NPProtocol.h"
#include "TunnelEndpoint.h"
#include "TunnelGateway.h"
namespace i2p
{
namespace tunnel
{
class TransitTunnel
{
public:
TransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey,
bool isGateway, bool isEndpoint);
void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
uint32_t GetTunnelID () const { return m_TunnelID; };
bool IsGateway () const { return m_IsGateway; };
bool IsEndpoint () const { return m_IsEndpoint; };
bool IsParticipant () const { return !IsGateway () && !IsEndpoint (); };
private:
void Encrypt (uint8_t * payload);
private:
uint32_t m_TunnelID, m_NextTunnelID;
uint8_t m_NextIdent[32];
uint8_t m_LayerKey[32];
uint8_t m_IVKey[32];
bool m_IsGateway, m_IsEndpoint;
TunnelEndpoint m_Endpoint;
TunnelGatewayBuffer m_Gateway;
CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_ECBEncryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_CBCEncryption;
};
}
}
#endif

26
TunnelBase.h

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
#ifndef TUNNEL_BASE_H__
#define TUNNEL_BASE_H__
#include <inttypes.h>
namespace i2p
{
namespace tunnel
{
enum TunnelDeliveryType
{
eDeliveryTypeLocal = 0,
eDeliveryTypeTunnel = 1,
eDeliveryTypeRouter = 2
};
struct TunnelMessageBlock
{
TunnelDeliveryType deliveryType;
uint32_t tunnelID;
uint8_t hash[32];
I2NPMessage * data;
};
}
}
#endif

159
TunnelEndpoint.cpp

@ -0,0 +1,159 @@ @@ -0,0 +1,159 @@
#include <string.h>
#include "Log.h"
#include "I2NPProtocol.h"
#include "Transports.h"
#include "TunnelEndpoint.h"
namespace i2p
{
namespace tunnel
{
void TunnelEndpoint::HandleDecryptedTunnelDataMsg (I2NPMessage * msg)
{
uint8_t * decrypted = msg->GetPayload () + 20; // 4 + 16
uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, 1004); // witout checksum
if (zero)
{
LogPrint ("TunnelMessage: zero found at ", (int)(zero-decrypted));
uint8_t * fragment = zero + 1;
while (fragment < decrypted + 1008)
{
uint8_t flag = fragment[0];
fragment++;
bool isFollowOnFragment = flag & 0x80, isLastFragment = true;
uint32_t msgID = 0;
TunnelMessageBlock m;
if (!isFollowOnFragment)
{
// first fragment
m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
switch (m.deliveryType)
{
case eDeliveryTypeLocal: // 0
LogPrint ("Delivery type local");
break;
case eDeliveryTypeTunnel: // 1
LogPrint ("Delivery type tunnel");
m.tunnelID = be32toh (*(uint32_t *)fragment);
fragment += 4; // tunnelID
memcpy (m.hash, fragment, 32);
fragment += 32; // hash
break;
case eDeliveryTypeRouter: // 2
LogPrint ("Delivery type router");
memcpy (m.hash, fragment, 32);
fragment += 32; // to hash
break;
default:
;
}
bool isFragmented = flag & 0x08;
if (isFragmented)
{
// Message ID
msgID = be32toh (*(uint32_t *)fragment);
fragment += 4;
LogPrint ("Fragmented message ", msgID);
isLastFragment = false;
}
}
else
{
// follow on
msgID = be32toh (*(uint32_t *)fragment); // MessageID
fragment += 4;
int fragmentNum = (flag >> 1) & 0x3F; // 6 bits
isLastFragment = flag & 0x01;
LogPrint ("Follow on fragment ", fragmentNum, " of message ", msgID, isLastFragment ? " last" : " non-last");
}
uint16_t size = be16toh (*(uint16_t *)fragment);
fragment += 2;
LogPrint ("Fragment size=", (int)size);
msg->offset = fragment - msg->buf;
msg->len = msg->offset + size;
bool isLastMessage = false;
if (fragment + size < decrypted + 1008)
{
// this is not last message. we have to copy it
m.data = NewI2NPMessage ();
m.data->offset += sizeof (TunnelGatewayHeader); // reserve room for TunnelGateway header
m.data->len += sizeof (TunnelGatewayHeader);
*(m.data) = *msg;
}
else
{
m.data = msg;
isLastMessage = true;
}
if (!isFollowOnFragment && isLastFragment)
HandleNextMessage (m);
else
{
if (msgID) // msgID is presented, assume message is fragmented
{
if (!isFollowOnFragment) // create new incomlete message
m_IncompleteMessages[msgID] = m;
else
{
auto it = m_IncompleteMessages.find (msgID);
if (it != m_IncompleteMessages.end())
{
I2NPMessage * incompleteMessage = it->second.data;
memcpy (incompleteMessage->buf + incompleteMessage->len, fragment, size); // concatenate fragment
incompleteMessage->len += size;
// TODO: check fragmentNum sequence
if (isLastFragment)
{
// message complete
HandleNextMessage (it->second);
m_IncompleteMessages.erase (it);
}
}
else
LogPrint ("First fragment of message ", msgID, " not found. Discarded");
if (isLastMessage)
// last message is follow-on fragment
// not passed to anywhere because first fragment
i2p::DeleteI2NPMessage (msg);
}
}
else
LogPrint ("Message is fragmented, but msgID is not presented");
}
fragment += size;
}
}
else
{
LogPrint ("TunnelMessage: zero not found");
i2p::DeleteI2NPMessage (msg);
}
}
void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
{
switch (msg.deliveryType)
{
case eDeliveryTypeLocal:
i2p::HandleI2NPMessage (msg.data);
break;
case eDeliveryTypeTunnel:
i2p::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
break;
case eDeliveryTypeRouter:
i2p::transports.SendMessage (msg.hash, msg.data);
break;
default:
;
};
}
}
}

31
TunnelEndpoint.h

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
#ifndef TUNNEL_ENDPOINT_H__
#define TUNNEL_ENDPOINT_H__
#include <inttypes.h>
#include <map>
#include <string>
#include "I2NPProtocol.h"
#include "TunnelBase.h"
namespace i2p
{
namespace tunnel
{
class TunnelEndpoint
{
public:
void HandleDecryptedTunnelDataMsg (I2NPMessage * msg);
private:
void HandleNextMessage (const TunnelMessageBlock& msg);
private:
std::map<uint32_t, TunnelMessageBlock> m_IncompleteMessages;
};
}
}
#endif

166
TunnelGateway.cpp

@ -0,0 +1,166 @@ @@ -0,0 +1,166 @@
#include <string.h>
#include <endian.h>
#include <cryptopp/sha.h>
#include "RouterContext.h"
#include "TunnelGateway.h"
namespace i2p
{
namespace tunnel
{
void TunnelGatewayBuffer::PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg)
{
TunnelMessageBlockExt * block = new TunnelMessageBlockExt;
block->deliveryInstructionsLen = 1; // flag
if (gwHash)
{
block->deliveryInstructionsLen = 32; // hash
memcpy (block->hash, gwHash, 32);
if (gwTunnel)
{
block->deliveryType = eDeliveryTypeTunnel;
block->deliveryInstructionsLen += 4; // tunnelID
block->tunnelID = gwTunnel;
}
else
block->deliveryType = eDeliveryTypeRouter;
}
else
block->deliveryType = eDeliveryTypeLocal;
// we don't reserve 4 bytes for msgID because we don't if it fits
block->totalLen = block->deliveryInstructionsLen + msg->GetLength ();
block->data = msg;
m_I2NPMsgs.push_back (block);
}
std::vector<I2NPMessage *> TunnelGatewayBuffer::GetTunnelDataMsgs (uint32_t tunnelID)
{
std::vector<I2NPMessage *> res;
int cnt = m_I2NPMsgs.size (), pos = 0, prev = 0;
m_NextOffset = 0;
if (cnt > 0)
{
size_t size = 0;
while (pos < cnt)
{
TunnelMessageBlockExt * block = m_I2NPMsgs[pos];
if (size + block->totalLen >= 1003) // 1003 = 1008 - checksum - zero
{
// we have to make sure if we can put delivery instructions + msgID of last message
if (size + block->deliveryInstructionsLen + 4 > 1003)
{
// we have to exclude last message
pos--;
}
else
size = 1003;
res.push_back (CreateNextTunnelMessage (tunnelID, prev, pos, size));
prev = pos;
}
else
size += block->totalLen;
pos++;
}
res.push_back (CreateNextTunnelMessage (tunnelID, prev, pos, size)); // last message
for (auto m: m_I2NPMsgs)
delete m;
m_I2NPMsgs.clear ();
}
return res;
}
size_t TunnelGatewayBuffer::CreateFirstFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len)
{
if (block->deliveryInstructionsLen > len) return 0; // can't put even delivery instructions
size_t ret = 1;
buf[0] = block->deliveryType << 5; // flag
if (block->deliveryType == eDeliveryTypeTunnel)
{
*(uint32_t *)(buf + ret) = htobe32 (block->tunnelID);
ret += 4;
}
if (block->deliveryType == eDeliveryTypeTunnel || block->deliveryType == eDeliveryTypeRouter)
{
memcpy (buf + ret, block->hash, 32);
ret += 32;
}
size_t size = block->data->GetLength ();
if (block->totalLen > len) // entire message doesn't fit
{
if (ret + 4 > len) return 0; // can't put delivery instructions with msgID
buf[0] |= 0x08; // set fragmented bit
m_NextMsgID = block->data->GetHeader ()->msgID;
*(uint32_t *)(buf + ret) = m_NextMsgID;
ret += 4; // msgID
m_NextSeqn = 1;
size -= (block->totalLen - len);
m_NextOffset = size;
}
*(uint16_t *)(buf + ret) = htobe16 (size); // size
ret += 2;
memcpy (buf + ret, block->data->GetBuffer (), size);
ret += size;
return ret;
}
size_t TunnelGatewayBuffer::CreateFollowOnFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len)
{
int ret = 0;
buf[0] = 0x80 | (m_NextSeqn << 1);// follow-on flag and seqn
size_t fragmentLen = len - 7; // 7 bytes of header
if (fragmentLen >= block->totalLen - m_NextOffset)
{
// fragment fits
fragmentLen = block->totalLen - m_NextOffset;
buf[0] |= 0x01; // last fragment
}
else
m_NextSeqn++;
*(uint32_t *)(buf + 1) = m_NextMsgID; // msgID
*(uint16_t *)(buf + 5) = htobe16 (fragmentLen); // size
memcpy (buf + 7, block->data->GetBuffer () + m_NextOffset, fragmentLen);
m_NextOffset += fragmentLen;
ret += fragmentLen + 7;
return ret;
}
I2NPMessage * TunnelGatewayBuffer::CreateNextTunnelMessage (uint32_t tunnelID,
int from, int to, size_t size)
{
I2NPMessage * tunnelMsg = NewI2NPMessage ();
uint8_t * buf = tunnelMsg->GetPayload ();
*(uint32_t *)(buf) = htobe32 (tunnelID);
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (buf + 4, 16); // original IV
memcpy (buf + 1028, buf + 4, 16); // copy IV for checksum
size_t zero = 1028 - size;
buf[zero] = 0; // zero
buf += zero;
for (int i = from; i <= to; i++)
{
TunnelMessageBlockExt * block = m_I2NPMsgs[i];
size_t s = CreateFirstFragment (block, buf, size);
if (s < size)
{
size -= s;
buf += s;
}
else
break;
}
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest(hash, buf+zero+1, size+16);
memcpy (buf+20, hash, 4); // checksum
if (zero > 25)
memset (buf+24, 1, zero-25); // padding
// we can't fill message header yet because encryption is required
return tunnelMsg;
}
}
}

41
TunnelGateway.h

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
#ifndef TUNNEL_GATEWAY_H__
#define TUNNEL_GATEWAY_H__
#include <inttypes.h>
#include <vector>
#include "I2NPProtocol.h"
#include "TunnelBase.h"
namespace i2p
{
namespace tunnel
{
class TunnelGatewayBuffer
{
struct TunnelMessageBlockExt: public TunnelMessageBlock
{
size_t deliveryInstructionsLen, totalLen;
};
public:
void PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg);
std::vector<I2NPMessage *> GetTunnelDataMsgs (uint32_t tunnelID);
private:
size_t CreateFirstFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len);
size_t CreateFollowOnFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len);
I2NPMessage * CreateNextTunnelMessage (uint32_t tunnelID, int from, int to, size_t size);
private:
std::vector<TunnelMessageBlockExt *> m_I2NPMsgs;
// for fragmented messages
size_t m_NextOffset, m_NextSeqn;
uint32_t m_NextMsgID;
};
}
}
#endif
Loading…
Cancel
Save