2020-05-22 13:18:41 +00:00
/*
2024-01-14 22:16:31 +00:00
* Copyright ( c ) 2013 - 2024 , The PurpleI2P Project
2020-05-22 13:18:41 +00:00
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
2015-04-09 16:45:00 +00:00
# include "Log.h"
# include "I2NPProtocol.h"
# include "Transports.h"
2017-04-22 00:04:16 +00:00
# include "NetDb.hpp"
2015-04-09 16:45:00 +00:00
# include "NetDbRequests.h"
2024-01-14 22:16:31 +00:00
# include "ECIESX25519AEADRatchetSession.h"
2024-05-15 17:31:31 +00:00
# include "RouterContext.h"
2015-04-09 16:45:00 +00:00
namespace i2p
{
namespace data
{
2024-01-16 00:32:17 +00:00
RequestedDestination : : RequestedDestination ( const IdentHash & destination , bool isExploratory , bool direct ) :
2024-04-25 22:52:10 +00:00
m_Destination ( destination ) , m_IsExploratory ( isExploratory ) , m_IsDirect ( direct ) , m_IsActive ( true ) ,
2024-05-15 17:31:31 +00:00
m_CreationTime ( i2p : : util : : GetSecondsSinceEpoch ( ) ) , m_LastRequestTime ( 0 ) , m_NumAttempts ( 0 )
2024-01-16 00:32:17 +00:00
{
2024-05-15 17:31:31 +00:00
if ( i2p : : context . IsFloodfill ( ) )
m_ExcludedPeers . insert ( i2p : : context . GetIdentHash ( ) ) ; // exclude self if floodfill
2024-01-16 00:32:17 +00:00
}
RequestedDestination : : ~ RequestedDestination ( )
{
if ( m_RequestComplete ) m_RequestComplete ( nullptr ) ;
}
2015-06-22 19:47:45 +00:00
std : : shared_ptr < I2NPMessage > RequestedDestination : : CreateRequestMessage ( std : : shared_ptr < const RouterInfo > router ,
2015-04-09 16:45:00 +00:00
std : : shared_ptr < const i2p : : tunnel : : InboundTunnel > replyTunnel )
{
2024-04-26 22:11:05 +00:00
std : : lock_guard < std : : mutex > l ( m_ExcludedPeersMutex ) ;
2016-11-14 17:05:44 +00:00
std : : shared_ptr < I2NPMessage > msg ;
if ( replyTunnel )
2018-01-06 03:48:51 +00:00
msg = i2p : : CreateRouterInfoDatabaseLookupMsg ( m_Destination ,
2020-03-01 10:25:50 +00:00
replyTunnel - > GetNextIdentHash ( ) , replyTunnel - > GetNextTunnelID ( ) , m_IsExploratory ,
& m_ExcludedPeers ) ;
2016-11-14 17:05:44 +00:00
else
msg = i2p : : CreateRouterInfoDatabaseLookupMsg ( m_Destination , i2p : : context . GetIdentHash ( ) , 0 , m_IsExploratory , & m_ExcludedPeers ) ;
if ( router )
m_ExcludedPeers . insert ( router - > GetIdentHash ( ) ) ;
2024-01-16 00:32:17 +00:00
m_LastRequestTime = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2024-05-15 17:31:31 +00:00
m_NumAttempts + + ;
2015-11-03 14:15:49 +00:00
return msg ;
2018-01-06 03:48:51 +00:00
}
2015-04-09 16:45:00 +00:00
2015-06-17 16:25:02 +00:00
std : : shared_ptr < I2NPMessage > RequestedDestination : : CreateRequestMessage ( const IdentHash & floodfill )
2015-04-09 16:45:00 +00:00
{
2024-04-26 22:11:05 +00:00
std : : lock_guard < std : : mutex > l ( m_ExcludedPeersMutex ) ;
2018-01-06 03:48:51 +00:00
auto msg = i2p : : CreateRouterInfoDatabaseLookupMsg ( m_Destination ,
2015-04-09 16:45:00 +00:00
i2p : : context . GetRouterInfo ( ) . GetIdentHash ( ) , 0 , false , & m_ExcludedPeers ) ;
m_ExcludedPeers . insert ( floodfill ) ;
2024-05-15 18:12:57 +00:00
m_NumAttempts + + ;
2024-01-16 00:32:17 +00:00
m_LastRequestTime = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2015-11-03 14:15:49 +00:00
return msg ;
2018-01-06 03:48:51 +00:00
}
2015-04-09 16:45:00 +00:00
2024-04-26 19:35:32 +00:00
bool RequestedDestination : : IsExcluded ( const IdentHash & ident ) const
{
2024-04-26 22:11:05 +00:00
std : : lock_guard < std : : mutex > l ( m_ExcludedPeersMutex ) ;
2024-04-26 19:35:32 +00:00
return m_ExcludedPeers . count ( ident ) ;
}
2015-04-09 16:45:00 +00:00
void RequestedDestination : : ClearExcludedPeers ( )
{
2024-04-26 22:11:05 +00:00
std : : lock_guard < std : : mutex > l ( m_ExcludedPeersMutex ) ;
2015-04-09 16:45:00 +00:00
m_ExcludedPeers . clear ( ) ;
2018-01-06 03:48:51 +00:00
}
2024-05-15 17:31:31 +00:00
std : : unordered_set < IdentHash > RequestedDestination : : GetExcludedPeers ( ) const
2024-04-27 12:38:43 +00:00
{
std : : lock_guard < std : : mutex > l ( m_ExcludedPeersMutex ) ;
return m_ExcludedPeers ;
}
2015-04-09 16:45:00 +00:00
void RequestedDestination : : Success ( std : : shared_ptr < RouterInfo > r )
{
2024-05-03 17:00:30 +00:00
if ( m_IsActive )
{
m_IsActive = false ;
if ( m_RequestComplete )
{
m_RequestComplete ( r ) ;
m_RequestComplete = nullptr ;
}
}
2015-04-09 16:45:00 +00:00
}
void RequestedDestination : : Fail ( )
{
2024-05-03 17:00:30 +00:00
if ( m_IsActive )
{
m_IsActive = false ;
if ( m_RequestComplete )
{
m_RequestComplete ( nullptr ) ;
m_RequestComplete = nullptr ;
}
}
2015-04-09 16:45:00 +00:00
}
2024-05-22 01:25:19 +00:00
NetDbRequests : : NetDbRequests ( ) :
RunnableServiceWithWork ( " NetDbReq " ) ,
m_ManageRequestsTimer ( GetIOService ( ) )
{
}
NetDbRequests : : ~ NetDbRequests ( )
{
Stop ( ) ;
}
2015-04-09 16:45:00 +00:00
void NetDbRequests : : Start ( )
{
2024-05-05 15:24:44 +00:00
m_LastPoolCleanUpTime = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2024-05-22 01:25:19 +00:00
if ( ! IsRunning ( ) )
{
StartIOService ( ) ;
ScheduleManageRequests ( ) ;
}
2015-04-09 16:45:00 +00:00
}
void NetDbRequests : : Stop ( )
{
2024-05-22 01:25:19 +00:00
if ( IsRunning ( ) )
{
m_ManageRequestsTimer . cancel ( ) ;
StopIOService ( ) ;
m_RequestedDestinations . clear ( ) ;
m_RequestedDestinationsPool . CleanUpMt ( ) ;
}
2015-04-09 16:45:00 +00:00
}
2024-01-14 22:16:31 +00:00
std : : shared_ptr < RequestedDestination > NetDbRequests : : CreateRequest ( const IdentHash & destination ,
bool isExploratory , bool direct , RequestedDestination : : RequestComplete requestComplete )
2015-04-09 16:45:00 +00:00
{
// request RouterInfo directly
2024-05-05 15:24:44 +00:00
auto dest = m_RequestedDestinationsPool . AcquireSharedMt ( destination , isExploratory , direct ) ;
2015-04-09 16:45:00 +00:00
dest - > SetRequestComplete ( requestComplete ) ;
{
2024-01-14 22:16:31 +00:00
std : : unique_lock < std : : mutex > l ( m_RequestedDestinationsMutex ) ;
2024-01-14 23:54:21 +00:00
auto ret = m_RequestedDestinations . emplace ( destination , dest ) ;
if ( ! ret . second ) // not inserted
{
dest - > SetRequestComplete ( nullptr ) ; // don't call requestComplete in destructor
2024-05-03 17:34:11 +00:00
dest = ret . first - > second ; // existing one
if ( requestComplete & & dest - > IsActive ( ) )
2024-01-14 23:54:21 +00:00
{
2024-05-03 17:34:11 +00:00
auto prev = dest - > GetRequestComplete ( ) ;
2024-01-14 23:54:21 +00:00
if ( prev ) // if already set
2024-05-03 17:34:11 +00:00
dest - > SetRequestComplete (
2024-01-14 23:54:21 +00:00
[ requestComplete , prev ] ( std : : shared_ptr < RouterInfo > r )
{
prev ( r ) ; // call previous
requestComplete ( r ) ; // then new
} ) ;
else
2024-05-03 17:34:11 +00:00
dest - > SetRequestComplete ( requestComplete ) ;
2024-01-14 23:54:21 +00:00
}
2018-01-06 03:48:51 +00:00
return nullptr ;
2024-01-14 23:54:21 +00:00
}
2015-04-09 16:45:00 +00:00
}
return dest ;
2018-01-06 03:48:51 +00:00
}
2015-04-09 16:45:00 +00:00
void NetDbRequests : : RequestComplete ( const IdentHash & ident , std : : shared_ptr < RouterInfo > r )
{
2016-08-16 02:36:58 +00:00
std : : shared_ptr < RequestedDestination > request ;
{
std : : unique_lock < std : : mutex > l ( m_RequestedDestinationsMutex ) ;
auto it = m_RequestedDestinations . find ( ident ) ;
if ( it ! = m_RequestedDestinations . end ( ) )
2018-01-06 03:48:51 +00:00
{
2016-08-16 02:36:58 +00:00
request = it - > second ;
2024-05-03 17:00:30 +00:00
if ( request - > IsExploratory ( ) )
m_RequestedDestinations . erase ( it ) ;
// otherwise cache for a while
2018-01-06 03:48:51 +00:00
}
}
2016-08-16 02:36:58 +00:00
if ( request )
{
2015-04-09 16:45:00 +00:00
if ( r )
2016-08-16 02:36:58 +00:00
request - > Success ( r ) ;
2015-04-09 16:45:00 +00:00
else
2016-08-16 02:36:58 +00:00
request - > Fail ( ) ;
2018-01-06 03:48:51 +00:00
}
2015-04-09 16:45:00 +00:00
}
std : : shared_ptr < RequestedDestination > NetDbRequests : : FindRequest ( const IdentHash & ident ) const
{
2016-08-16 02:36:58 +00:00
std : : unique_lock < std : : mutex > l ( m_RequestedDestinationsMutex ) ;
2015-04-09 16:45:00 +00:00
auto it = m_RequestedDestinations . find ( ident ) ;
if ( it ! = m_RequestedDestinations . end ( ) )
return it - > second ;
return nullptr ;
2018-01-06 03:48:51 +00:00
}
2015-04-09 16:45:00 +00:00
void NetDbRequests : : ManageRequests ( )
{
2018-01-06 03:48:51 +00:00
uint64_t ts = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2024-05-05 15:24:44 +00:00
if ( ts > m_LastPoolCleanUpTime + REQUESTED_DESTINATIONS_POOL_CLEANUP_INTERVAL )
{
m_RequestedDestinationsPool . CleanUpMt ( ) ;
m_LastPoolCleanUpTime = ts ;
}
2018-01-06 03:48:51 +00:00
std : : unique_lock < std : : mutex > l ( m_RequestedDestinationsMutex ) ;
2015-04-09 16:45:00 +00:00
for ( auto it = m_RequestedDestinations . begin ( ) ; it ! = m_RequestedDestinations . end ( ) ; )
{
auto & dest = it - > second ;
2024-05-03 17:00:30 +00:00
if ( dest - > IsActive ( ) | | ts < dest - > GetCreationTime ( ) + REQUEST_CACHE_TIME )
2024-05-01 16:08:47 +00:00
{
2024-05-03 17:00:30 +00:00
if ( ! dest - > IsExploratory ( ) )
{
// regular request
bool done = false ;
if ( ts < dest - > GetCreationTime ( ) + MAX_REQUEST_TIME )
{
if ( ts > dest - > GetLastRequestTime ( ) + MIN_REQUEST_TIME ) // try next floodfill if no response after min interval
done = ! SendNextRequest ( dest ) ;
}
else // request is expired
done = true ;
if ( done )
dest - > Fail ( ) ;
it + + ;
}
else
{
// exploratory
if ( ts > = dest - > GetCreationTime ( ) + MAX_EXPLORATORY_REQUEST_TIME )
{
dest - > Fail ( ) ;
it = m_RequestedDestinations . erase ( it ) ; // delete expired exploratory request right a way
}
else
it + + ;
}
2024-05-01 16:08:47 +00:00
}
2015-04-09 16:45:00 +00:00
else
2024-05-03 17:00:30 +00:00
it = m_RequestedDestinations . erase ( it ) ;
2018-01-06 03:48:51 +00:00
}
2015-04-09 16:45:00 +00:00
}
2024-01-14 22:16:31 +00:00
bool NetDbRequests : : SendNextRequest ( std : : shared_ptr < RequestedDestination > dest )
{
2024-05-03 17:34:11 +00:00
if ( ! dest | | ! dest - > IsActive ( ) ) return false ;
2024-01-14 22:16:31 +00:00
bool ret = true ;
2024-05-15 17:31:31 +00:00
auto count = dest - > GetNumAttempts ( ) ;
2024-01-14 22:16:31 +00:00
if ( ! dest - > IsExploratory ( ) & & count < MAX_NUM_REQUEST_ATTEMPTS )
{
auto nextFloodfill = netdb . GetClosestFloodfill ( dest - > GetDestination ( ) , dest - > GetExcludedPeers ( ) ) ;
if ( nextFloodfill )
{
bool direct = dest - > IsDirect ( ) ;
if ( direct & & ! nextFloodfill - > IsReachableFrom ( i2p : : context . GetRouterInfo ( ) ) & &
! i2p : : transport : : transports . IsConnected ( nextFloodfill - > GetIdentHash ( ) ) )
direct = false ; // floodfill can't be reached directly
if ( direct )
2024-01-30 00:54:43 +00:00
{
2024-05-02 17:17:15 +00:00
if ( CheckLogLevel ( eLogDebug ) )
LogPrint ( eLogDebug , " NetDbReq: Try " , dest - > GetDestination ( ) . ToBase64 ( ) , " at " , count , " floodfill " , nextFloodfill - > GetIdentHash ( ) . ToBase64 ( ) , " directly " ) ;
2024-01-30 00:54:43 +00:00
auto msg = dest - > CreateRequestMessage ( nextFloodfill - > GetIdentHash ( ) ) ;
2024-05-06 22:23:20 +00:00
auto s = shared_from_this ( ) ;
msg - > onDrop = [ s , dest ] ( ) { if ( dest - > IsActive ( ) ) s - > SendNextRequest ( dest ) ; } ;
2024-01-30 00:54:43 +00:00
i2p : : transport : : transports . SendMessage ( nextFloodfill - > GetIdentHash ( ) , msg ) ;
}
2024-01-14 22:16:31 +00:00
else
{
auto pool = i2p : : tunnel : : tunnels . GetExploratoryPool ( ) ;
2024-04-26 17:48:44 +00:00
if ( pool )
{
auto outbound = pool - > GetNextOutboundTunnel ( ) ;
auto inbound = pool - > GetNextInboundTunnel ( ) ;
if ( nextFloodfill & & outbound & & inbound )
{
2024-05-02 17:17:15 +00:00
if ( CheckLogLevel ( eLogDebug ) )
LogPrint ( eLogDebug , " NetDbReq: Try " , dest - > GetDestination ( ) . ToBase64 ( ) , " at " , count , " floodfill " , nextFloodfill - > GetIdentHash ( ) . ToBase64 ( ) , " through tunnels " ) ;
2024-04-26 17:48:44 +00:00
auto msg = dest - > CreateRequestMessage ( nextFloodfill , inbound ) ;
2024-05-06 22:23:20 +00:00
auto s = shared_from_this ( ) ;
msg - > onDrop = [ s , dest ] ( ) { if ( dest - > IsActive ( ) ) s - > SendNextRequest ( dest ) ; } ;
2024-04-26 17:48:44 +00:00
outbound - > SendTunnelDataMsgTo ( nextFloodfill - > GetIdentHash ( ) , 0 ,
i2p : : garlic : : WrapECIESX25519MessageForRouter ( msg , nextFloodfill - > GetIdentity ( ) - > GetEncryptionPublicKey ( ) ) ) ;
}
else
{
ret = false ;
if ( ! inbound ) LogPrint ( eLogWarning , " NetDbReq: No inbound tunnels " ) ;
if ( ! outbound ) LogPrint ( eLogWarning , " NetDbReq: No outbound tunnels " ) ;
}
2024-01-14 22:16:31 +00:00
}
else
{
ret = false ;
2024-04-26 17:48:44 +00:00
LogPrint ( eLogWarning , " NetDbReq: Exploratory pool is not ready " ) ;
}
2024-01-14 22:16:31 +00:00
}
}
else
{
ret = false ;
2024-05-13 18:45:41 +00:00
LogPrint ( eLogWarning , " NetDbReq: No more floodfills for " , dest - > GetDestination ( ) . ToBase64 ( ) , " after " , count , " attempts " ) ;
2024-01-14 22:16:31 +00:00
}
}
else
{
if ( ! dest - > IsExploratory ( ) )
2024-05-13 18:45:41 +00:00
LogPrint ( eLogWarning , " NetDbReq: " , dest - > GetDestination ( ) . ToBase64 ( ) , " not found after " , MAX_NUM_REQUEST_ATTEMPTS , " attempts " ) ;
2024-01-14 22:16:31 +00:00
ret = false ;
}
return ret ;
}
2024-05-22 01:25:19 +00:00
void NetDbRequests : : ScheduleManageRequests ( )
{
m_ManageRequestsTimer . expires_from_now ( boost : : posix_time : : seconds ( MANAGE_REQUESTS_INTERVAL ) ) ;
m_ManageRequestsTimer . async_wait ( std : : bind ( & NetDbRequests : : HandleManageRequestsTimer ,
this , std : : placeholders : : _1 ) ) ;
}
void NetDbRequests : : HandleManageRequestsTimer ( const boost : : system : : error_code & ecode )
{
if ( ecode ! = boost : : asio : : error : : operation_aborted )
{
if ( i2p : : tunnel : : tunnels . GetExploratoryPool ( ) ) // expolratory pool is ready?
ManageRequests ( ) ;
ScheduleManageRequests ( ) ;
}
}
2015-04-09 16:45:00 +00:00
}
}