@ -52,26 +52,25 @@ namespace tunnel
bool isFollowOnFragment = flag & 0x80 , isLastFragment = true ;
bool isFollowOnFragment = flag & 0x80 , isLastFragment = true ;
uint32_t msgID = 0 ;
uint32_t msgID = 0 ;
int fragmentNum = 0 ;
int fragmentNum = 0 ;
TunnelMessageBlockEx & m = m_CurrentMessage ;
if ( ! isFollowOnFragment )
if ( ! isFollowOnFragment )
{
{
// first fragment
// first fragment
if ( m_CurrentMsgID )
if ( m_CurrentMsgID )
AddIncompleteCurrentMessage ( ) ; // we have got a new message while previous is not complete
AddIncompleteCurrentMessage ( ) ; // we have got a new message while previous is not complete
m . deliveryType = ( TunnelDeliveryType ) ( ( flag > > 5 ) & 0x03 ) ;
m_CurrentMessage . deliveryType = ( TunnelDeliveryType ) ( ( flag > > 5 ) & 0x03 ) ;
switch ( m . deliveryType )
switch ( m_CurrentMessage . deliveryType )
{
{
case eDeliveryTypeLocal : // 0
case eDeliveryTypeLocal : // 0
break ;
break ;
case eDeliveryTypeTunnel : // 1
case eDeliveryTypeTunnel : // 1
m . tunnelID = bufbe32toh ( fragment ) ;
m_CurrentMessage . tunnelID = bufbe32toh ( fragment ) ;
fragment + = 4 ; // tunnelID
fragment + = 4 ; // tunnelID
m . hash = i2p : : data : : IdentHash ( fragment ) ;
m_CurrentMessage . hash = i2p : : data : : IdentHash ( fragment ) ;
fragment + = 32 ; // hash
fragment + = 32 ; // hash
break ;
break ;
case eDeliveryTypeRouter : // 2
case eDeliveryTypeRouter : // 2
m . hash = i2p : : data : : IdentHash ( fragment ) ;
m_CurrentMessage . hash = i2p : : data : : IdentHash ( fragment ) ;
fragment + = 32 ; // to hash
fragment + = 32 ; // to hash
break ;
break ;
default : ;
default : ;
@ -99,42 +98,51 @@ namespace tunnel
uint16_t size = bufbe16toh ( fragment ) ;
uint16_t size = bufbe16toh ( fragment ) ;
fragment + = 2 ;
fragment + = 2 ;
if ( isFollowOnFragment & & m_CurrentMsgID & & m_CurrentMsgID = = msgID & &
// handle fragment
m_CurrentMessage . nextFragmentNum = = fragmentNum )
if ( isFollowOnFragment )
{
HandleCurrenMessageFollowOnFragment ( fragment , size , isLastFragment ) ;
fragment + = size ;
continue ;
}
msg - > offset = fragment - msg - > buf ;
msg - > len = msg - > offset + size ;
if ( msg - > len > msg - > maxLen )
{
LogPrint ( eLogError , " TunnelMessage: fragment is too long " , ( int ) size ) ;
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
return ;
}
if ( fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE )
{
{
// this is not last message. we have to copy it
// existing message
m . data = NewI2NPTunnelMessage ( ) ;
if ( m_CurrentMsgID & & m_CurrentMsgID = = msgID & & m_CurrentMessage . nextFragmentNum = = fragmentNum )
m . data - > offset + = TUNNEL_GATEWAY_HEADER_SIZE ; // reserve room for TunnelGateway header
HandleCurrenMessageFollowOnFragment ( fragment , size , isLastFragment ) ; // previous
m . data - > len + = TUNNEL_GATEWAY_HEADER_SIZE ;
else
* ( m . data ) = * msg ;
{
}
HandleFollowOnFragment ( msgID , isLastFragment , fragmentNum , fragment , size ) ; // another
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
}
}
else
else
m . data = msg ;
{
// new message
if ( ! isFollowOnFragment )
msg - > offset = fragment - msg - > buf ;
{
msg - > len = msg - > offset + size ;
// check message size
if ( msg - > len > msg - > maxLen )
{
LogPrint ( eLogError , " TunnelMessage: fragment is too long " , ( int ) size ) ;
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
return ;
}
// create new or assign I2NP message
if ( fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE )
{
// this is not last message. we have to copy it
m_CurrentMessage . data = NewI2NPTunnelMessage ( ) ;
m_CurrentMessage . data - > offset + = TUNNEL_GATEWAY_HEADER_SIZE ; // reserve room for TunnelGateway header
m_CurrentMessage . data - > len + = TUNNEL_GATEWAY_HEADER_SIZE ;
* ( m_CurrentMessage . data ) = * msg ;
}
else
m_CurrentMessage . data = msg ;
if ( isLastFragment )
if ( isLastFragment )
{
{
HandleNextMessage ( m ) ;
// single message
HandleNextMessage ( m_CurrentMessage ) ;
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
}
}
else if ( msgID )
else if ( msgID )
{
{
// first fragment of a new message
m_CurrentMessage . nextFragmentNum = 1 ;
m_CurrentMessage . nextFragmentNum = 1 ;
m_CurrentMessage . receiveTime = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
m_CurrentMessage . receiveTime = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
HandleOutOfSequenceFragments ( msgID , m_CurrentMessage ) ;
HandleOutOfSequenceFragments ( msgID , m_CurrentMessage ) ;
@ -145,13 +153,7 @@ namespace tunnel
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
}
}
}
}
else
{
m . nextFragmentNum = fragmentNum ;
HandleFollowOnFragment ( msgID , isLastFragment , m ) ;
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
}
fragment + = size ;
fragment + = size ;
}
}
}
}
@ -159,15 +161,14 @@ namespace tunnel
LogPrint ( eLogError , " TunnelMessage: zero not found " ) ;
LogPrint ( eLogError , " TunnelMessage: zero not found " ) ;
}
}
void TunnelEndpoint : : HandleFollowOnFragment ( uint32_t msgID , bool isLastFragment , const TunnelMessageBlockEx & m )
void TunnelEndpoint : : HandleFollowOnFragment ( uint32_t msgID , bool isLastFragment ,
uint8_t fragmentNum , const uint8_t * fragment , size_t size )
{
{
auto fragment = m . data - > GetBuffer ( ) ;
auto size = m . data - > GetLength ( ) ;
auto it = m_IncompleteMessages . find ( msgID ) ;
auto it = m_IncompleteMessages . find ( msgID ) ;
if ( it ! = m_IncompleteMessages . end ( ) )
if ( it ! = m_IncompleteMessages . end ( ) )
{
{
auto & msg = it - > second ;
auto & msg = it - > second ;
if ( m . nextF ragmentNum = = msg . nextFragmentNum )
if ( f ragmentNum = = msg . nextFragmentNum )
{
{
if ( ConcatFollowOnFragment ( msg , fragment , size ) )
if ( ConcatFollowOnFragment ( msg , fragment , size ) )
{
{
@ -185,20 +186,20 @@ namespace tunnel
}
}
else
else
{
{
LogPrint ( eLogError , " TunnelMessage: Fragment " , m . nextF ragmentNum, " of message " , msgID , " exceeds max I2NP message size, message dropped " ) ;
LogPrint ( eLogError , " TunnelMessage: Fragment " , f ragmentNum, " of message " , msgID , " exceeds max I2NP message size, message dropped " ) ;
m_IncompleteMessages . erase ( it ) ;
m_IncompleteMessages . erase ( it ) ;
}
}
}
}
else
else
{
{
LogPrint ( eLogWarning , " TunnelMessage: Unexpected fragment " , ( int ) m . nextF ragmentNum, " instead " , ( int ) msg . nextFragmentNum , " of message " , msgID , " , saved " ) ;
LogPrint ( eLogWarning , " TunnelMessage: Unexpected fragment " , ( int ) f ragmentNum, " instead " , ( int ) msg . nextFragmentNum , " of message " , msgID , " , saved " ) ;
AddOutOfSequenceFragment ( msgID , m . nextF ragmentNum, isLastFragment , m . data ) ;
AddOutOfSequenceFragment ( msgID , f ragmentNum, isLastFragment , fragment , size ) ;
}
}
}
}
else
else
{
{
LogPrint ( eLogWarning , " TunnelMessage: First fragment of message " , msgID , " not found, saved " ) ;
LogPrint ( eLogWarning , " TunnelMessage: First fragment of message " , msgID , " not found, saved " ) ;
AddOutOfSequenceFragment ( msgID , m . nextF ragmentNum, isLastFragment , m . data ) ;
AddOutOfSequenceFragment ( msgID , f ragmentNum, isLastFragment , fragment , size ) ;
}
}
}
}
@ -259,10 +260,12 @@ namespace tunnel
}
}
}
}
void TunnelEndpoint : : AddOutOfSequenceFragment ( uint32_t msgID , uint8_t fragmentNum , bool isLastFragment , std : : shared_ptr < I2NPMessage > data )
void TunnelEndpoint : : AddOutOfSequenceFragment ( uint32_t msgID , uint8_t fragmentNum ,
bool isLastFragment , const uint8_t * fragment , size_t size )
{
{
if ( ! m_OutOfSequenceFragments . insert ( { ( uint64_t ) msgID < < 32 | fragmentNum ,
std : : unique_ptr < Fragment > f ( new Fragment ( isLastFragment , i2p : : util : : GetMillisecondsSinceEpoch ( ) , size ) ) ;
{ isLastFragment , data , i2p : : util : : GetMillisecondsSinceEpoch ( ) } } ) . second )
memcpy ( f - > data . data ( ) , fragment , size ) ;
if ( ! m_OutOfSequenceFragments . emplace ( ( uint64_t ) msgID < < 32 | fragmentNum , std : : move ( f ) ) . second )
LogPrint ( eLogInfo , " TunnelMessage: duplicate out-of-sequence fragment " , fragmentNum , " of message " , msgID ) ;
LogPrint ( eLogInfo , " TunnelMessage: duplicate out-of-sequence fragment " , fragmentNum , " of message " , msgID ) ;
}
}
@ -291,7 +294,7 @@ namespace tunnel
if ( it ! = m_OutOfSequenceFragments . end ( ) )
if ( it ! = m_OutOfSequenceFragments . end ( ) )
{
{
LogPrint ( eLogDebug , " TunnelMessage: Out-of-sequence fragment " , ( int ) msg . nextFragmentNum , " of message " , msgID , " found " ) ;
LogPrint ( eLogDebug , " TunnelMessage: Out-of-sequence fragment " , ( int ) msg . nextFragmentNum , " of message " , msgID , " found " ) ;
size_t size = it - > second . data - > GetLength ( ) ;
size_t size = it - > second - > data . size ( ) ;
if ( msg . data - > len + size > msg . data - > maxLen )
if ( msg . data - > len + size > msg . data - > maxLen )
{
{
LogPrint ( eLogWarning , " TunnelMessage: Tunnel endpoint I2NP message size " , msg . data - > maxLen , " is not enough " ) ;
LogPrint ( eLogWarning , " TunnelMessage: Tunnel endpoint I2NP message size " , msg . data - > maxLen , " is not enough " ) ;
@ -299,9 +302,9 @@ namespace tunnel
* newMsg = * ( msg . data ) ;
* newMsg = * ( msg . data ) ;
msg . data = newMsg ;
msg . data = newMsg ;
}
}
if ( msg . data - > Concat ( it - > second . data - > GetBuffer ( ) , size ) < size ) // concatenate out-of-sync fragment
if ( msg . data - > Concat ( it - > second - > data . data ( ) , size ) < size ) // concatenate out-of-sync fragment
LogPrint ( eLogError , " TunnelMessage: Tunnel endpoint I2NP buffer overflow " , msg . data - > maxLen ) ;
LogPrint ( eLogError , " TunnelMessage: Tunnel endpoint I2NP buffer overflow " , msg . data - > maxLen ) ;
if ( it - > second . isLastFragment )
if ( it - > second - > isLastFragment )
// message complete
// message complete
msg . nextFragmentNum = 0 ;
msg . nextFragmentNum = 0 ;
else
else
@ -354,7 +357,7 @@ namespace tunnel
// out-of-sequence fragments
// out-of-sequence fragments
for ( auto it = m_OutOfSequenceFragments . begin ( ) ; it ! = m_OutOfSequenceFragments . end ( ) ; )
for ( auto it = m_OutOfSequenceFragments . begin ( ) ; it ! = m_OutOfSequenceFragments . end ( ) ; )
{
{
if ( ts > it - > second . receiveTime + i2p : : I2NP_MESSAGE_EXPIRATION_TIMEOUT )
if ( ts > it - > second - > receiveTime + i2p : : I2NP_MESSAGE_EXPIRATION_TIMEOUT )
it = m_OutOfSequenceFragments . erase ( it ) ;
it = m_OutOfSequenceFragments . erase ( it ) ;
else
else
+ + it ;
+ + it ;