2020-05-22 16:18:41 +03:00
/*
* Copyright ( c ) 2013 - 2020 , The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
2014-01-09 23:56:07 +04:00
# include "I2PEndian.h"
2013-11-11 03:19:49 +04:00
# include <string.h>
2016-05-11 22:12:38 +03:00
# include "Crypto.h"
2013-11-11 03:19:49 +04:00
# include "Log.h"
2017-04-22 03:04:16 +03:00
# include "NetDb.hpp"
2013-11-11 03:19:49 +04:00
# include "I2NPProtocol.h"
# include "Transports.h"
2014-07-10 20:44:49 +04:00
# include "RouterContext.h"
2016-11-09 22:51:55 +03:00
# include "Timestamp.h"
2013-11-11 03:19:49 +04:00
# include "TunnelEndpoint.h"
namespace i2p
{
namespace tunnel
{
2014-07-05 16:33:08 +04:00
TunnelEndpoint : : ~ TunnelEndpoint ( )
{
2018-01-06 06:48:51 +03:00
}
2015-06-19 21:38:31 +03:00
void TunnelEndpoint : : HandleDecryptedTunnelDataMsg ( std : : shared_ptr < I2NPMessage > msg )
2013-11-11 03:19:49 +04:00
{
2013-12-10 17:10:49 +04:00
m_NumReceivedBytes + = TUNNEL_DATA_MSG_SIZE ;
2018-01-06 06:48:51 +03:00
2013-11-11 03:19:49 +04:00
uint8_t * decrypted = msg - > GetPayload ( ) + 20 ; // 4 + 16
2013-12-10 17:10:49 +04:00
uint8_t * zero = ( uint8_t * ) memchr ( decrypted + 4 , 0 , TUNNEL_DATA_ENCRYPTED_SIZE - 4 ) ; // witout 4-byte checksum
2013-11-11 03:19:49 +04:00
if ( zero )
2018-01-06 06:48:51 +03:00
{
2013-11-11 03:19:49 +04:00
uint8_t * fragment = zero + 1 ;
2014-06-28 04:11:21 +04:00
// verify checksum
memcpy ( msg - > GetPayload ( ) + TUNNEL_DATA_MSG_SIZE , msg - > GetPayload ( ) + 4 , 16 ) ; // copy iv to the end
uint8_t hash [ 32 ] ;
2015-11-03 17:15:49 +03:00
SHA256 ( fragment , TUNNEL_DATA_MSG_SIZE - ( fragment - msg - > GetPayload ( ) ) + 16 , hash ) ; // payload + iv
2014-06-28 04:11:21 +04:00
if ( memcmp ( hash , decrypted , 4 ) )
{
2015-02-05 06:05:09 +03:00
LogPrint ( eLogError , " TunnelMessage: checksum verification failed " ) ;
2014-06-28 04:11:21 +04:00
return ;
2018-01-06 06:48:51 +03:00
}
2014-06-28 04:11:21 +04:00
// process fragments
2013-12-10 17:10:49 +04:00
while ( fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE )
2013-11-11 03:19:49 +04:00
{
uint8_t flag = fragment [ 0 ] ;
fragment + + ;
2018-01-06 06:48:51 +03:00
bool isFollowOnFragment = flag & 0x80 , isLastFragment = true ;
2013-11-11 03:19:49 +04:00
uint32_t msgID = 0 ;
2014-06-11 18:56:20 +04:00
int fragmentNum = 0 ;
2013-11-11 03:19:49 +04:00
if ( ! isFollowOnFragment )
2018-01-06 06:48:51 +03:00
{
2013-11-11 03:19:49 +04:00
// first fragment
2021-06-26 14:18:42 +03:00
if ( m_CurrentMsgID )
AddIncompleteCurrentMessage ( ) ; // we have got a new message while previous is not complete
2021-06-27 00:40:25 +03:00
m_CurrentMessage . deliveryType = ( TunnelDeliveryType ) ( ( flag > > 5 ) & 0x03 ) ;
switch ( m_CurrentMessage . deliveryType )
2013-11-11 03:19:49 +04:00
{
case eDeliveryTypeLocal : // 0
break ;
2018-01-06 07:01:44 +03:00
case eDeliveryTypeTunnel : // 1
2021-06-27 00:40:25 +03:00
m_CurrentMessage . tunnelID = bufbe32toh ( fragment ) ;
2013-11-11 03:19:49 +04:00
fragment + = 4 ; // tunnelID
2021-06-27 00:40:25 +03:00
m_CurrentMessage . hash = i2p : : data : : IdentHash ( fragment ) ;
2013-11-11 03:19:49 +04:00
fragment + = 32 ; // hash
break ;
case eDeliveryTypeRouter : // 2
2021-06-27 00:40:25 +03:00
m_CurrentMessage . hash = i2p : : data : : IdentHash ( fragment ) ;
2013-11-11 03:19:49 +04:00
fragment + = 32 ; // to hash
break ;
2020-03-01 13:25:50 +03:00
default : ;
2018-01-06 06:48:51 +03:00
}
2013-11-11 03:19:49 +04:00
bool isFragmented = flag & 0x08 ;
if ( isFragmented )
{
// Message ID
2018-01-06 06:48:51 +03:00
msgID = bufbe32toh ( fragment ) ;
2013-11-11 03:19:49 +04:00
fragment + = 4 ;
2021-06-26 14:18:42 +03:00
m_CurrentMsgID = msgID ;
2013-11-11 03:19:49 +04:00
isLastFragment = false ;
2018-01-06 06:48:51 +03:00
}
2013-11-11 03:19:49 +04:00
}
else
{
// follow on
2018-01-06 06:48:51 +03:00
msgID = bufbe32toh ( fragment ) ; // MessageID
fragment + = 4 ;
2014-06-11 18:56:20 +04:00
fragmentNum = ( flag > > 1 ) & 0x3F ; // 6 bits
2013-11-11 03:19:49 +04:00
isLastFragment = flag & 0x01 ;
2018-01-06 06:48:51 +03:00
}
2014-12-30 01:04:02 +03:00
uint16_t size = bufbe16toh ( fragment ) ;
2013-11-11 03:19:49 +04:00
fragment + = 2 ;
2021-06-27 00:40:25 +03:00
// handle fragment
if ( isFollowOnFragment )
2013-11-11 03:19:49 +04:00
{
2021-06-27 00:40:25 +03:00
// existing message
if ( m_CurrentMsgID & & m_CurrentMsgID = = msgID & & m_CurrentMessage . nextFragmentNum = = fragmentNum )
HandleCurrenMessageFollowOnFragment ( fragment , size , isLastFragment ) ; // previous
else
{
HandleFollowOnFragment ( msgID , isLastFragment , fragmentNum , fragment , size ) ; // another
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
}
}
2013-11-11 03:19:49 +04:00
else
2021-06-27 00:40:25 +03:00
{
// new message
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
2021-06-27 22:49:57 +03:00
m_CurrentMessage . data = NewI2NPTunnelMessage ( true ) ;
2021-06-27 00:40:25 +03:00
* ( m_CurrentMessage . data ) = * msg ;
}
else
m_CurrentMessage . data = msg ;
2021-06-26 14:18:42 +03:00
if ( isLastFragment )
{
2021-06-27 00:40:25 +03:00
// single message
HandleNextMessage ( m_CurrentMessage ) ;
2021-06-26 14:18:42 +03:00
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
}
else if ( msgID )
2013-11-11 03:19:49 +04:00
{
2021-06-27 00:40:25 +03:00
// first fragment of a new message
2021-06-26 14:18:42 +03:00
m_CurrentMessage . nextFragmentNum = 1 ;
m_CurrentMessage . receiveTime = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
HandleOutOfSequenceFragments ( msgID , m_CurrentMessage ) ;
}
2018-01-06 06:48:51 +03:00
else
2021-06-26 14:18:42 +03:00
{
2016-01-20 03:00:00 +03:00
LogPrint ( eLogError , " TunnelMessage: Message is fragmented, but msgID is not presented " ) ;
2021-06-26 14:18:42 +03:00
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
}
}
2021-06-27 00:40:25 +03:00
2013-11-11 03:19:49 +04:00
fragment + = size ;
2018-01-06 06:48:51 +03:00
}
}
2013-11-11 03:19:49 +04:00
else
2015-02-05 06:05:09 +03:00
LogPrint ( eLogError , " TunnelMessage: zero not found " ) ;
2018-01-06 06:48:51 +03:00
}
2013-11-11 03:19:49 +04:00
2021-06-27 00:40:25 +03:00
void TunnelEndpoint : : HandleFollowOnFragment ( uint32_t msgID , bool isLastFragment ,
uint8_t fragmentNum , const uint8_t * fragment , size_t size )
2014-07-05 04:54:03 +04:00
{
auto it = m_IncompleteMessages . find ( msgID ) ;
if ( it ! = m_IncompleteMessages . end ( ) )
{
2014-09-11 05:31:32 +04:00
auto & msg = it - > second ;
2021-06-27 00:40:25 +03:00
if ( fragmentNum = = msg . nextFragmentNum )
2014-07-05 04:54:03 +04:00
{
2021-06-26 14:18:42 +03:00
if ( ConcatFollowOnFragment ( msg , fragment , size ) )
2018-01-06 06:48:51 +03:00
{
2014-07-05 04:54:03 +04:00
if ( isLastFragment )
{
// message complete
2018-01-06 06:48:51 +03:00
HandleNextMessage ( msg ) ;
m_IncompleteMessages . erase ( it ) ;
}
2014-07-05 04:54:03 +04:00
else
2018-01-06 06:48:51 +03:00
{
2014-09-11 05:31:32 +04:00
msg . nextFragmentNum + + ;
2016-11-08 23:37:27 +03:00
HandleOutOfSequenceFragments ( msgID , msg ) ;
2018-01-06 06:48:51 +03:00
}
2014-07-05 04:54:03 +04:00
}
else
{
2021-06-27 00:40:25 +03:00
LogPrint ( eLogError , " TunnelMessage: Fragment " , fragmentNum , " of message " , msgID , " exceeds max I2NP message size, message dropped " ) ;
2014-07-05 04:54:03 +04:00
m_IncompleteMessages . erase ( it ) ;
}
}
else
2018-01-06 06:48:51 +03:00
{
2021-06-27 00:40:25 +03:00
LogPrint ( eLogWarning , " TunnelMessage: Unexpected fragment " , ( int ) fragmentNum , " instead " , ( int ) msg . nextFragmentNum , " of message " , msgID , " , saved " ) ;
AddOutOfSequenceFragment ( msgID , fragmentNum , isLastFragment , fragment , size ) ;
2014-07-05 04:54:03 +04:00
}
}
else
2018-01-06 06:48:51 +03:00
{
2021-06-27 04:44:51 +03:00
LogPrint ( eLogDebug , " TunnelMessage: First fragment of message " , msgID , " not found, saved " ) ;
2021-06-27 00:40:25 +03:00
AddOutOfSequenceFragment ( msgID , fragmentNum , isLastFragment , fragment , size ) ;
2018-01-06 06:48:51 +03:00
}
}
2014-09-11 05:31:32 +04:00
2021-06-26 14:18:42 +03:00
bool TunnelEndpoint : : ConcatFollowOnFragment ( TunnelMessageBlockEx & msg , const uint8_t * fragment , size_t size ) const
{
if ( msg . data - > len + size < I2NP_MAX_MESSAGE_SIZE ) // check if message is not too long
{
if ( msg . data - > len + size > msg . data - > maxLen )
{
// LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ( ) ;
* newMsg = * ( msg . data ) ;
msg . data = newMsg ;
}
if ( msg . data - > Concat ( fragment , size ) < size ) // concatenate fragment
{
LogPrint ( eLogError , " TunnelMessage: I2NP buffer overflow " , msg . data - > maxLen ) ;
return false ;
}
}
else
return false ;
return true ;
}
void TunnelEndpoint : : HandleCurrenMessageFollowOnFragment ( const uint8_t * fragment , size_t size , bool isLastFragment )
{
if ( ConcatFollowOnFragment ( m_CurrentMessage , fragment , size ) )
{
if ( isLastFragment )
{
// message complete
HandleNextMessage ( m_CurrentMessage ) ;
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
}
else
{
m_CurrentMessage . nextFragmentNum + + ;
HandleOutOfSequenceFragments ( m_CurrentMsgID , m_CurrentMessage ) ;
}
}
else
{
LogPrint ( eLogError , " TunnelMessage: Fragment " , m_CurrentMessage . nextFragmentNum , " of message " , m_CurrentMsgID , " exceeds max I2NP message size, message dropped " ) ;
m_CurrentMsgID = 0 ; m_CurrentMessage . data = nullptr ;
}
}
void TunnelEndpoint : : AddIncompleteCurrentMessage ( )
{
if ( m_CurrentMsgID )
{
auto ret = m_IncompleteMessages . emplace ( m_CurrentMsgID , m_CurrentMessage ) ;
if ( ! ret . second )
LogPrint ( eLogError , " TunnelMessage: Incomplete message " , m_CurrentMsgID , " already exists " ) ;
m_CurrentMessage . data = nullptr ;
m_CurrentMsgID = 0 ;
}
}
2021-06-27 00:40:25 +03:00
void TunnelEndpoint : : AddOutOfSequenceFragment ( uint32_t msgID , uint8_t fragmentNum ,
bool isLastFragment , const uint8_t * fragment , size_t size )
2014-09-11 05:31:32 +04:00
{
2021-06-27 00:40:25 +03:00
std : : unique_ptr < Fragment > f ( new Fragment ( isLastFragment , i2p : : util : : GetMillisecondsSinceEpoch ( ) , size ) ) ;
memcpy ( f - > data . data ( ) , fragment , size ) ;
if ( ! m_OutOfSequenceFragments . emplace ( ( uint64_t ) msgID < < 32 | fragmentNum , std : : move ( f ) ) . second )
2016-11-08 23:37:27 +03:00
LogPrint ( eLogInfo , " TunnelMessage: duplicate out-of-sequence fragment " , fragmentNum , " of message " , msgID ) ;
2018-01-06 06:48:51 +03:00
}
2014-07-05 04:54:03 +04:00
2016-11-08 23:37:27 +03:00
void TunnelEndpoint : : HandleOutOfSequenceFragments ( uint32_t msgID , TunnelMessageBlockEx & msg )
2014-09-11 05:31:32 +04:00
{
2018-01-06 06:48:51 +03:00
while ( ConcatNextOutOfSequenceFragment ( msgID , msg ) )
2014-09-11 05:31:32 +04:00
{
2016-11-08 23:37:27 +03:00
if ( ! msg . nextFragmentNum ) // message complete
{
2018-01-06 06:48:51 +03:00
HandleNextMessage ( msg ) ;
2021-06-26 14:18:42 +03:00
if ( & msg = = & m_CurrentMessage )
{
m_CurrentMsgID = 0 ;
m_CurrentMessage . data = nullptr ;
}
else
m_IncompleteMessages . erase ( msgID ) ;
2021-06-27 04:44:51 +03:00
LogPrint ( eLogDebug , " TunnelMessage: All fragments of message " , msgID , " found " ) ;
2016-11-08 23:37:27 +03:00
break ;
}
}
}
bool TunnelEndpoint : : ConcatNextOutOfSequenceFragment ( uint32_t msgID , TunnelMessageBlockEx & msg )
{
2021-06-26 14:18:42 +03:00
auto it = m_OutOfSequenceFragments . find ( ( uint64_t ) msgID < < 32 | msg . nextFragmentNum ) ;
2016-11-08 23:37:27 +03:00
if ( it ! = m_OutOfSequenceFragments . end ( ) )
2018-01-06 06:48:51 +03:00
{
2016-11-09 22:51:55 +03:00
LogPrint ( eLogDebug , " TunnelMessage: Out-of-sequence fragment " , ( int ) msg . nextFragmentNum , " of message " , msgID , " found " ) ;
2021-06-27 00:40:25 +03:00
size_t size = it - > second - > data . size ( ) ;
2016-11-09 22:51:55 +03:00
if ( msg . data - > len + size > msg . data - > maxLen )
2014-09-11 05:31:32 +04:00
{
2016-11-09 22:51:55 +03:00
LogPrint ( eLogWarning , " TunnelMessage: Tunnel endpoint I2NP message size " , msg . data - > maxLen , " is not enough " ) ;
auto newMsg = NewI2NPMessage ( ) ;
* newMsg = * ( msg . data ) ;
msg . data = newMsg ;
2016-11-08 23:37:27 +03:00
}
2021-06-27 00:40:25 +03:00
if ( msg . data - > Concat ( it - > second - > data . data ( ) , size ) < size ) // concatenate out-of-sync fragment
2016-11-09 22:51:55 +03:00
LogPrint ( eLogError , " TunnelMessage: Tunnel endpoint I2NP buffer overflow " , msg . data - > maxLen ) ;
2021-06-27 00:40:25 +03:00
if ( it - > second - > isLastFragment )
2016-11-09 22:51:55 +03:00
// message complete
msg . nextFragmentNum = 0 ;
2016-11-08 23:37:27 +03:00
else
2016-11-09 22:51:55 +03:00
msg . nextFragmentNum + + ;
m_OutOfSequenceFragments . erase ( it ) ;
2018-01-06 06:48:51 +03:00
return true ;
}
2016-11-08 23:37:27 +03:00
return false ;
2018-01-06 06:48:51 +03:00
}
2013-11-11 03:19:49 +04:00
void TunnelEndpoint : : HandleNextMessage ( const TunnelMessageBlock & msg )
{
2016-01-19 19:16:50 +03:00
if ( ! m_IsInbound & & msg . data - > IsExpired ( ) )
2016-01-19 05:13:43 +03:00
{
LogPrint ( eLogInfo , " TunnelMessage: message expired " ) ;
return ;
2018-01-06 06:48:51 +03:00
}
2016-06-28 19:20:18 +03:00
uint8_t typeID = msg . data - > GetTypeID ( ) ;
2016-01-18 03:00:00 +03:00
LogPrint ( eLogDebug , " TunnelMessage: handle fragment of " , msg . data - > GetLength ( ) , " bytes, msg type " , ( int ) typeID ) ;
2018-01-06 06:48:51 +03:00
// catch RI or reply with new list of routers
2016-02-01 02:27:47 +03:00
if ( ( IsRouterInfoMsg ( msg . data ) | | typeID = = eI2NPDatabaseSearchReply ) & &
! m_IsInbound & & msg . deliveryType ! = eDeliveryTypeLocal )
i2p : : data : : netdb . PostI2NPMsg ( CopyI2NPMessage ( msg . data ) ) ;
2013-11-11 03:19:49 +04:00
switch ( msg . deliveryType )
{
case eDeliveryTypeLocal :
2014-03-13 04:13:49 +04:00
i2p : : HandleI2NPMessage ( msg . data ) ;
2013-11-11 03:19:49 +04:00
break ;
case eDeliveryTypeTunnel :
2015-12-09 18:03:51 +03:00
if ( ! m_IsInbound ) // outbound transit tunnel
i2p : : transport : : transports . SendMessage ( msg . hash , i2p : : CreateTunnelGatewayMsg ( msg . tunnelID , msg . data ) ) ;
2014-07-10 20:44:49 +04:00
else
2016-01-20 03:00:00 +03:00
LogPrint ( eLogError , " TunnelMessage: Delivery type 'tunnel' arrived from an inbound tunnel, dropped " ) ;
2015-12-09 18:03:51 +03:00
break ;
2018-01-06 06:48:51 +03:00
case eDeliveryTypeRouter :
2015-12-09 18:03:51 +03:00
if ( ! m_IsInbound ) // outbound transit tunnel
i2p : : transport : : transports . SendMessage ( msg . hash , msg . data ) ;
2018-01-06 06:48:51 +03:00
else // we shouldn't send this message. possible leakage
2016-01-20 03:00:00 +03:00
LogPrint ( eLogError , " TunnelMessage: Delivery type 'router' arrived from an inbound tunnel, dropped " ) ;
2013-11-11 03:19:49 +04:00
break ;
default :
2015-02-05 06:05:09 +03:00
LogPrint ( eLogError , " TunnelMessage: Unknown delivery type " , ( int ) msg . deliveryType ) ;
2018-01-06 06:48:51 +03:00
} ;
2016-11-09 22:51:55 +03:00
}
void TunnelEndpoint : : Cleanup ( )
{
auto ts = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
// out-of-sequence fragments
for ( auto it = m_OutOfSequenceFragments . begin ( ) ; it ! = m_OutOfSequenceFragments . end ( ) ; )
{
2021-06-27 00:40:25 +03:00
if ( ts > it - > second - > receiveTime + i2p : : I2NP_MESSAGE_EXPIRATION_TIMEOUT )
2016-11-09 22:51:55 +03:00
it = m_OutOfSequenceFragments . erase ( it ) ;
else
+ + it ;
}
2016-12-07 00:23:52 +03:00
// incomplete messages
for ( auto it = m_IncompleteMessages . begin ( ) ; it ! = m_IncompleteMessages . end ( ) ; )
{
if ( ts > it - > second . receiveTime + i2p : : I2NP_MESSAGE_EXPIRATION_TIMEOUT )
it = m_IncompleteMessages . erase ( it ) ;
else
+ + it ;
}
2018-01-06 06:48:51 +03:00
}
}
2013-11-11 03:19:49 +04:00
}