2022-02-04 20:01:18 +00:00
/*
2024-01-11 20:39:42 +00:00
* Copyright ( c ) 2022 - 2024 , The PurpleI2P Project
2022-02-04 20:01:18 +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
*/
2022-07-30 20:31:44 +00:00
# include <random>
2022-03-01 02:46:00 +00:00
# include "Log.h"
2022-02-28 01:15:14 +00:00
# include "RouterContext.h"
2022-02-05 20:58:39 +00:00
# include "Transports.h"
2022-03-23 18:06:55 +00:00
# include "NetDb.hpp"
2022-06-05 23:33:36 +00:00
# include "Config.h"
2022-02-04 20:01:18 +00:00
# include "SSU2.h"
namespace i2p
{
namespace transport
2022-10-09 17:24:43 +00:00
{
2022-03-11 21:17:44 +00:00
SSU2Server : : SSU2Server ( ) :
2022-04-05 20:27:52 +00:00
RunnableServiceWithWork ( " SSU2 " ) , m_ReceiveService ( " SSU2r " ) ,
m_SocketV4 ( m_ReceiveService . GetService ( ) ) , m_SocketV6 ( m_ReceiveService . GetService ( ) ) ,
2022-06-17 19:16:12 +00:00
m_AddressV4 ( boost : : asio : : ip : : address_v4 ( ) ) , m_AddressV6 ( boost : : asio : : ip : : address_v6 ( ) ) ,
2023-01-14 22:05:09 +00:00
m_TerminationTimer ( GetService ( ) ) , m_CleanupTimer ( GetService ( ) ) , m_ResendTimer ( GetService ( ) ) ,
2022-07-21 01:55:48 +00:00
m_IntroducersUpdateTimer ( GetService ( ) ) , m_IntroducersUpdateTimerV6 ( GetService ( ) ) ,
2023-12-30 22:16:28 +00:00
m_IsPublished ( true ) , m_IsSyncClockFromPeers ( true ) , m_PendingTimeOffset ( 0 ) ,
2024-06-08 02:10:52 +00:00
m_Rng ( i2p : : util : : GetMonotonicMicroseconds ( ) % 1000000LL ) , m_IsThroughProxy ( false )
2022-03-01 02:46:00 +00:00
{
}
2022-03-11 21:17:44 +00:00
void SSU2Server : : Start ( )
{
if ( ! IsRunning ( ) )
{
StartIOService ( ) ;
2022-07-21 01:55:48 +00:00
i2p : : config : : GetOption ( " ssu2.published " , m_IsPublished ) ;
2022-08-08 23:57:48 +00:00
i2p : : config : : GetOption ( " nettime.frompeers " , m_IsSyncClockFromPeers ) ;
2022-04-05 20:27:52 +00:00
bool found = false ;
2022-12-05 00:21:51 +00:00
auto addresses = i2p : : context . GetRouterInfo ( ) . GetAddresses ( ) ;
if ( ! addresses ) return ;
for ( const auto & address : * addresses )
2022-03-11 21:17:44 +00:00
{
if ( ! address ) continue ;
2022-03-14 01:34:11 +00:00
if ( address - > transportStyle = = i2p : : data : : RouterInfo : : eTransportSSU2 )
2022-03-11 21:17:44 +00:00
{
2022-10-26 20:05:40 +00:00
if ( m_IsThroughProxy )
{
found = true ;
2022-10-27 17:56:42 +00:00
if ( address - > IsV6 ( ) )
2022-12-13 18:41:58 +00:00
{
2022-10-31 22:11:36 +00:00
uint16_t mtu ; i2p : : config : : GetOption ( " ssu2.mtu6 " , mtu ) ;
if ( ! mtu | | mtu > SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE )
mtu = SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE ;
i2p : : context . SetMTU ( mtu , false ) ;
2022-12-13 18:41:58 +00:00
}
else
{
2022-10-31 22:11:36 +00:00
uint16_t mtu ; i2p : : config : : GetOption ( " ssu2.mtu4 " , mtu ) ;
if ( ! mtu | | mtu > SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE )
mtu = SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE ;
i2p : : context . SetMTU ( mtu , true ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-26 22:55:13 +00:00
continue ; // we don't need port for proxy
2022-12-13 18:41:58 +00:00
}
2022-03-17 22:45:14 +00:00
auto port = address - > port ;
2022-03-14 01:34:11 +00:00
if ( ! port )
{
uint16_t ssu2Port ; i2p : : config : : GetOption ( " ssu2.port " , ssu2Port ) ;
if ( ssu2Port ) port = ssu2Port ;
else
{
uint16_t p ; i2p : : config : : GetOption ( " port " , p ) ;
2022-11-23 03:03:19 +00:00
if ( p ) port = p ;
2022-05-20 16:56:05 +00:00
}
}
2022-03-14 01:34:11 +00:00
if ( port )
2022-05-20 16:56:05 +00:00
{
2022-03-17 22:45:14 +00:00
if ( address - > IsV4 ( ) )
2022-04-05 20:14:13 +00:00
{
2022-04-05 20:27:52 +00:00
found = true ;
2023-05-26 22:00:19 +00:00
LogPrint ( eLogDebug , " SSU2: Opening IPv4 socket at Start " ) ;
2022-06-17 19:16:12 +00:00
OpenSocket ( boost : : asio : : ip : : udp : : endpoint ( m_AddressV4 , port ) ) ;
2022-04-05 20:27:52 +00:00
m_ReceiveService . GetService ( ) . post (
2022-05-20 16:56:05 +00:00
[ this ] ( )
{
2022-04-05 20:14:13 +00:00
Receive ( m_SocketV4 ) ;
} ) ;
2022-07-21 01:55:48 +00:00
ScheduleIntroducersUpdateTimer ( ) ; // wait for 30 seconds and decide if we need introducers
2022-05-20 16:56:05 +00:00
}
2022-03-17 22:45:14 +00:00
if ( address - > IsV6 ( ) )
2022-05-20 16:56:05 +00:00
{
2022-04-05 20:27:52 +00:00
found = true ;
2023-05-26 22:00:19 +00:00
LogPrint ( eLogDebug , " SSU2: Opening IPv6 socket at Start " ) ;
2022-06-17 19:16:12 +00:00
OpenSocket ( boost : : asio : : ip : : udp : : endpoint ( m_AddressV6 , port ) ) ;
2022-04-05 20:27:52 +00:00
m_ReceiveService . GetService ( ) . post (
2022-05-20 16:56:05 +00:00
[ this ] ( )
{
2022-04-05 20:14:13 +00:00
Receive ( m_SocketV6 ) ;
} ) ;
2022-07-21 01:55:48 +00:00
ScheduleIntroducersUpdateTimerV6 ( ) ; // wait for 30 seconds and decide if we need introducers
2022-05-20 16:56:05 +00:00
}
}
2022-03-14 01:34:11 +00:00
else
2023-03-31 11:29:04 +00:00
LogPrint ( eLogCritical , " SSU2: Can't start server because port not specified " ) ;
2022-03-11 21:17:44 +00:00
}
2022-03-19 00:21:31 +00:00
}
2022-04-05 20:27:52 +00:00
if ( found )
2022-12-13 18:41:58 +00:00
{
2022-10-26 20:05:40 +00:00
if ( m_IsThroughProxy )
ConnectToProxy ( ) ;
2022-10-26 22:55:13 +00:00
m_ReceiveService . Start ( ) ;
2022-12-13 18:41:58 +00:00
}
2022-03-19 00:21:31 +00:00
ScheduleTermination ( ) ;
2023-01-14 22:05:09 +00:00
ScheduleCleanup ( ) ;
2022-09-03 19:38:52 +00:00
ScheduleResend ( false ) ;
2022-05-20 16:56:05 +00:00
}
2022-03-11 21:17:44 +00:00
}
2022-05-20 16:56:05 +00:00
2022-03-11 21:17:44 +00:00
void SSU2Server : : Stop ( )
{
2022-07-26 22:46:05 +00:00
if ( IsRunning ( ) )
2022-10-09 17:24:43 +00:00
{
2022-07-26 22:46:05 +00:00
m_TerminationTimer . cancel ( ) ;
2023-01-14 22:05:09 +00:00
m_CleanupTimer . cancel ( ) ;
2022-07-26 22:46:05 +00:00
m_ResendTimer . cancel ( ) ;
m_IntroducersUpdateTimer . cancel ( ) ;
m_IntroducersUpdateTimerV6 . cancel ( ) ;
2022-10-09 17:24:43 +00:00
}
2022-07-26 22:46:05 +00:00
auto sessions = m_Sessions ;
for ( auto & it : sessions )
2022-10-09 17:24:43 +00:00
{
2022-07-10 21:13:25 +00:00
it . second - > RequestTermination ( eSSU2TerminationReasonRouterShutdown ) ;
2022-06-10 16:50:55 +00:00
it . second - > Done ( ) ;
2022-10-09 17:24:43 +00:00
}
2022-04-05 20:27:52 +00:00
if ( context . SupportsV4 ( ) | | context . SupportsV6 ( ) )
m_ReceiveService . Stop ( ) ;
2022-06-10 16:50:55 +00:00
m_SocketV4 . close ( ) ;
m_SocketV6 . close ( ) ;
2022-10-09 17:24:43 +00:00
2022-10-17 01:23:28 +00:00
if ( m_UDPAssociateSocket )
2022-12-13 18:41:58 +00:00
{
2022-10-17 01:23:28 +00:00
m_UDPAssociateSocket - > close ( ) ;
m_UDPAssociateSocket . reset ( nullptr ) ;
2022-12-13 18:41:58 +00:00
}
2022-03-11 21:17:44 +00:00
StopIOService ( ) ;
2022-07-26 22:46:05 +00:00
m_Sessions . clear ( ) ;
m_SessionsByRouterHash . clear ( ) ;
m_PendingOutgoingSessions . clear ( ) ;
m_Relays . clear ( ) ;
2024-09-29 02:05:25 +00:00
m_PeerTests . clear ( ) ;
2022-07-26 22:46:05 +00:00
m_Introducers . clear ( ) ;
m_IntroducersV6 . clear ( ) ;
2024-09-23 18:16:24 +00:00
m_ConnectedRecently . clear ( ) ;
m_RequestedPeerTests . clear ( ) ;
2022-05-20 16:56:05 +00:00
}
2022-03-14 23:25:59 +00:00
2022-06-17 19:16:12 +00:00
void SSU2Server : : SetLocalAddress ( const boost : : asio : : ip : : address & localAddress )
{
if ( localAddress . is_unspecified ( ) ) return ;
if ( localAddress . is_v4 ( ) )
2022-10-09 17:24:43 +00:00
{
2022-06-17 19:16:12 +00:00
m_AddressV4 = localAddress ;
2022-10-31 22:11:36 +00:00
uint16_t mtu ; i2p : : config : : GetOption ( " ssu2.mtu4 " , mtu ) ;
if ( ! mtu ) mtu = i2p : : util : : net : : GetMTU ( localAddress ) ;
2022-07-24 19:24:01 +00:00
if ( mtu < ( int ) SSU2_MIN_PACKET_SIZE ) mtu = SSU2_MIN_PACKET_SIZE ;
if ( mtu > ( int ) SSU2_MAX_PACKET_SIZE ) mtu = SSU2_MAX_PACKET_SIZE ;
i2p : : context . SetMTU ( mtu , true ) ;
2022-10-09 17:24:43 +00:00
}
2022-06-17 19:16:12 +00:00
else if ( localAddress . is_v6 ( ) )
2022-10-09 17:24:43 +00:00
{
2022-06-17 19:16:12 +00:00
m_AddressV6 = localAddress ;
2022-10-31 22:11:36 +00:00
uint16_t mtu ; i2p : : config : : GetOption ( " ssu2.mtu6 " , mtu ) ;
if ( ! mtu )
2022-12-13 18:41:58 +00:00
{
2022-10-31 22:11:36 +00:00
int maxMTU = i2p : : util : : net : : GetMaxMTU ( localAddress . to_v6 ( ) ) ;
mtu = i2p : : util : : net : : GetMTU ( localAddress ) ;
if ( mtu > maxMTU ) mtu = maxMTU ;
2022-12-13 18:41:58 +00:00
}
2023-01-03 18:25:19 +00:00
else
2022-10-31 22:11:36 +00:00
if ( mtu > ( int ) SSU2_MAX_PACKET_SIZE ) mtu = SSU2_MAX_PACKET_SIZE ;
2022-07-24 19:24:01 +00:00
if ( mtu < ( int ) SSU2_MIN_PACKET_SIZE ) mtu = SSU2_MIN_PACKET_SIZE ;
i2p : : context . SetMTU ( mtu , false ) ;
2022-10-09 17:24:43 +00:00
}
}
2022-06-21 20:20:39 +00:00
bool SSU2Server : : IsSupported ( const boost : : asio : : ip : : address & addr ) const
{
2022-10-27 17:56:42 +00:00
if ( m_IsThroughProxy )
return m_SocketV4 . is_open ( ) ;
2022-06-21 20:20:39 +00:00
if ( addr . is_v4 ( ) )
2022-10-09 17:24:43 +00:00
{
if ( m_SocketV4 . is_open ( ) )
2022-06-21 20:20:39 +00:00
return true ;
2022-10-09 17:24:43 +00:00
}
2022-06-21 20:20:39 +00:00
else if ( addr . is_v6 ( ) )
2022-10-09 17:24:43 +00:00
{
if ( m_SocketV6 . is_open ( ) )
2022-06-21 20:20:39 +00:00
return true ;
}
return false ;
2022-10-09 17:24:43 +00:00
}
2022-08-03 00:02:55 +00:00
uint16_t SSU2Server : : GetPort ( bool v4 ) const
{
boost : : system : : error_code ec ;
2022-10-27 17:56:42 +00:00
boost : : asio : : ip : : udp : : endpoint ep = ( v4 | | m_IsThroughProxy ) ? m_SocketV4 . local_endpoint ( ec ) : m_SocketV6 . local_endpoint ( ec ) ;
2022-08-03 00:02:55 +00:00
if ( ec ) return 0 ;
return ep . port ( ) ;
2022-10-09 17:24:43 +00:00
}
2024-09-18 18:35:59 +00:00
bool SSU2Server : : IsConnectedRecently ( const boost : : asio : : ip : : udp : : endpoint & ep )
2024-09-18 01:49:23 +00:00
{
if ( ! ep . port ( ) | | ep . address ( ) . is_unspecified ( ) ) return false ;
auto it = m_ConnectedRecently . find ( ep ) ;
if ( it ! = m_ConnectedRecently . end ( ) )
2024-09-18 18:35:59 +00:00
{
if ( i2p : : util : : GetSecondsSinceEpoch ( ) < = it - > second + SSU2_HOLE_PUNCH_EXPIRATION )
return true ;
else
m_ConnectedRecently . erase ( it ) ;
}
2024-09-18 01:49:23 +00:00
return false ;
}
void SSU2Server : : AddConnectedRecently ( const boost : : asio : : ip : : udp : : endpoint & ep , uint64_t ts )
{
if ( ! ep . port ( ) | | ep . address ( ) . is_unspecified ( ) | |
i2p : : util : : GetSecondsSinceEpoch ( ) > ts + SSU2_HOLE_PUNCH_EXPIRATION ) return ;
2024-09-18 18:35:59 +00:00
auto [ it , added ] = m_ConnectedRecently . try_emplace ( ep , ts ) ;
if ( ! added & & ts > it - > second )
it - > second = ts ; // renew timestamp of existing endpoint
2024-09-18 01:49:23 +00:00
}
2024-01-25 00:05:38 +00:00
void SSU2Server : : AdjustTimeOffset ( int64_t offset , std : : shared_ptr < const i2p : : data : : IdentityEx > from )
2023-12-30 22:16:28 +00:00
{
if ( offset )
{
if ( m_PendingTimeOffset ) // one more
{
2024-01-25 00:05:38 +00:00
if ( m_PendingTimeOffsetFrom & & from & &
m_PendingTimeOffsetFrom - > GetIdentHash ( ) . GetLL ( ) [ 0 ] ! = from - > GetIdentHash ( ) . GetLL ( ) [ 0 ] ) // from different routers
2023-12-31 19:39:59 +00:00
{
2024-01-25 00:05:38 +00:00
if ( std : : abs ( m_PendingTimeOffset - offset ) < SSU2_CLOCK_SKEW )
{
offset = ( m_PendingTimeOffset + offset ) / 2 ; // average
LogPrint ( eLogWarning , " SSU2: Clock adjusted by " , offset , " seconds " ) ;
i2p : : util : : AdjustTimeOffset ( offset ) ;
}
else
LogPrint ( eLogWarning , " SSU2: Time offsets are too different. Clock not adjusted " ) ;
m_PendingTimeOffset = 0 ;
m_PendingTimeOffsetFrom = nullptr ;
}
2023-12-31 19:39:59 +00:00
else
2024-01-25 00:05:38 +00:00
LogPrint ( eLogWarning , " SSU2: Time offsets from same router. Clock not adjusted " ) ;
2023-12-30 22:16:28 +00:00
}
else
2024-01-25 00:05:38 +00:00
{
2023-12-30 22:16:28 +00:00
m_PendingTimeOffset = offset ; // first
2024-01-25 00:05:38 +00:00
m_PendingTimeOffsetFrom = from ;
}
2023-12-30 22:16:28 +00:00
}
else
2024-01-25 00:05:38 +00:00
{
2023-12-30 22:16:28 +00:00
m_PendingTimeOffset = 0 ; // reset
2024-01-25 00:05:38 +00:00
m_PendingTimeOffsetFrom = nullptr ;
}
2023-12-30 22:16:28 +00:00
}
2022-03-17 22:45:14 +00:00
boost : : asio : : ip : : udp : : socket & SSU2Server : : OpenSocket ( const boost : : asio : : ip : : udp : : endpoint & localEndpoint )
2022-03-01 02:46:00 +00:00
{
2022-04-05 20:14:13 +00:00
boost : : asio : : ip : : udp : : socket & socket = localEndpoint . address ( ) . is_v6 ( ) ? m_SocketV6 : m_SocketV4 ;
2022-03-01 02:46:00 +00:00
try
{
2023-02-11 06:29:37 +00:00
if ( socket . is_open ( ) )
socket . close ( ) ;
2022-03-17 22:45:14 +00:00
socket . open ( localEndpoint . protocol ( ) ) ;
if ( localEndpoint . address ( ) . is_v6 ( ) )
socket . set_option ( boost : : asio : : ip : : v6_only ( true ) ) ;
2024-02-29 14:02:43 +00:00
uint64_t bufferSize = i2p : : context . GetBandwidthLimit ( ) * 1024 / 5 ; // max lag = 200ms
bufferSize = std : : max ( SSU2_SOCKET_MIN_BUFFER_SIZE , std : : min ( bufferSize , SSU2_SOCKET_MAX_BUFFER_SIZE ) ) ;
boost : : asio : : socket_base : : receive_buffer_size receiveBufferSizeSet ( bufferSize ) ;
boost : : asio : : socket_base : : send_buffer_size sendBufferSizeSet ( bufferSize ) ;
socket . set_option ( receiveBufferSizeSet ) ;
socket . set_option ( sendBufferSizeSet ) ;
boost : : asio : : socket_base : : receive_buffer_size receiveBufferSizeGet ;
boost : : asio : : socket_base : : send_buffer_size sendBufferSizeGet ;
socket . get_option ( receiveBufferSizeGet ) ;
socket . get_option ( sendBufferSizeGet ) ;
if ( receiveBufferSizeGet . value ( ) ! = receiveBufferSizeSet . value ( ) | |
sendBufferSizeGet . value ( ) ! = sendBufferSizeSet . value ( ) )
2024-02-29 08:27:52 +00:00
{
LogPrint ( eLogWarning , " SSU2: Socket receive buffer size: requested = " ,
2024-02-29 14:02:43 +00:00
receiveBufferSizeSet . value ( ) , " , got = " , receiveBufferSizeGet . value ( ) ) ;
2024-02-29 08:27:52 +00:00
LogPrint ( eLogWarning , " SSU2: Socket send buffer size: requested = " ,
2024-02-29 14:02:43 +00:00
sendBufferSizeSet . value ( ) , " , got = " , sendBufferSizeGet . value ( ) ) ;
2024-02-29 08:27:52 +00:00
}
else
{
2024-02-29 14:02:43 +00:00
LogPrint ( eLogInfo , " SSU2: Socket receive buffer size: " , receiveBufferSizeGet . value ( ) ) ;
LogPrint ( eLogInfo , " SSU2: Socket send buffer size: " , sendBufferSizeGet . value ( ) ) ;
2024-02-29 08:27:52 +00:00
}
2024-02-29 14:02:43 +00:00
2024-02-28 16:55:28 +00:00
socket . non_blocking ( true ) ;
2024-02-09 23:08:51 +00:00
}
catch ( std : : exception & ex )
{
LogPrint ( eLogCritical , " SSU2: Failed to open socket on " , localEndpoint . address ( ) , " : " , ex . what ( ) ) ;
ThrowFatal ( " Unable to start SSU2 transport on " , localEndpoint . address ( ) , " : " , ex . what ( ) ) ;
return socket ;
}
try
{
2022-03-17 22:45:14 +00:00
socket . bind ( localEndpoint ) ;
LogPrint ( eLogInfo , " SSU2: Start listening on " , localEndpoint ) ;
2022-03-01 02:46:00 +00:00
}
catch ( std : : exception & ex )
{
2024-02-09 23:08:51 +00:00
LogPrint ( eLogWarning , " SSU2: Failed to bind to " , localEndpoint , " : " , ex . what ( ) , " . Actual endpoint is " , socket . local_endpoint ( ) ) ;
// we can continue without binding being firewalled
2022-03-01 02:46:00 +00:00
}
2022-03-17 22:45:14 +00:00
return socket ;
2022-03-01 02:46:00 +00:00
}
2022-05-20 16:56:05 +00:00
2022-03-17 22:45:14 +00:00
void SSU2Server : : Receive ( boost : : asio : : ip : : udp : : socket & socket )
2022-03-14 23:25:59 +00:00
{
Packet * packet = m_PacketsPool . AcquireMt ( ) ;
2022-07-09 21:05:23 +00:00
socket . async_receive_from ( boost : : asio : : buffer ( packet - > buf , SSU2_MAX_PACKET_SIZE ) , packet - > from ,
2022-03-17 22:45:14 +00:00
std : : bind ( & SSU2Server : : HandleReceivedFrom , this , std : : placeholders : : _1 , std : : placeholders : : _2 , packet , std : : ref ( socket ) ) ) ;
2022-03-14 23:25:59 +00:00
}
2022-05-20 16:56:05 +00:00
void SSU2Server : : HandleReceivedFrom ( const boost : : system : : error_code & ecode , size_t bytes_transferred ,
2022-03-17 22:45:14 +00:00
Packet * packet , boost : : asio : : ip : : udp : : socket & socket )
2022-03-14 23:25:59 +00:00
{
2022-12-13 18:41:58 +00:00
if ( ! ecode
| | ecode = = boost : : asio : : error : : connection_refused
| | ecode = = boost : : asio : : error : : connection_reset
2023-05-26 22:00:19 +00:00
| | ecode = = boost : : asio : : error : : network_reset
2022-12-13 18:41:58 +00:00
| | ecode = = boost : : asio : : error : : network_unreachable
| | ecode = = boost : : asio : : error : : host_unreachable
# ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO
| | ecode . value ( ) = = boost : : winapi : : ERROR_CONNECTION_REFUSED_
2023-05-26 22:00:19 +00:00
| | ecode . value ( ) = = boost : : winapi : : WSAENETRESET_ // 10052
2022-12-13 18:41:58 +00:00
| | ecode . value ( ) = = boost : : winapi : : ERROR_NETWORK_UNREACHABLE_
| | ecode . value ( ) = = boost : : winapi : : ERROR_HOST_UNREACHABLE_
# endif
)
// just try continue reading when received ICMP response otherwise socket can crash,
// but better to find out which host were sent it and mark that router as unreachable
2022-03-14 23:25:59 +00:00
{
2022-03-26 20:35:07 +00:00
i2p : : transport : : transports . UpdateReceivedBytes ( bytes_transferred ) ;
2024-01-19 19:09:48 +00:00
if ( bytes_transferred < SSU2_MIN_RECEIVED_PACKET_SIZE )
{
// drop too short packets
m_PacketsPool . ReleaseMt ( packet ) ;
Receive ( socket ) ;
return ;
}
2022-03-14 23:25:59 +00:00
packet - > len = bytes_transferred ;
2024-01-19 19:09:48 +00:00
2022-04-12 15:42:51 +00:00
boost : : system : : error_code ec ;
size_t moreBytes = socket . available ( ec ) ;
2022-04-13 16:33:59 +00:00
if ( ! ec & & moreBytes )
2022-04-12 15:42:51 +00:00
{
2024-09-28 13:49:45 +00:00
auto packets = m_PacketsArrayPool . AcquireMt ( ) ;
packets - > AddPacket ( packet ) ;
while ( moreBytes & & packets - > numPackets < SSU2_MAX_NUM_PACKETS_PER_BATCH )
2022-04-12 15:42:51 +00:00
{
packet = m_PacketsPool . AcquireMt ( ) ;
2022-07-15 00:12:27 +00:00
packet - > len = socket . receive_from ( boost : : asio : : buffer ( packet - > buf , SSU2_MAX_PACKET_SIZE ) , packet - > from , 0 , ec ) ;
2022-04-12 15:42:51 +00:00
if ( ! ec )
{
i2p : : transport : : transports . UpdateReceivedBytes ( packet - > len ) ;
2024-09-26 12:48:17 +00:00
if ( packet - > len > = SSU2_MIN_RECEIVED_PACKET_SIZE )
2024-09-28 13:49:45 +00:00
{
if ( ! packets - > AddPacket ( packet ) )
{
LogPrint ( eLogError , " SSU2: Received packets array is full " ) ;
m_PacketsPool . ReleaseMt ( packet ) ;
}
}
2024-09-26 12:48:17 +00:00
else // drop too short packets
m_PacketsPool . ReleaseMt ( packet ) ;
2022-04-12 15:42:51 +00:00
moreBytes = socket . available ( ec ) ;
if ( ec ) break ;
}
else
{
LogPrint ( eLogError , " SSU2: receive_from error: code " , ec . value ( ) , " : " , ec . message ( ) ) ;
m_PacketsPool . ReleaseMt ( packet ) ;
break ;
}
}
2024-09-28 13:49:45 +00:00
GetService ( ) . post ( std : : bind ( & SSU2Server : : HandleReceivedPackets , this , packets ) ) ;
2022-05-20 16:56:05 +00:00
}
2022-04-13 16:33:59 +00:00
else
GetService ( ) . post ( std : : bind ( & SSU2Server : : HandleReceivedPacket , this , packet ) ) ;
2022-03-17 22:45:14 +00:00
Receive ( socket ) ;
2022-03-14 23:25:59 +00:00
}
else
{
m_PacketsPool . ReleaseMt ( packet ) ;
if ( ecode ! = boost : : asio : : error : : operation_aborted )
{
LogPrint ( eLogError , " SSU2: Receive error: code " , ecode . value ( ) , " : " , ecode . message ( ) ) ;
2022-11-08 15:37:34 +00:00
if ( m_IsThroughProxy )
{
m_UDPAssociateSocket . reset ( nullptr ) ;
m_ProxyRelayEndpoint . reset ( nullptr ) ;
m_SocketV4 . close ( ) ;
ConnectToProxy ( ) ;
}
else
2022-12-13 18:41:58 +00:00
{
2022-11-08 15:37:34 +00:00
auto ep = socket . local_endpoint ( ) ;
2023-05-26 22:00:19 +00:00
LogPrint ( eLogCritical , " SSU2: Reopening socket in HandleReceivedFrom: code " , ecode . value ( ) , " : " , ecode . message ( ) ) ;
2022-11-08 15:37:34 +00:00
OpenSocket ( ep ) ;
Receive ( socket ) ;
2022-12-13 18:41:58 +00:00
}
2022-03-14 23:25:59 +00:00
}
}
}
2022-04-05 20:14:13 +00:00
2022-04-13 16:33:59 +00:00
void SSU2Server : : HandleReceivedPacket ( Packet * packet )
{
if ( packet )
{
2022-10-26 20:05:40 +00:00
if ( m_IsThroughProxy )
ProcessNextPacketFromProxy ( packet - > buf , packet - > len ) ;
2022-12-13 18:41:58 +00:00
else
2022-10-26 20:05:40 +00:00
ProcessNextPacket ( packet - > buf , packet - > len , packet - > from ) ;
2022-04-13 16:33:59 +00:00
m_PacketsPool . ReleaseMt ( packet ) ;
2022-10-09 17:24:43 +00:00
if ( m_LastSession & & m_LastSession - > GetState ( ) ! = eSSU2SessionStateTerminated )
2022-07-25 22:46:25 +00:00
m_LastSession - > FlushData ( ) ;
2022-05-20 16:56:05 +00:00
}
}
2024-09-28 13:49:45 +00:00
void SSU2Server : : HandleReceivedPackets ( Packets * packets )
2022-04-05 20:14:13 +00:00
{
2024-09-28 13:49:45 +00:00
if ( ! packets ) return ;
2022-10-26 20:05:40 +00:00
if ( m_IsThroughProxy )
2024-09-28 13:49:45 +00:00
for ( size_t i = 0 ; i < packets - > numPackets ; i + + )
{
auto & packet = ( * packets ) [ i ] ;
2022-10-26 20:05:40 +00:00
ProcessNextPacketFromProxy ( packet - > buf , packet - > len ) ;
2024-09-28 13:49:45 +00:00
}
2022-12-13 18:41:58 +00:00
else
2024-09-28 13:49:45 +00:00
for ( size_t i = 0 ; i < packets - > numPackets ; i + + )
{
auto & packet = ( * packets ) [ i ] ;
2022-10-26 20:05:40 +00:00
ProcessNextPacket ( packet - > buf , packet - > len , packet - > from ) ;
2024-09-28 13:49:45 +00:00
}
m_PacketsPool . ReleaseMt ( packets - > data ( ) , packets - > numPackets ) ;
m_PacketsArrayPool . ReleaseMt ( packets ) ;
2022-10-09 17:24:43 +00:00
if ( m_LastSession & & m_LastSession - > GetState ( ) ! = eSSU2SessionStateTerminated )
2022-07-25 22:46:25 +00:00
m_LastSession - > FlushData ( ) ;
2022-05-20 16:56:05 +00:00
}
2022-03-27 20:39:58 +00:00
void SSU2Server : : AddSession ( std : : shared_ptr < SSU2Session > session )
2022-02-28 01:15:14 +00:00
{
2022-03-27 20:39:58 +00:00
if ( session )
2022-05-20 16:56:05 +00:00
{
2022-03-27 20:39:58 +00:00
m_Sessions . emplace ( session - > GetConnID ( ) , session ) ;
2024-09-25 00:03:15 +00:00
if ( session - > GetState ( ) ! = eSSU2SessionStatePeerTest )
AddSessionByRouterHash ( session ) ;
2022-05-20 16:56:05 +00:00
}
}
2022-02-28 01:15:14 +00:00
2022-03-27 20:39:58 +00:00
void SSU2Server : : RemoveSession ( uint64_t connID )
2022-03-01 02:46:00 +00:00
{
2022-04-28 17:11:51 +00:00
auto it = m_Sessions . find ( connID ) ;
if ( it ! = m_Sessions . end ( ) )
2022-05-20 16:56:05 +00:00
{
2024-09-25 00:03:15 +00:00
if ( it - > second - > GetState ( ) ! = eSSU2SessionStatePeerTest )
{
auto ident = it - > second - > GetRemoteIdentity ( ) ;
if ( ident )
{
2024-09-25 18:34:52 +00:00
std : : lock_guard < std : : mutex > l ( m_SessionsByRouterHashMutex ) ;
2024-09-25 00:03:15 +00:00
auto it1 = m_SessionsByRouterHash . find ( ident - > GetIdentHash ( ) ) ;
if ( it1 ! = m_SessionsByRouterHash . end ( ) & & it - > second = = it1 - > second . lock ( ) )
m_SessionsByRouterHash . erase ( it1 ) ;
}
}
2022-07-08 23:06:09 +00:00
if ( m_LastSession = = it - > second )
m_LastSession = nullptr ;
2022-04-28 17:11:51 +00:00
m_Sessions . erase ( it ) ;
2022-05-20 16:56:05 +00:00
}
}
2022-10-09 17:24:43 +00:00
2024-09-25 00:57:04 +00:00
void SSU2Server : : RequestRemoveSession ( uint64_t connID )
{
GetService ( ) . post ( [ connID , this ] ( ) { RemoveSession ( connID ) ; } ) ;
}
2022-05-20 16:56:05 +00:00
void SSU2Server : : AddSessionByRouterHash ( std : : shared_ptr < SSU2Session > session )
2022-04-28 17:11:51 +00:00
{
if ( session )
2022-05-20 16:56:05 +00:00
{
2022-04-28 17:11:51 +00:00
auto ident = session - > GetRemoteIdentity ( ) ;
if ( ident )
{
2024-09-25 18:34:52 +00:00
std : : shared_ptr < SSU2Session > oldSession ;
2022-04-28 17:11:51 +00:00
{
2024-09-25 18:34:52 +00:00
std : : lock_guard < std : : mutex > l ( m_SessionsByRouterHashMutex ) ;
auto ret = m_SessionsByRouterHash . emplace ( ident - > GetIdentHash ( ) , session ) ;
if ( ! ret . second )
{
oldSession = ret . first - > second . lock ( ) ;
2024-09-25 00:03:15 +00:00
// update session
ret . first - > second = session ;
}
2024-09-25 18:34:52 +00:00
}
if ( oldSession & & oldSession ! = session )
{
// session already exists
LogPrint ( eLogWarning , " SSU2: Session to " , ident - > GetIdentHash ( ) . ToBase64 ( ) , " already exists " ) ;
// move unsent msgs to new session
oldSession - > MoveSendQueue ( session ) ;
// terminate existing
GetService ( ) . post ( std : : bind ( & SSU2Session : : RequestTermination , oldSession , eSSU2TerminationReasonReplacedByNewSession ) ) ;
}
2022-05-20 16:56:05 +00:00
}
}
}
2022-06-22 15:59:29 +00:00
bool SSU2Server : : AddPendingOutgoingSession ( std : : shared_ptr < SSU2Session > session )
2022-03-27 20:39:58 +00:00
{
2022-06-22 15:59:29 +00:00
if ( ! session ) return false ;
2024-09-25 18:34:52 +00:00
std : : lock_guard < std : : mutex > l ( m_PendingOutgoingSessionsMutex ) ;
2022-06-22 15:59:29 +00:00
return m_PendingOutgoingSessions . emplace ( session - > GetRemoteEndpoint ( ) , session ) . second ;
2022-03-01 02:46:00 +00:00
}
2022-04-21 19:47:36 +00:00
2024-09-25 15:13:01 +00:00
std : : shared_ptr < SSU2Session > SSU2Server : : FindSession ( const i2p : : data : : IdentHash & ident )
2022-06-07 16:55:58 +00:00
{
2024-09-25 18:34:52 +00:00
std : : lock_guard < std : : mutex > l ( m_SessionsByRouterHashMutex ) ;
2022-06-07 16:55:58 +00:00
auto it = m_SessionsByRouterHash . find ( ident ) ;
if ( it ! = m_SessionsByRouterHash . end ( ) )
2024-09-25 15:13:01 +00:00
{
if ( ! it - > second . expired ( ) )
{
auto s = it - > second . lock ( ) ;
if ( s & & s - > GetState ( ) ! = eSSU2SessionStateTerminated )
return s ;
}
m_SessionsByRouterHash . erase ( it ) ;
}
2022-06-07 16:55:58 +00:00
return nullptr ;
2022-10-09 17:24:43 +00:00
}
2022-06-09 22:04:37 +00:00
2022-06-22 15:59:29 +00:00
std : : shared_ptr < SSU2Session > SSU2Server : : FindPendingOutgoingSession ( const boost : : asio : : ip : : udp : : endpoint & ep ) const
2022-10-09 17:24:43 +00:00
{
2024-09-25 18:34:52 +00:00
std : : lock_guard < std : : mutex > l ( m_PendingOutgoingSessionsMutex ) ;
2022-06-22 15:59:29 +00:00
auto it = m_PendingOutgoingSessions . find ( ep ) ;
if ( it ! = m_PendingOutgoingSessions . end ( ) )
return it - > second ;
return nullptr ;
}
2022-06-22 17:15:25 +00:00
void SSU2Server : : RemovePendingOutgoingSession ( const boost : : asio : : ip : : udp : : endpoint & ep )
{
2024-09-25 18:34:52 +00:00
std : : lock_guard < std : : mutex > l ( m_PendingOutgoingSessionsMutex ) ;
2022-06-22 17:15:25 +00:00
m_PendingOutgoingSessions . erase ( ep ) ;
2022-10-09 17:24:43 +00:00
}
2024-03-02 02:59:52 +00:00
std : : shared_ptr < SSU2Session > SSU2Server : : GetRandomPeerTestSession (
2024-08-10 18:43:29 +00:00
i2p : : data : : RouterInfo : : CompatibleTransports remoteTransports , const i2p : : data : : IdentHash & excluded )
2022-06-09 22:04:37 +00:00
{
if ( m_Sessions . empty ( ) ) return nullptr ;
2024-08-10 18:43:29 +00:00
int ind = m_Rng ( ) % m_Sessions . size ( ) ;
2022-06-09 22:04:37 +00:00
auto it = m_Sessions . begin ( ) ;
std : : advance ( it , ind ) ;
while ( it ! = m_Sessions . end ( ) )
{
2024-08-10 18:43:29 +00:00
if ( it - > second - > IsEstablished ( ) & & ( it - > second - > GetRemotePeerTestTransports ( ) & remoteTransports ) & &
2022-06-26 14:07:39 +00:00
it - > second - > GetRemoteIdentity ( ) - > GetIdentHash ( ) ! = excluded )
2022-06-09 22:04:37 +00:00
return it - > second ;
it + + ;
}
2022-11-25 20:37:52 +00:00
// not found, try from beginning
2022-06-09 22:04:37 +00:00
it = m_Sessions . begin ( ) ;
while ( it ! = m_Sessions . end ( ) & & ind )
{
2024-08-10 18:43:29 +00:00
if ( it - > second - > IsEstablished ( ) & & ( it - > second - > GetRemotePeerTestTransports ( ) & remoteTransports ) & &
2022-06-26 14:07:39 +00:00
it - > second - > GetRemoteIdentity ( ) - > GetIdentHash ( ) ! = excluded )
2022-06-09 22:04:37 +00:00
return it - > second ;
it + + ; ind - - ;
2022-10-09 17:24:43 +00:00
}
2022-06-09 22:04:37 +00:00
return nullptr ;
2022-10-09 17:24:43 +00:00
}
2022-04-21 19:47:36 +00:00
void SSU2Server : : AddRelay ( uint32_t tag , std : : shared_ptr < SSU2Session > relay )
{
m_Relays . emplace ( tag , relay ) ;
}
2022-05-20 16:56:05 +00:00
2022-04-21 19:47:36 +00:00
void SSU2Server : : RemoveRelay ( uint32_t tag )
{
m_Relays . erase ( tag ) ;
2022-05-20 16:56:05 +00:00
}
2022-04-21 19:47:36 +00:00
std : : shared_ptr < SSU2Session > SSU2Server : : FindRelaySession ( uint32_t tag )
{
auto it = m_Relays . find ( tag ) ;
if ( it ! = m_Relays . end ( ) )
{
2024-09-26 22:38:17 +00:00
if ( ! it - > second . expired ( ) )
{
auto s = it - > second . lock ( ) ;
if ( s & & s - > IsEstablished ( ) )
return s ;
}
m_Relays . erase ( it ) ;
2022-04-21 19:47:36 +00:00
}
return nullptr ;
}
2022-12-13 18:41:58 +00:00
2024-09-29 02:05:25 +00:00
bool SSU2Server : : AddPeerTest ( uint32_t nonce , std : : shared_ptr < SSU2Session > aliceSession , uint64_t ts )
{
return m_PeerTests . emplace ( nonce , std : : pair { aliceSession , ts } ) . second ;
}
std : : shared_ptr < SSU2Session > SSU2Server : : GetPeerTest ( uint32_t nonce )
{
auto it = m_PeerTests . find ( nonce ) ;
if ( it ! = m_PeerTests . end ( ) )
{
auto s = it - > second . first . lock ( ) ;
m_PeerTests . erase ( it ) ;
return s ;
}
return nullptr ;
}
2024-09-23 18:16:24 +00:00
bool SSU2Server : : AddRequestedPeerTest ( uint32_t nonce , std : : shared_ptr < SSU2PeerTestSession > session , uint64_t ts )
{
return m_RequestedPeerTests . emplace ( nonce , std : : pair { session , ts } ) . second ;
}
std : : shared_ptr < SSU2PeerTestSession > SSU2Server : : GetRequestedPeerTest ( uint32_t nonce )
{
auto it = m_RequestedPeerTests . find ( nonce ) ;
if ( it ! = m_RequestedPeerTests . end ( ) )
{
auto s = it - > second . first . lock ( ) ;
m_RequestedPeerTests . erase ( it ) ;
return s ;
}
return nullptr ;
}
2022-03-01 02:46:00 +00:00
void SSU2Server : : ProcessNextPacket ( uint8_t * buf , size_t len , const boost : : asio : : ip : : udp : : endpoint & senderEndpoint )
2022-02-28 01:15:14 +00:00
{
2022-04-29 00:41:06 +00:00
if ( len < 24 ) return ;
2022-03-05 02:51:40 +00:00
uint64_t connID ;
2022-02-28 01:15:14 +00:00
memcpy ( & connID , buf , 8 ) ;
2022-03-05 02:51:40 +00:00
connID ^ = CreateHeaderMask ( i2p : : context . GetSSU2IntroKey ( ) , buf + ( len - 24 ) ) ;
2022-04-07 14:57:57 +00:00
if ( ! m_LastSession | | m_LastSession - > GetConnID ( ) ! = connID )
2022-02-28 01:15:14 +00:00
{
2022-04-12 15:42:51 +00:00
if ( m_LastSession ) m_LastSession - > FlushData ( ) ;
2022-04-07 14:57:57 +00:00
auto it = m_Sessions . find ( connID ) ;
if ( it ! = m_Sessions . end ( ) )
m_LastSession = it - > second ;
else
m_LastSession = nullptr ;
}
if ( m_LastSession )
{
2022-05-11 21:48:25 +00:00
switch ( m_LastSession - > GetState ( ) )
{
case eSSU2SessionStateEstablished :
2022-10-09 17:24:43 +00:00
case eSSU2SessionStateSessionConfirmedSent :
2022-09-07 23:11:33 +00:00
m_LastSession - > ProcessData ( buf , len , senderEndpoint ) ;
2022-05-11 21:48:25 +00:00
break ;
2022-06-24 17:07:02 +00:00
case eSSU2SessionStateSessionCreatedSent :
2022-07-12 16:17:58 +00:00
if ( ! m_LastSession - > ProcessSessionConfirmed ( buf , len ) )
{
2022-07-26 23:56:30 +00:00
m_LastSession - > Done ( ) ;
2022-07-12 16:17:58 +00:00
m_LastSession = nullptr ;
2022-10-09 17:24:43 +00:00
}
2022-05-20 16:56:05 +00:00
break ;
2022-05-11 21:48:25 +00:00
case eSSU2SessionStateIntroduced :
2022-10-09 17:24:43 +00:00
if ( m_LastSession - > GetRemoteEndpoint ( ) . address ( ) . is_unspecified ( ) )
2022-06-28 03:03:27 +00:00
m_LastSession - > SetRemoteEndpoint ( senderEndpoint ) ;
2022-10-17 22:38:44 +00:00
if ( m_LastSession - > GetRemoteEndpoint ( ) . address ( ) = = senderEndpoint . address ( ) ) // port might be different
2022-06-28 03:03:27 +00:00
m_LastSession - > ProcessHolePunch ( buf , len ) ;
else
{
2022-10-17 22:38:44 +00:00
LogPrint ( eLogWarning , " SSU2: HolePunch address " , senderEndpoint . address ( ) ,
" doesn't match RelayResponse " , m_LastSession - > GetRemoteEndpoint ( ) . address ( ) ) ;
2022-07-26 23:56:30 +00:00
m_LastSession - > Done ( ) ;
2022-10-09 17:24:43 +00:00
m_LastSession = nullptr ;
}
2022-05-11 21:48:25 +00:00
break ;
case eSSU2SessionStatePeerTest :
m_LastSession - > SetRemoteEndpoint ( senderEndpoint ) ;
m_LastSession - > ProcessPeerTest ( buf , len ) ;
2022-05-20 16:56:05 +00:00
break ;
2022-07-08 23:06:09 +00:00
case eSSU2SessionStateClosing :
2022-09-07 23:11:33 +00:00
m_LastSession - > ProcessData ( buf , len , senderEndpoint ) ; // we might receive termintaion block
2022-12-07 02:40:33 +00:00
if ( m_LastSession & & m_LastSession - > GetState ( ) = = eSSU2SessionStateClosing )
2022-07-25 22:46:25 +00:00
m_LastSession - > RequestTermination ( eSSU2TerminationReasonIdleTimeout ) ; // send termination again
2022-10-09 17:24:43 +00:00
break ;
2022-12-13 18:41:58 +00:00
case eSSU2SessionStateClosingConfirmed :
2022-06-27 01:35:26 +00:00
case eSSU2SessionStateTerminated :
m_LastSession = nullptr ;
2022-10-09 17:24:43 +00:00
break ;
2022-05-11 21:48:25 +00:00
default :
LogPrint ( eLogWarning , " SSU2: Invalid session state " , ( int ) m_LastSession - > GetState ( ) ) ;
2022-05-20 16:56:05 +00:00
}
}
else
2022-03-01 02:46:00 +00:00
{
2022-03-20 19:10:18 +00:00
// check pending sessions if it's SessionCreated or Retry
2022-03-01 02:46:00 +00:00
auto it1 = m_PendingOutgoingSessions . find ( senderEndpoint ) ;
if ( it1 ! = m_PendingOutgoingSessions . end ( ) )
{
2022-06-29 12:09:43 +00:00
if ( it1 - > second - > GetState ( ) = = eSSU2SessionStateSessionRequestSent & &
it1 - > second - > ProcessSessionCreated ( buf , len ) )
2022-12-03 19:18:40 +00:00
{
2024-09-25 18:34:52 +00:00
std : : lock_guard < std : : mutex > l ( m_PendingOutgoingSessionsMutex ) ;
2022-10-09 17:24:43 +00:00
m_PendingOutgoingSessions . erase ( it1 ) ; // we are done with that endpoint
2022-12-13 18:41:58 +00:00
}
2022-03-21 16:56:02 +00:00
else
it1 - > second - > ProcessRetry ( buf , len ) ;
2022-03-01 02:46:00 +00:00
}
2024-01-11 20:39:42 +00:00
else if ( ! i2p : : transport : : transports . IsInReservedRange ( senderEndpoint . address ( ) ) & & senderEndpoint . port ( ) )
2022-03-01 02:46:00 +00:00
{
// assume new incoming session
2022-03-05 23:39:27 +00:00
auto session = std : : make_shared < SSU2Session > ( * this ) ;
2022-03-09 02:33:21 +00:00
session - > SetRemoteEndpoint ( senderEndpoint ) ;
2022-03-24 01:48:41 +00:00
session - > ProcessFirstIncomingMessage ( connID , buf , len ) ;
2022-05-20 16:56:05 +00:00
}
2022-12-14 20:44:31 +00:00
else
LogPrint ( eLogError , " SSU2: Incoming packet received from invalid endpoint " , senderEndpoint ) ;
2022-05-20 16:56:05 +00:00
}
}
2022-12-13 18:41:58 +00:00
2022-05-20 16:56:05 +00:00
void SSU2Server : : Send ( const uint8_t * header , size_t headerLen , const uint8_t * payload , size_t payloadLen ,
2022-03-26 20:35:07 +00:00
const boost : : asio : : ip : : udp : : endpoint & to )
{
2022-10-26 20:05:40 +00:00
if ( m_IsThroughProxy )
{
SendThroughProxy ( header , headerLen , nullptr , 0 , payload , payloadLen , to ) ;
return ;
2022-12-13 18:41:58 +00:00
}
2023-05-26 22:00:19 +00:00
2022-03-26 20:35:07 +00:00
std : : vector < boost : : asio : : const_buffer > bufs
{
boost : : asio : : buffer ( header , headerLen ) ,
boost : : asio : : buffer ( payload , payloadLen )
} ;
2023-05-26 22:00:19 +00:00
2022-03-26 20:35:07 +00:00
boost : : system : : error_code ec ;
if ( to . address ( ) . is_v6 ( ) )
2023-05-26 22:00:19 +00:00
{
if ( ! m_SocketV6 . is_open ( ) ) return ;
2022-03-26 20:35:07 +00:00
m_SocketV6 . send_to ( bufs , to , 0 , ec ) ;
2023-05-26 22:00:19 +00:00
}
2022-05-20 16:56:05 +00:00
else
2023-05-26 22:00:19 +00:00
{
if ( ! m_SocketV4 . is_open ( ) ) return ;
2022-04-05 20:14:13 +00:00
m_SocketV4 . send_to ( bufs , to , 0 , ec ) ;
2023-05-26 22:00:19 +00:00
}
2022-05-20 16:56:05 +00:00
if ( ! ec )
2022-04-22 19:03:49 +00:00
i2p : : transport : : transports . UpdateSentBytes ( headerLen + payloadLen ) ;
else
2024-02-29 14:12:51 +00:00
{
LogPrint ( ec = = boost : : asio : : error : : would_block ? eLogInfo : eLogError ,
" SSU2: Send exception: " , ec . message ( ) , " to " , to ) ;
}
2022-05-20 16:56:05 +00:00
}
void SSU2Server : : Send ( const uint8_t * header , size_t headerLen , const uint8_t * headerX , size_t headerXLen ,
2022-03-01 02:46:00 +00:00
const uint8_t * payload , size_t payloadLen , const boost : : asio : : ip : : udp : : endpoint & to )
{
2022-10-26 20:05:40 +00:00
if ( m_IsThroughProxy )
{
SendThroughProxy ( header , headerLen , headerX , headerXLen , payload , payloadLen , to ) ;
return ;
}
2023-05-26 22:00:19 +00:00
2022-03-01 02:46:00 +00:00
std : : vector < boost : : asio : : const_buffer > bufs
{
boost : : asio : : buffer ( header , headerLen ) ,
boost : : asio : : buffer ( headerX , headerXLen ) ,
boost : : asio : : buffer ( payload , payloadLen )
} ;
2023-05-26 22:00:19 +00:00
2022-03-01 02:46:00 +00:00
boost : : system : : error_code ec ;
2022-03-18 19:32:32 +00:00
if ( to . address ( ) . is_v6 ( ) )
2023-05-26 22:00:19 +00:00
{
if ( ! m_SocketV6 . is_open ( ) ) return ;
2022-03-18 19:32:32 +00:00
m_SocketV6 . send_to ( bufs , to , 0 , ec ) ;
2023-05-26 22:00:19 +00:00
}
2022-05-20 16:56:05 +00:00
else
2023-05-26 22:00:19 +00:00
{
if ( ! m_SocketV4 . is_open ( ) ) return ;
2022-04-05 20:14:13 +00:00
m_SocketV4 . send_to ( bufs , to , 0 , ec ) ;
2023-05-26 22:00:19 +00:00
}
2022-05-20 16:56:05 +00:00
2022-04-22 19:03:49 +00:00
if ( ! ec )
i2p : : transport : : transports . UpdateSentBytes ( headerLen + headerXLen + payloadLen ) ;
2022-05-20 16:56:05 +00:00
else
2024-02-29 14:12:51 +00:00
{
LogPrint ( ec = = boost : : asio : : error : : would_block ? eLogInfo : eLogError ,
" SSU2: Send exception: " , ec . message ( ) , " to " , to ) ;
}
2022-05-04 17:58:06 +00:00
}
2022-05-20 16:56:05 +00:00
2022-03-17 01:11:48 +00:00
bool SSU2Server : : CreateSession ( std : : shared_ptr < const i2p : : data : : RouterInfo > router ,
2022-06-12 01:26:23 +00:00
std : : shared_ptr < const i2p : : data : : RouterInfo : : Address > address , bool peerTest )
2022-03-17 01:11:48 +00:00
{
if ( router & & address )
2022-04-28 15:43:33 +00:00
{
2022-07-23 18:32:16 +00:00
// check if no session
2024-09-25 18:34:52 +00:00
auto existingSession = FindSession ( router - > GetIdentHash ( ) ) ;
if ( existingSession )
2022-07-23 18:32:16 +00:00
{
// session with router found, trying to send peer test if requested
2024-09-25 18:34:52 +00:00
if ( peerTest & & existingSession - > IsEstablished ( ) )
GetService ( ) . post ( [ existingSession ] ( ) { existingSession - > SendPeerTest ( ) ; } ) ;
2022-07-23 18:32:16 +00:00
return false ;
2022-10-09 17:24:43 +00:00
}
2022-07-23 18:32:16 +00:00
// check is no pending session
2022-06-22 15:59:29 +00:00
bool isValidEndpoint = ! address - > host . is_unspecified ( ) & & address - > port ;
if ( isValidEndpoint )
2022-10-09 17:24:43 +00:00
{
2024-01-11 20:39:42 +00:00
if ( i2p : : transport : : transports . IsInReservedRange ( address - > host ) ) return false ;
2022-06-22 15:59:29 +00:00
auto s = FindPendingOutgoingSession ( boost : : asio : : ip : : udp : : endpoint ( address - > host , address - > port ) ) ;
if ( s )
2022-10-09 17:24:43 +00:00
{
2022-06-22 15:59:29 +00:00
if ( peerTest )
{
// if peer test requested add it to the list for pending session
auto onEstablished = s - > GetOnEstablished ( ) ;
if ( onEstablished )
2022-10-09 17:24:43 +00:00
s - > SetOnEstablished ( [ s , onEstablished ] ( )
{
onEstablished ( ) ;
s - > SendPeerTest ( ) ;
} ) ;
else
2022-06-22 15:59:29 +00:00
s - > SetOnEstablished ( [ s ] ( ) { s - > SendPeerTest ( ) ; } ) ;
2022-10-09 17:24:43 +00:00
}
2022-06-22 15:59:29 +00:00
return false ;
2022-10-09 17:24:43 +00:00
}
}
2022-06-12 01:26:23 +00:00
auto session = std : : make_shared < SSU2Session > ( * this , router , address ) ;
if ( peerTest )
session - > SetOnEstablished ( [ session ] ( ) { session - > SendPeerTest ( ) ; } ) ;
2022-05-20 16:56:05 +00:00
if ( address - > UsesIntroducer ( ) )
2022-06-12 01:26:23 +00:00
GetService ( ) . post ( std : : bind ( & SSU2Server : : ConnectThroughIntroducer , this , session ) ) ;
2022-10-09 17:24:43 +00:00
else if ( isValidEndpoint ) // we can't connect without endpoint
2022-06-12 01:26:23 +00:00
GetService ( ) . post ( [ session ] ( ) { session - > Connect ( ) ; } ) ;
2022-06-22 15:59:29 +00:00
else
return false ;
2022-05-20 16:56:05 +00:00
}
2022-03-17 01:11:48 +00:00
else
return false ;
return true ;
2022-05-20 16:56:05 +00:00
}
2022-03-19 00:21:31 +00:00
2022-06-12 01:26:23 +00:00
void SSU2Server : : ConnectThroughIntroducer ( std : : shared_ptr < SSU2Session > session )
2022-05-01 14:33:25 +00:00
{
2022-06-12 01:26:23 +00:00
if ( ! session ) return ;
auto address = session - > GetAddress ( ) ;
if ( ! address ) return ;
2022-06-19 20:40:03 +00:00
session - > WaitForIntroduction ( ) ;
2024-06-06 18:19:30 +00:00
auto ts = i2p : : util : : GetSecondsSinceEpoch ( ) ;
std : : vector < int > indices ; int i = 0 ;
2022-05-01 14:33:25 +00:00
// try to find existing session first
for ( auto & it : address - > ssu - > introducers )
{
2024-06-06 18:19:30 +00:00
if ( it . iTag & & ts < it . iExp )
2022-05-01 14:33:25 +00:00
{
2024-09-25 18:34:52 +00:00
auto s = FindSession ( it . iH ) ;
if ( s )
2024-06-06 18:19:30 +00:00
{
2024-09-25 18:34:52 +00:00
auto addr = s - > GetAddress ( ) ;
if ( addr & & addr - > IsIntroducer ( ) )
2024-06-27 22:02:17 +00:00
{
2024-09-25 18:34:52 +00:00
s - > Introduce ( session , it . iTag ) ;
return ;
}
2024-06-06 18:19:30 +00:00
}
else
indices . push_back ( i ) ;
}
i + + ;
2022-05-20 16:56:05 +00:00
}
2022-05-01 14:33:25 +00:00
// we have to start a new session to an introducer
2024-06-07 17:29:51 +00:00
std : : vector < i2p : : data : : IdentHash > newRouters ;
2022-05-01 14:33:25 +00:00
std : : shared_ptr < i2p : : data : : RouterInfo > r ;
2024-06-09 22:38:13 +00:00
std : : shared_ptr < const i2p : : data : : RouterInfo : : Address > addr ;
2022-05-01 14:33:25 +00:00
uint32_t relayTag = 0 ;
2024-06-06 18:19:30 +00:00
if ( ! indices . empty ( ) )
2022-10-09 17:24:43 +00:00
{
2022-11-25 20:37:52 +00:00
if ( indices . size ( ) > 1 )
2024-06-08 02:10:52 +00:00
std : : shuffle ( indices . begin ( ) , indices . end ( ) , m_Rng ) ;
2022-07-30 20:31:44 +00:00
2024-06-06 20:13:30 +00:00
for ( auto ind : indices )
2022-07-30 20:31:44 +00:00
{
2024-06-06 20:13:30 +00:00
const auto & introducer = address - > ssu - > introducers [ ind ] ;
2024-06-06 18:19:30 +00:00
// introducer is not expired, because in indices
r = i2p : : data : : netdb . FindRouter ( introducer . iH ) ;
2024-06-07 17:29:51 +00:00
if ( r )
{
2024-06-14 22:05:01 +00:00
if ( r - > IsPublishedOn ( i2p : : context . GetRouterInfo ( ) . GetCompatibleTransports ( false ) & // outgoing
( i2p : : data : : RouterInfo : : eSSU2V4 | i2p : : data : : RouterInfo : : eSSU2V6 ) ) )
2024-06-07 17:29:51 +00:00
{
relayTag = introducer . iTag ;
2024-06-09 22:38:13 +00:00
addr = address - > IsV6 ( ) ? r - > GetSSU2V6Address ( ) : r - > GetSSU2V4Address ( ) ;
2024-06-27 22:02:17 +00:00
if ( addr & & addr - > IsIntroducer ( ) & & ! addr - > host . is_unspecified ( ) & & addr - > port & &
2024-06-09 22:38:13 +00:00
! i2p : : transport : : transports . IsInReservedRange ( addr - > host ) )
break ;
else
{
2024-06-27 22:02:17 +00:00
// address is invalid or not intrudcer, try another SSU2 address if exists
2024-06-14 22:05:01 +00:00
if ( address - > IsV4 ( ) )
{
if ( i2p : : context . SupportsV6 ( ) )
addr = r - > GetSSU2V6Address ( ) ;
}
else
{
if ( i2p : : context . SupportsV4 ( ) )
addr = r - > GetSSU2V4Address ( ) ;
}
2024-06-27 22:02:17 +00:00
if ( addr & & addr - > IsIntroducer ( ) & & ! addr - > host . is_unspecified ( ) & & addr - > port & &
2024-06-14 22:05:01 +00:00
! i2p : : transport : : transports . IsInReservedRange ( addr - > host ) )
break ;
else
{
// all addresses are invalid, try next introducer
relayTag = 0 ;
addr = nullptr ;
2024-06-26 12:53:04 +00:00
r = nullptr ;
2024-06-14 22:05:01 +00:00
}
2024-06-09 22:38:13 +00:00
}
2024-06-07 17:29:51 +00:00
}
2024-06-26 12:53:04 +00:00
else
r = nullptr ;
2024-06-07 17:29:51 +00:00
}
2024-06-07 17:35:37 +00:00
else if ( ! i2p : : data : : IsRouterBanned ( introducer . iH ) )
2024-06-07 17:29:51 +00:00
newRouters . push_back ( introducer . iH ) ;
2022-07-30 20:31:44 +00:00
}
2022-10-09 17:24:43 +00:00
}
2022-05-01 14:33:25 +00:00
if ( r )
{
2024-06-09 22:38:13 +00:00
if ( relayTag & & addr )
2022-05-20 16:56:05 +00:00
{
// introducer and tag found connect to it through SSU2
2024-06-09 22:38:13 +00:00
auto s = FindPendingOutgoingSession ( boost : : asio : : ip : : udp : : endpoint ( addr - > host , addr - > port ) ) ;
if ( ! s )
2022-05-01 14:33:25 +00:00
{
2024-06-09 22:38:13 +00:00
s = std : : make_shared < SSU2Session > ( * this , r , addr ) ;
s - > SetOnEstablished ( [ session , s , relayTag ] ( ) { s - > Introduce ( session , relayTag ) ; } ) ;
s - > Connect ( ) ;
}
else
{
auto onEstablished = s - > GetOnEstablished ( ) ;
if ( onEstablished )
s - > SetOnEstablished ( [ session , s , relayTag , onEstablished ] ( )
{
onEstablished ( ) ;
s - > Introduce ( session , relayTag ) ;
} ) ;
else
s - > SetOnEstablished ( [ session , s , relayTag ] ( ) { s - > Introduce ( session , relayTag ) ; } ) ;
2022-05-20 16:56:05 +00:00
}
}
2024-06-09 22:38:13 +00:00
else
session - > Done ( ) ;
2022-05-01 14:33:25 +00:00
}
else
{
// introducers not found, try to request them
2024-06-07 17:29:51 +00:00
for ( auto & it : newRouters )
i2p : : data : : netdb . RequestDestination ( it ) ;
2024-06-09 22:38:13 +00:00
session - > Done ( ) ; // don't wait for connect timeout
2022-05-20 16:56:05 +00:00
}
}
2022-06-02 01:51:02 +00:00
bool SSU2Server : : StartPeerTest ( std : : shared_ptr < const i2p : : data : : RouterInfo > router , bool v4 )
{
if ( ! router ) return false ;
auto addr = v4 ? router - > GetSSU2V4Address ( ) : router - > GetSSU2V6Address ( ) ;
if ( ! addr ) return false ;
2024-09-25 18:34:52 +00:00
auto session = FindSession ( router - > GetIdentHash ( ) ) ;
2024-09-25 00:03:15 +00:00
if ( session )
2022-06-02 01:51:02 +00:00
{
2024-09-25 00:03:15 +00:00
auto remoteAddr = session - > GetAddress ( ) ;
2024-03-02 23:02:55 +00:00
if ( ! remoteAddr | | ! remoteAddr - > IsPeerTesting ( ) | |
2024-09-25 00:03:15 +00:00
( v4 & & ! remoteAddr - > IsV4 ( ) ) | | ( ! v4 & & ! remoteAddr - > IsV6 ( ) ) ) return false ;
if ( session - > IsEstablished ( ) )
GetService ( ) . post ( [ session ] ( ) { session - > SendPeerTest ( ) ; } ) ;
2022-10-09 17:24:43 +00:00
else
2024-09-25 00:03:15 +00:00
session - > SetOnEstablished ( [ session ] ( ) { session - > SendPeerTest ( ) ; } ) ;
2022-10-09 17:24:43 +00:00
return true ;
2022-07-29 16:48:23 +00:00
}
2022-10-09 17:24:43 +00:00
else
2022-07-29 16:48:23 +00:00
CreateSession ( router , addr , true ) ;
2022-06-02 01:51:02 +00:00
return true ;
2022-10-09 17:24:43 +00:00
}
2022-03-19 00:21:31 +00:00
void SSU2Server : : ScheduleTermination ( )
{
2024-08-27 00:57:28 +00:00
m_TerminationTimer . expires_from_now ( boost : : posix_time : : seconds (
SSU2_TERMINATION_CHECK_TIMEOUT + m_Rng ( ) % SSU2_TERMINATION_CHECK_TIMEOUT_VARIANCE ) ) ;
2022-03-19 00:21:31 +00:00
m_TerminationTimer . async_wait ( std : : bind ( & SSU2Server : : HandleTerminationTimer ,
this , std : : placeholders : : _1 ) ) ;
}
void SSU2Server : : HandleTerminationTimer ( const boost : : system : : error_code & ecode )
{
if ( ecode ! = boost : : asio : : error : : operation_aborted )
{
auto ts = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2024-09-25 18:34:52 +00:00
2022-03-19 00:21:31 +00:00
{
2024-09-25 18:34:52 +00:00
std : : lock_guard < std : : mutex > l ( m_PendingOutgoingSessionsMutex ) ;
for ( auto it = m_PendingOutgoingSessions . begin ( ) ; it ! = m_PendingOutgoingSessions . end ( ) ; )
2022-03-19 00:21:31 +00:00
{
2024-09-25 18:34:52 +00:00
if ( it - > second - > IsTerminationTimeoutExpired ( ts ) )
{
//it->second->Terminate ();
it = m_PendingOutgoingSessions . erase ( it ) ;
}
else
it + + ;
2022-03-19 00:21:31 +00:00
}
2024-09-25 18:34:52 +00:00
}
2022-03-19 00:21:31 +00:00
2022-07-08 23:06:09 +00:00
for ( auto it : m_Sessions )
2022-03-19 00:21:31 +00:00
{
2022-07-08 23:06:09 +00:00
auto state = it . second - > GetState ( ) ;
if ( state = = eSSU2SessionStateTerminated | | state = = eSSU2SessionStateClosing )
2022-07-26 23:56:30 +00:00
it . second - > Done ( ) ;
2022-07-08 23:06:09 +00:00
else if ( it . second - > IsTerminationTimeoutExpired ( ts ) )
2022-03-19 00:21:31 +00:00
{
2022-07-08 23:06:09 +00:00
if ( it . second - > IsEstablished ( ) )
2022-07-09 21:05:23 +00:00
it . second - > RequestTermination ( eSSU2TerminationReasonIdleTimeout ) ;
2022-07-08 23:06:09 +00:00
else
2022-07-26 23:56:30 +00:00
it . second - > Done ( ) ;
2022-03-19 00:21:31 +00:00
}
else
2022-07-08 23:06:09 +00:00
it . second - > CleanUp ( ts ) ;
2022-03-19 00:21:31 +00:00
}
2022-03-23 23:13:44 +00:00
2023-01-14 22:05:09 +00:00
ScheduleTermination ( ) ;
}
}
void SSU2Server : : ScheduleCleanup ( )
{
m_CleanupTimer . expires_from_now ( boost : : posix_time : : seconds ( SSU2_CLEANUP_INTERVAL ) ) ;
m_CleanupTimer . async_wait ( std : : bind ( & SSU2Server : : HandleCleanupTimer ,
this , std : : placeholders : : _1 ) ) ;
}
2023-02-11 06:29:37 +00:00
2023-01-14 22:05:09 +00:00
void SSU2Server : : HandleCleanupTimer ( const boost : : system : : error_code & ecode )
{
if ( ecode ! = boost : : asio : : error : : operation_aborted )
{
auto ts = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2022-07-24 20:44:02 +00:00
for ( auto it = m_Relays . begin ( ) ; it ! = m_Relays . begin ( ) ; )
{
2024-09-26 22:38:17 +00:00
if ( it - > second . expired ( ) )
2022-07-24 20:44:02 +00:00
it = m_Relays . erase ( it ) ;
else
it + + ;
2022-10-09 17:24:43 +00:00
}
2024-09-29 02:05:25 +00:00
for ( auto it = m_PeerTests . begin ( ) ; it ! = m_PeerTests . end ( ) ; )
{
if ( ts > it - > second . second + SSU2_PEER_TEST_EXPIRATION_TIMEOUT | | it - > second . first . expired ( ) )
{
LogPrint ( eLogInfo , " SSU2: Peer test nonce " , it - > first , " was not responded in " , SSU2_PEER_TEST_EXPIRATION_TIMEOUT , " seconds or session invalid. Deleted " ) ;
it = m_PeerTests . erase ( it ) ;
}
else
it + + ;
}
2022-03-23 23:13:44 +00:00
for ( auto it = m_IncomingTokens . begin ( ) ; it ! = m_IncomingTokens . end ( ) ; )
{
if ( ts > it - > second . second )
2022-05-20 16:56:05 +00:00
it = m_IncomingTokens . erase ( it ) ;
2022-03-23 23:13:44 +00:00
else
it + + ;
}
for ( auto it = m_OutgoingTokens . begin ( ) ; it ! = m_OutgoingTokens . end ( ) ; )
{
if ( ts > it - > second . second )
2022-05-20 16:56:05 +00:00
it = m_OutgoingTokens . erase ( it ) ;
2022-03-23 23:13:44 +00:00
else
it + + ;
}
2023-02-11 06:29:37 +00:00
2024-09-18 01:49:23 +00:00
for ( auto it = m_ConnectedRecently . begin ( ) ; it ! = m_ConnectedRecently . end ( ) ; )
{
if ( ts > it - > second + SSU2_HOLE_PUNCH_EXPIRATION )
it = m_ConnectedRecently . erase ( it ) ;
else
it + + ;
}
2024-09-23 18:16:24 +00:00
for ( auto it = m_RequestedPeerTests . begin ( ) ; it ! = m_RequestedPeerTests . end ( ) ; )
{
if ( ts > it - > second . second + SSU2_PEER_TEST_EXPIRATION_TIMEOUT )
it = m_RequestedPeerTests . erase ( it ) ;
else
it + + ;
}
2024-09-25 18:34:52 +00:00
{
std : : lock_guard < std : : mutex > l ( m_SessionsByRouterHashMutex ) ;
for ( auto it = m_SessionsByRouterHash . begin ( ) ; it ! = m_SessionsByRouterHash . begin ( ) ; )
{
if ( it - > second . expired ( ) )
it = m_SessionsByRouterHash . erase ( it ) ;
else
it + + ;
}
}
2024-09-18 01:49:23 +00:00
2022-07-24 20:44:02 +00:00
m_PacketsPool . CleanUpMt ( ) ;
2024-09-28 13:49:45 +00:00
m_PacketsArrayPool . CleanUpMt ( ) ;
2022-08-04 22:13:44 +00:00
m_SentPacketsPool . CleanUp ( ) ;
2023-01-18 02:32:36 +00:00
m_IncompleteMessagesPool . CleanUp ( ) ;
2023-01-14 22:05:09 +00:00
m_FragmentsPool . CleanUp ( ) ;
ScheduleCleanup ( ) ;
2023-02-11 06:29:37 +00:00
}
}
2022-09-03 19:38:52 +00:00
void SSU2Server : : ScheduleResend ( bool more )
2022-03-31 19:35:55 +00:00
{
2024-05-15 15:57:14 +00:00
m_ResendTimer . expires_from_now ( boost : : posix_time : : milliseconds ( more ?
2024-06-08 02:10:52 +00:00
( SSU2_RESEND_CHECK_MORE_TIMEOUT + m_Rng ( ) % SSU2_RESEND_CHECK_MORE_TIMEOUT_VARIANCE ) :
( SSU2_RESEND_CHECK_TIMEOUT + m_Rng ( ) % SSU2_RESEND_CHECK_TIMEOUT_VARIANCE ) ) ) ;
2022-03-31 19:35:55 +00:00
m_ResendTimer . async_wait ( std : : bind ( & SSU2Server : : HandleResendTimer ,
this , std : : placeholders : : _1 ) ) ;
}
2022-05-20 16:56:05 +00:00
2022-03-31 19:35:55 +00:00
void SSU2Server : : HandleResendTimer ( const boost : : system : : error_code & ecode )
{
if ( ecode ! = boost : : asio : : error : : operation_aborted )
{
2022-09-03 19:38:52 +00:00
size_t resentPacketsNum = 0 ;
2022-07-28 23:30:08 +00:00
auto ts = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
2022-03-31 19:35:55 +00:00
for ( auto it : m_Sessions )
2022-10-09 17:24:43 +00:00
{
2024-05-14 22:17:47 +00:00
if ( ts > = it . second - > GetLastResendTime ( ) + SSU2_RESEND_CHECK_TIMEOUT )
resentPacketsNum + = it . second - > Resend ( ts ) ;
2022-09-03 19:38:52 +00:00
if ( resentPacketsNum > SSU2_MAX_RESEND_PACKETS ) break ;
2022-10-09 17:24:43 +00:00
}
2022-06-24 17:07:02 +00:00
for ( auto it : m_PendingOutgoingSessions )
it . second - > Resend ( ts ) ;
2022-09-03 19:38:52 +00:00
ScheduleResend ( resentPacketsNum > SSU2_MAX_RESEND_PACKETS ) ;
2022-05-20 16:56:05 +00:00
}
}
2022-03-23 23:13:44 +00:00
void SSU2Server : : UpdateOutgoingToken ( const boost : : asio : : ip : : udp : : endpoint & ep , uint64_t token , uint32_t exp )
{
m_OutgoingTokens [ ep ] = { token , exp } ;
2022-05-20 16:56:05 +00:00
}
2022-03-23 23:13:44 +00:00
2022-12-03 20:05:27 +00:00
uint64_t SSU2Server : : FindOutgoingToken ( const boost : : asio : : ip : : udp : : endpoint & ep )
2022-03-23 23:13:44 +00:00
{
auto it = m_OutgoingTokens . find ( ep ) ;
if ( it ! = m_OutgoingTokens . end ( ) )
2022-06-19 12:52:47 +00:00
{
if ( i2p : : util : : GetSecondsSinceEpoch ( ) + SSU2_TOKEN_EXPIRATION_THRESHOLD > it - > second . second )
2022-12-13 18:41:58 +00:00
{
2022-12-03 20:05:27 +00:00
// token expired
m_OutgoingTokens . erase ( it ) ;
2023-01-03 18:25:19 +00:00
return 0 ;
2022-12-13 18:41:58 +00:00
}
2022-03-23 23:13:44 +00:00
return it - > second . first ;
2022-10-09 17:24:43 +00:00
}
2022-03-23 23:13:44 +00:00
return 0 ;
2022-05-20 16:56:05 +00:00
}
2022-03-24 01:48:41 +00:00
2022-06-17 02:37:33 +00:00
uint64_t SSU2Server : : GetIncomingToken ( const boost : : asio : : ip : : udp : : endpoint & ep )
2022-03-24 01:48:41 +00:00
{
2022-12-03 20:05:27 +00:00
auto ts = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2022-03-24 01:48:41 +00:00
auto it = m_IncomingTokens . find ( ep ) ;
if ( it ! = m_IncomingTokens . end ( ) )
2022-12-03 20:05:27 +00:00
{
if ( ts + SSU2_TOKEN_EXPIRATION_THRESHOLD < = it - > second . second )
return it - > second . first ;
else // token expired
m_IncomingTokens . erase ( it ) ;
2022-12-13 18:41:58 +00:00
}
2022-03-24 01:48:41 +00:00
uint64_t token ;
RAND_bytes ( ( uint8_t * ) & token , 8 ) ;
2023-03-02 10:14:49 +00:00
m_IncomingTokens . emplace ( ep , std : : make_pair ( token , uint32_t ( ts + SSU2_TOKEN_EXPIRATION_TIMEOUT ) ) ) ;
2022-06-17 02:37:33 +00:00
return token ;
}
std : : pair < uint64_t , uint32_t > SSU2Server : : NewIncomingToken ( const boost : : asio : : ip : : udp : : endpoint & ep )
{
m_IncomingTokens . erase ( ep ) ; // drop previous
uint64_t token ;
RAND_bytes ( ( uint8_t * ) & token , 8 ) ;
2023-03-02 10:14:49 +00:00
auto ret = std : : make_pair ( token , uint32_t ( i2p : : util : : GetSecondsSinceEpoch ( ) + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT ) ) ;
2022-06-13 21:55:41 +00:00
m_IncomingTokens . emplace ( ep , ret ) ;
return ret ;
2022-10-09 17:24:43 +00:00
}
2022-07-10 21:13:25 +00:00
2024-06-08 20:08:32 +00:00
std : : vector < std : : shared_ptr < SSU2Session > > SSU2Server : : FindIntroducers ( int maxNumIntroducers ,
2024-10-03 22:44:09 +00:00
bool v4 , const std : : unordered_set < i2p : : data : : IdentHash > & excluded )
2022-07-10 21:13:25 +00:00
{
2024-06-08 20:08:32 +00:00
std : : vector < std : : shared_ptr < SSU2Session > > ret ;
2024-10-03 22:44:09 +00:00
if ( maxNumIntroducers < = 0 | | m_Sessions . empty ( ) ) return ret ;
std : : vector < std : : shared_ptr < SSU2Session > > eligible ;
eligible . reserve ( m_Sessions . size ( ) / 2 ) ;
auto ts = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2022-07-10 21:13:25 +00:00
for ( const auto & s : m_Sessions )
{
2022-10-09 17:24:43 +00:00
if ( s . second - > IsEstablished ( ) & & ( s . second - > GetRelayTag ( ) & & s . second - > IsOutgoing ( ) ) & &
2024-10-03 22:44:09 +00:00
ts < s . second - > GetCreationTime ( ) + SSU2_TO_INTRODUCER_SESSION_DURATION / 2 & &
2022-07-10 21:13:25 +00:00
! excluded . count ( s . second - > GetRemoteIdentity ( ) - > GetIdentHash ( ) ) & &
2022-07-11 08:16:07 +00:00
( ( v4 & & ( s . second - > GetRemoteTransports ( ) & i2p : : data : : RouterInfo : : eSSU2V4 ) ) | |
2022-10-09 17:24:43 +00:00
( ! v4 & & ( s . second - > GetRemoteTransports ( ) & i2p : : data : : RouterInfo : : eSSU2V6 ) ) ) )
2024-10-03 22:44:09 +00:00
eligible . push_back ( s . second ) ;
2022-10-09 17:24:43 +00:00
}
2024-10-03 22:44:09 +00:00
std : : sample ( eligible . begin ( ) , eligible . end ( ) , std : : back_inserter ( ret ) , maxNumIntroducers , m_Rng ) ;
2022-07-10 21:13:25 +00:00
return ret ;
2022-10-09 17:24:43 +00:00
}
2022-07-10 21:13:25 +00:00
void SSU2Server : : UpdateIntroducers ( bool v4 )
{
2022-07-19 22:38:58 +00:00
uint32_t ts = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2024-09-27 17:32:20 +00:00
std : : list < std : : pair < i2p : : data : : IdentHash , uint32_t > > newList , impliedList ;
2022-07-10 21:13:25 +00:00
auto & introducers = v4 ? m_Introducers : m_IntroducersV6 ;
2024-05-15 17:31:31 +00:00
std : : unordered_set < i2p : : data : : IdentHash > excluded ;
2024-09-27 17:32:20 +00:00
for ( const auto & [ ident , tag ] : introducers )
2022-07-10 21:13:25 +00:00
{
2024-09-27 17:32:20 +00:00
std : : shared_ptr < SSU2Session > session = FindSession ( ident ) ;
2024-09-25 18:34:52 +00:00
if ( session )
2024-09-27 17:32:20 +00:00
excluded . insert ( ident ) ;
2024-09-25 18:34:52 +00:00
if ( session )
2024-06-13 22:10:45 +00:00
{
2024-09-25 18:34:52 +00:00
if ( session - > IsEstablished ( ) & & session - > GetRelayTag ( ) & & session - > IsOutgoing ( ) & & // still session with introducer?
ts < session - > GetCreationTime ( ) + SSU2_TO_INTRODUCER_SESSION_EXPIRATION )
2023-07-22 19:03:03 +00:00
{
2024-09-25 18:34:52 +00:00
session - > SendKeepAlive ( ) ;
2024-10-02 01:26:54 +00:00
if ( ts < session - > GetCreationTime ( ) + SSU2_TO_INTRODUCER_SESSION_DURATION )
2024-09-27 17:32:20 +00:00
{
newList . push_back ( { ident , session - > GetRelayTag ( ) } ) ;
if ( tag ! = session - > GetRelayTag ( ) )
{
LogPrint ( eLogDebug , " SSU2: Introducer session to " , session - > GetIdentHashBase64 ( ) , " was replaced. iTag " , tag , " -> " , session - > GetRelayTag ( ) ) ;
i2p : : context . UpdateSSU2Introducer ( ident , v4 , session - > GetRelayTag ( ) ,
session - > GetCreationTime ( ) + SSU2_TO_INTRODUCER_SESSION_EXPIRATION ) ;
}
}
2024-09-25 18:34:52 +00:00
else
{
2024-09-27 17:32:20 +00:00
impliedList . push_back ( { ident , session - > GetRelayTag ( ) } ) ; // keep in introducers list, but not publish
2024-09-25 18:34:52 +00:00
session = nullptr ;
2024-09-27 17:32:20 +00:00
}
2024-09-25 18:34:52 +00:00
}
else
session = nullptr ;
2024-06-13 22:10:45 +00:00
}
2022-07-21 23:38:18 +00:00
if ( ! session )
2024-09-27 17:32:20 +00:00
i2p : : context . RemoveSSU2Introducer ( ident , v4 ) ;
2022-10-09 17:24:43 +00:00
}
2024-10-04 12:26:32 +00:00
int numOldSessions = 0 ;
2022-07-10 21:13:25 +00:00
if ( newList . size ( ) < SSU2_MAX_NUM_INTRODUCERS )
{
2022-07-19 22:38:58 +00:00
auto sessions = FindIntroducers ( SSU2_MAX_NUM_INTRODUCERS - newList . size ( ) , v4 , excluded ) ;
2024-10-03 22:44:09 +00:00
if ( sessions . empty ( ) & & ! impliedList . empty ( ) )
2022-07-19 22:38:58 +00:00
{
LogPrint ( eLogDebug , " SSU2: No new introducers found. Trying to reuse existing " ) ;
2024-10-03 22:44:09 +00:00
for ( const auto & it : impliedList )
2022-10-09 17:24:43 +00:00
{
2024-09-28 20:20:59 +00:00
auto session = FindSession ( it . first ) ;
2024-10-03 22:44:09 +00:00
if ( session )
2022-07-23 18:32:16 +00:00
{
2024-09-27 17:32:20 +00:00
if ( std : : find_if ( newList . begin ( ) , newList . end ( ) ,
2024-09-28 20:20:59 +00:00
[ & ident = it . first ] ( const auto & s ) { return ident = = s . first ; } ) = = newList . end ( ) )
2024-10-04 12:26:32 +00:00
{
2024-09-25 18:34:52 +00:00
sessions . push_back ( session ) ;
2024-10-04 12:26:32 +00:00
numOldSessions + + ;
}
2022-10-09 17:24:43 +00:00
}
}
2024-10-03 22:44:09 +00:00
impliedList . clear ( ) ;
2022-10-09 17:24:43 +00:00
}
2022-07-10 21:13:25 +00:00
for ( const auto & it : sessions )
{
2024-10-03 22:44:09 +00:00
uint32_t tag = it - > GetRelayTag ( ) ;
2023-07-22 14:13:05 +00:00
uint32_t exp = it - > GetCreationTime ( ) + SSU2_TO_INTRODUCER_SESSION_EXPIRATION ;
2024-10-03 22:44:09 +00:00
if ( ! tag & & ts > = exp )
continue ; // don't publish expired introducer
2022-07-20 20:01:08 +00:00
i2p : : data : : RouterInfo : : Introducer introducer ;
2023-08-05 12:55:06 +00:00
introducer . iTag = tag ;
2022-11-23 18:44:03 +00:00
introducer . iH = it - > GetRemoteIdentity ( ) - > GetIdentHash ( ) ;
2023-07-22 14:13:05 +00:00
introducer . iExp = exp ;
2022-07-20 20:01:08 +00:00
excluded . insert ( it - > GetRemoteIdentity ( ) - > GetIdentHash ( ) ) ;
2022-07-21 23:38:18 +00:00
if ( i2p : : context . AddSSU2Introducer ( introducer , v4 ) )
2022-07-20 20:01:08 +00:00
{
2022-10-09 17:24:43 +00:00
LogPrint ( eLogDebug , " SSU2: Introducer added " , it - > GetRelayTag ( ) , " at " ,
2022-07-21 23:38:18 +00:00
i2p : : data : : GetIdentHashAbbreviation ( it - > GetRemoteIdentity ( ) - > GetIdentHash ( ) ) ) ;
2024-09-27 17:32:20 +00:00
newList . push_back ( { it - > GetRemoteIdentity ( ) - > GetIdentHash ( ) , tag } ) ;
2024-10-01 22:21:07 +00:00
it - > SendKeepAlive ( ) ;
2022-07-20 20:01:08 +00:00
if ( newList . size ( ) > = SSU2_MAX_NUM_INTRODUCERS ) break ;
2022-10-09 17:24:43 +00:00
}
}
}
2022-07-10 21:13:25 +00:00
introducers = newList ;
2022-07-19 22:38:58 +00:00
2024-10-04 12:26:32 +00:00
if ( introducers . size ( ) < SSU2_MAX_NUM_INTRODUCERS | | numOldSessions )
2022-07-19 22:38:58 +00:00
{
2024-10-04 12:26:32 +00:00
// we need to create more sessions with relay tag
// exclude all existing sessions
excluded . clear ( ) ;
2024-10-05 00:44:58 +00:00
{
std : : lock_guard < std : : mutex > l ( m_SessionsByRouterHashMutex ) ;
for ( const auto & [ ident , s ] : m_SessionsByRouterHash )
excluded . insert ( ident ) ;
}
2024-10-04 12:26:32 +00:00
// sesssion about to expire are not counted
for ( auto i = introducers . size ( ) ; i < SSU2_MAX_NUM_INTRODUCERS + numOldSessions ; i + + )
2022-07-19 22:38:58 +00:00
{
auto introducer = i2p : : data : : netdb . GetRandomSSU2Introducer ( v4 , excluded ) ;
if ( introducer )
{
auto address = v4 ? introducer - > GetSSU2V4Address ( ) : introducer - > GetSSU2V6Address ( ) ;
if ( address )
{
CreateSession ( introducer , address ) ;
excluded . insert ( introducer - > GetIdentHash ( ) ) ;
}
}
else
{
LogPrint ( eLogDebug , " SSU2: Can't find more introducers " ) ;
break ;
}
}
}
2023-07-22 19:03:03 +00:00
introducers . splice ( introducers . end ( ) , impliedList ) ; // insert non-published, but non-expired introducers back
2022-07-21 01:55:48 +00:00
}
void SSU2Server : : ScheduleIntroducersUpdateTimer ( )
{
if ( m_IsPublished )
2022-10-09 17:24:43 +00:00
{
2023-07-22 12:50:49 +00:00
m_IntroducersUpdateTimer . expires_from_now ( boost : : posix_time : : seconds (
2024-06-08 02:10:52 +00:00
SSU2_KEEP_ALIVE_INTERVAL + m_Rng ( ) % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE ) ) ;
2022-07-21 01:55:48 +00:00
m_IntroducersUpdateTimer . async_wait ( std : : bind ( & SSU2Server : : HandleIntroducersUpdateTimer ,
this , std : : placeholders : : _1 , true ) ) ;
2022-10-09 17:24:43 +00:00
}
}
2022-07-21 01:55:48 +00:00
void SSU2Server : : RescheduleIntroducersUpdateTimer ( )
{
if ( m_IsPublished )
{
m_IntroducersUpdateTimer . cancel ( ) ;
2022-08-07 13:50:30 +00:00
i2p : : context . ClearSSU2Introducers ( true ) ;
m_Introducers . clear ( ) ;
2023-07-22 12:50:49 +00:00
m_IntroducersUpdateTimer . expires_from_now ( boost : : posix_time : : seconds (
2024-06-08 02:10:52 +00:00
( SSU2_KEEP_ALIVE_INTERVAL + m_Rng ( ) % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE ) / 2 ) ) ;
2022-07-21 01:55:48 +00:00
m_IntroducersUpdateTimer . async_wait ( std : : bind ( & SSU2Server : : HandleIntroducersUpdateTimer ,
this , std : : placeholders : : _1 , true ) ) ;
2022-10-09 17:24:43 +00:00
}
2022-07-21 01:55:48 +00:00
}
void SSU2Server : : ScheduleIntroducersUpdateTimerV6 ( )
{
if ( m_IsPublished )
2022-10-09 17:24:43 +00:00
{
2023-07-22 12:50:49 +00:00
m_IntroducersUpdateTimerV6 . expires_from_now ( boost : : posix_time : : seconds (
2024-06-08 02:10:52 +00:00
SSU2_KEEP_ALIVE_INTERVAL + m_Rng ( ) % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE ) ) ;
2022-07-21 01:55:48 +00:00
m_IntroducersUpdateTimerV6 . async_wait ( std : : bind ( & SSU2Server : : HandleIntroducersUpdateTimer ,
this , std : : placeholders : : _1 , false ) ) ;
2022-10-09 17:24:43 +00:00
}
}
2022-07-21 01:55:48 +00:00
void SSU2Server : : RescheduleIntroducersUpdateTimerV6 ( )
{
if ( m_IsPublished )
{
m_IntroducersUpdateTimerV6 . cancel ( ) ;
2022-08-07 13:50:30 +00:00
i2p : : context . ClearSSU2Introducers ( false ) ;
m_IntroducersV6 . clear ( ) ;
2023-07-22 12:50:49 +00:00
m_IntroducersUpdateTimerV6 . expires_from_now ( boost : : posix_time : : seconds (
2024-06-08 02:10:52 +00:00
( SSU2_KEEP_ALIVE_INTERVAL + m_Rng ( ) % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE ) / 2 ) ) ;
2022-07-21 01:55:48 +00:00
m_IntroducersUpdateTimerV6 . async_wait ( std : : bind ( & SSU2Server : : HandleIntroducersUpdateTimer ,
this , std : : placeholders : : _1 , false ) ) ;
2022-10-09 17:24:43 +00:00
}
2022-07-21 01:55:48 +00:00
}
2022-10-09 17:24:43 +00:00
2022-07-21 01:55:48 +00:00
void SSU2Server : : HandleIntroducersUpdateTimer ( const boost : : system : : error_code & ecode , bool v4 )
{
if ( ecode ! = boost : : asio : : error : : operation_aborted )
{
// timeout expired
if ( v4 )
{
2023-07-30 12:29:10 +00:00
if ( i2p : : context . GetTesting ( ) )
2022-07-21 01:55:48 +00:00
{
// we still don't know if we need introducers
ScheduleIntroducersUpdateTimer ( ) ;
return ;
}
if ( i2p : : context . GetStatus ( ) ! = eRouterStatusFirewalled )
{
// we don't need introducers
2022-08-06 01:23:23 +00:00
i2p : : context . ClearSSU2Introducers ( true ) ;
2022-07-21 01:55:48 +00:00
m_Introducers . clear ( ) ;
return ;
}
2022-07-21 23:38:18 +00:00
// we are firewalled
auto addr = i2p : : context . GetRouterInfo ( ) . GetSSU2V4Address ( ) ;
if ( addr & & addr - > ssu & & addr - > ssu - > introducers . empty ( ) )
2022-11-23 03:03:19 +00:00
i2p : : context . SetUnreachable ( true , false ) ; // v4
2022-10-09 17:24:43 +00:00
2022-07-21 01:55:48 +00:00
UpdateIntroducers ( true ) ;
ScheduleIntroducersUpdateTimer ( ) ;
}
else
{
2023-07-30 12:29:10 +00:00
if ( i2p : : context . GetTestingV6 ( ) )
2022-07-21 01:55:48 +00:00
{
// we still don't know if we need introducers
ScheduleIntroducersUpdateTimerV6 ( ) ;
return ;
}
if ( i2p : : context . GetStatusV6 ( ) ! = eRouterStatusFirewalled )
{
// we don't need introducers
2022-08-06 01:23:23 +00:00
i2p : : context . ClearSSU2Introducers ( false ) ;
2022-07-21 01:55:48 +00:00
m_IntroducersV6 . clear ( ) ;
return ;
}
2022-07-21 23:38:18 +00:00
// we are firewalled
auto addr = i2p : : context . GetRouterInfo ( ) . GetSSU2V6Address ( ) ;
if ( addr & & addr - > ssu & & addr - > ssu - > introducers . empty ( ) )
2022-11-23 03:03:19 +00:00
i2p : : context . SetUnreachable ( false , true ) ; // v6
2022-10-09 17:24:43 +00:00
2022-07-21 01:55:48 +00:00
UpdateIntroducers ( false ) ;
ScheduleIntroducersUpdateTimerV6 ( ) ;
2022-10-09 17:24:43 +00:00
}
2022-07-21 01:55:48 +00:00
}
2022-10-09 17:24:43 +00:00
}
2022-10-17 01:23:28 +00:00
void SSU2Server : : SendThroughProxy ( const uint8_t * header , size_t headerLen , const uint8_t * headerX , size_t headerXLen ,
const uint8_t * payload , size_t payloadLen , const boost : : asio : : ip : : udp : : endpoint & to )
{
if ( ! m_ProxyRelayEndpoint ) return ;
size_t requestHeaderSize = 0 ;
memset ( m_UDPRequestHeader , 0 , 3 ) ;
if ( to . address ( ) . is_v6 ( ) )
2022-12-13 18:41:58 +00:00
{
2022-10-17 01:23:28 +00:00
m_UDPRequestHeader [ 3 ] = SOCKS5_ATYP_IPV6 ;
memcpy ( m_UDPRequestHeader + 4 , to . address ( ) . to_v6 ( ) . to_bytes ( ) . data ( ) , 16 ) ;
requestHeaderSize = SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE ;
2022-12-13 18:41:58 +00:00
}
2022-10-17 01:23:28 +00:00
else
2022-12-13 18:41:58 +00:00
{
2022-10-17 01:23:28 +00:00
m_UDPRequestHeader [ 3 ] = SOCKS5_ATYP_IPV4 ;
memcpy ( m_UDPRequestHeader + 4 , to . address ( ) . to_v4 ( ) . to_bytes ( ) . data ( ) , 4 ) ;
requestHeaderSize = SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE ;
2022-12-13 18:41:58 +00:00
}
2022-10-17 01:23:28 +00:00
htobe16buf ( m_UDPRequestHeader + requestHeaderSize - 2 , to . port ( ) ) ;
2022-12-13 18:41:58 +00:00
2022-10-17 01:23:28 +00:00
std : : vector < boost : : asio : : const_buffer > bufs ;
bufs . push_back ( boost : : asio : : buffer ( m_UDPRequestHeader , requestHeaderSize ) ) ;
bufs . push_back ( boost : : asio : : buffer ( header , headerLen ) ) ;
2022-12-13 18:41:58 +00:00
if ( headerX ) bufs . push_back ( boost : : asio : : buffer ( headerX , headerXLen ) ) ;
2022-10-17 01:23:28 +00:00
bufs . push_back ( boost : : asio : : buffer ( payload , payloadLen ) ) ;
boost : : system : : error_code ec ;
m_SocketV4 . send_to ( bufs , * m_ProxyRelayEndpoint , 0 , ec ) ; // TODO: implement ipv6 proxy
if ( ! ec )
i2p : : transport : : transports . UpdateSentBytes ( headerLen + payloadLen ) ;
else
LogPrint ( eLogError , " SSU2: Send exception: " , ec . message ( ) , " to " , to ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-17 02:16:16 +00:00
void SSU2Server : : ProcessNextPacketFromProxy ( uint8_t * buf , size_t len )
{
2022-10-27 17:56:42 +00:00
if ( buf [ 2 ] ) // FRAG
{
LogPrint ( eLogWarning , " SSU2: Proxy packet fragmentation is not supported " ) ;
return ;
2022-12-13 18:41:58 +00:00
}
2022-10-17 02:16:16 +00:00
size_t offset = 0 ;
boost : : asio : : ip : : udp : : endpoint ep ;
switch ( buf [ 3 ] ) // ATYP
2022-12-13 18:41:58 +00:00
{
2022-10-17 02:16:16 +00:00
case SOCKS5_ATYP_IPV4 :
{
offset = SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE ;
if ( offset > len ) return ;
boost : : asio : : ip : : address_v4 : : bytes_type bytes ;
memcpy ( bytes . data ( ) , buf + 4 , 4 ) ;
uint16_t port = bufbe16toh ( buf + 8 ) ;
ep = boost : : asio : : ip : : udp : : endpoint ( boost : : asio : : ip : : address_v4 ( bytes ) , port ) ;
break ;
2022-12-13 18:41:58 +00:00
}
2022-10-17 02:16:16 +00:00
case SOCKS5_ATYP_IPV6 :
{
offset = SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE ;
if ( offset > len ) return ;
boost : : asio : : ip : : address_v6 : : bytes_type bytes ;
memcpy ( bytes . data ( ) , buf + 4 , 16 ) ;
uint16_t port = bufbe16toh ( buf + 20 ) ;
ep = boost : : asio : : ip : : udp : : endpoint ( boost : : asio : : ip : : address_v6 ( bytes ) , port ) ;
break ;
2022-12-13 18:41:58 +00:00
}
2022-10-17 02:16:16 +00:00
default :
{
LogPrint ( eLogWarning , " SSU2: Unknown ATYP " , ( int ) buf [ 3 ] , " from proxy relay " ) ;
return ;
2022-12-13 18:41:58 +00:00
}
}
2022-10-17 02:16:16 +00:00
ProcessNextPacket ( buf + offset , len - offset , ep ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-19 01:11:06 +00:00
void SSU2Server : : ConnectToProxy ( )
{
if ( ! m_ProxyEndpoint ) return ;
m_UDPAssociateSocket . reset ( new boost : : asio : : ip : : tcp : : socket ( m_ReceiveService . GetService ( ) ) ) ;
2023-01-03 18:25:19 +00:00
m_UDPAssociateSocket - > async_connect ( * m_ProxyEndpoint ,
2022-10-19 01:11:06 +00:00
[ this ] ( const boost : : system : : error_code & ecode )
2023-01-03 18:25:19 +00:00
{
2022-10-19 01:11:06 +00:00
if ( ecode )
{
LogPrint ( eLogError , " SSU2: Can't connect to proxy " , * m_ProxyEndpoint , " " , ecode . message ( ) ) ;
m_UDPAssociateSocket . reset ( nullptr ) ;
2022-10-28 18:06:45 +00:00
ReconnectToProxy ( ) ;
2022-10-19 01:11:06 +00:00
}
else
HandshakeWithProxy ( ) ;
} ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-19 01:11:06 +00:00
void SSU2Server : : HandshakeWithProxy ( )
{
if ( ! m_UDPAssociateSocket ) return ;
2023-01-03 18:25:19 +00:00
m_UDPRequestHeader [ 0 ] = SOCKS5_VER ;
2022-10-19 01:11:06 +00:00
m_UDPRequestHeader [ 1 ] = 1 ; // 1 method
m_UDPRequestHeader [ 2 ] = 0 ; // no authentication
boost : : asio : : async_write ( * m_UDPAssociateSocket , boost : : asio : : buffer ( m_UDPRequestHeader , 3 ) , boost : : asio : : transfer_all ( ) ,
[ this ] ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
( void ) bytes_transferred ;
if ( ecode )
{
LogPrint ( eLogError , " SSU2: Proxy write error " , ecode . message ( ) ) ;
2022-12-13 18:41:58 +00:00
m_UDPAssociateSocket . reset ( nullptr ) ;
2022-10-28 18:06:45 +00:00
ReconnectToProxy ( ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-19 01:11:06 +00:00
else
ReadHandshakeWithProxyReply ( ) ;
} ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-19 01:11:06 +00:00
void SSU2Server : : ReadHandshakeWithProxyReply ( )
{
if ( ! m_UDPAssociateSocket ) return ;
boost : : asio : : async_read ( * m_UDPAssociateSocket , boost : : asio : : buffer ( m_UDPRequestHeader , 2 ) , boost : : asio : : transfer_all ( ) ,
[ this ] ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
( void ) bytes_transferred ;
if ( ecode )
{
LogPrint ( eLogError , " SSU2: Proxy read error " , ecode . message ( ) ) ;
m_UDPAssociateSocket . reset ( nullptr ) ;
2022-10-28 18:06:45 +00:00
ReconnectToProxy ( ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-19 01:11:06 +00:00
else
{
if ( m_UDPRequestHeader [ 0 ] = = SOCKS5_VER & & ! m_UDPRequestHeader [ 1 ] )
SendUDPAssociateRequest ( ) ;
else
{
LogPrint ( eLogError , " SSU2: Invalid proxy reply " ) ;
m_UDPAssociateSocket . reset ( nullptr ) ;
2022-12-13 18:41:58 +00:00
}
}
} ) ;
}
2022-10-19 01:11:06 +00:00
void SSU2Server : : SendUDPAssociateRequest ( )
{
if ( ! m_UDPAssociateSocket ) return ;
2023-01-03 18:25:19 +00:00
m_UDPRequestHeader [ 0 ] = SOCKS5_VER ;
m_UDPRequestHeader [ 1 ] = SOCKS5_CMD_UDP_ASSOCIATE ;
2022-10-19 01:11:06 +00:00
m_UDPRequestHeader [ 2 ] = 0 ; // RSV
m_UDPRequestHeader [ 3 ] = SOCKS5_ATYP_IPV4 ; // TODO: implement ipv6 proxy
memset ( m_UDPRequestHeader + 4 , 0 , 6 ) ; // address and port all zeros
boost : : asio : : async_write ( * m_UDPAssociateSocket , boost : : asio : : buffer ( m_UDPRequestHeader , SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE ) , boost : : asio : : transfer_all ( ) ,
[ this ] ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
( void ) bytes_transferred ;
if ( ecode )
{
LogPrint ( eLogError , " SSU2: Proxy write error " , ecode . message ( ) ) ;
2022-12-13 18:41:58 +00:00
m_UDPAssociateSocket . reset ( nullptr ) ;
2022-10-28 18:06:45 +00:00
ReconnectToProxy ( ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-19 01:11:06 +00:00
else
ReadUDPAssociateReply ( ) ;
} ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-19 01:11:06 +00:00
void SSU2Server : : ReadUDPAssociateReply ( )
{
if ( ! m_UDPAssociateSocket ) return ;
boost : : asio : : async_read ( * m_UDPAssociateSocket , boost : : asio : : buffer ( m_UDPRequestHeader , SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE ) , boost : : asio : : transfer_all ( ) ,
[ this ] ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
( void ) bytes_transferred ;
if ( ecode )
{
LogPrint ( eLogError , " SSU2: Proxy read error " , ecode . message ( ) ) ;
m_UDPAssociateSocket . reset ( nullptr ) ;
2022-10-28 18:06:45 +00:00
ReconnectToProxy ( ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-19 01:11:06 +00:00
else
{
if ( m_UDPRequestHeader [ 0 ] = = SOCKS5_VER & & ! m_UDPRequestHeader [ 1 ] )
2022-12-13 18:41:58 +00:00
{
2022-10-19 01:11:06 +00:00
if ( m_UDPRequestHeader [ 3 ] = = SOCKS5_ATYP_IPV4 )
{
boost : : asio : : ip : : address_v4 : : bytes_type bytes ;
memcpy ( bytes . data ( ) , m_UDPRequestHeader + 4 , 4 ) ;
uint16_t port = bufbe16toh ( m_UDPRequestHeader + 8 ) ;
m_ProxyRelayEndpoint . reset ( new boost : : asio : : ip : : udp : : endpoint ( boost : : asio : : ip : : address_v4 ( bytes ) , port ) ) ;
m_SocketV4 . open ( boost : : asio : : ip : : udp : : v4 ( ) ) ;
Receive ( m_SocketV4 ) ;
ReadUDPAssociateSocket ( ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-19 01:11:06 +00:00
else
{
LogPrint ( eLogError , " SSU2: Proxy UDP associate unsupported ATYP " , ( int ) m_UDPRequestHeader [ 3 ] ) ;
m_UDPAssociateSocket . reset ( nullptr ) ;
2022-12-13 18:41:58 +00:00
}
}
2022-10-19 01:11:06 +00:00
else
{
LogPrint ( eLogError , " SSU2: Proxy UDP associate error " , ( int ) m_UDPRequestHeader [ 1 ] ) ;
m_UDPAssociateSocket . reset ( nullptr ) ;
2022-12-13 18:41:58 +00:00
}
}
} ) ;
}
2022-10-19 01:11:06 +00:00
void SSU2Server : : ReadUDPAssociateSocket ( )
{
if ( ! m_UDPAssociateSocket ) return ;
m_UDPAssociateSocket - > async_read_some ( boost : : asio : : buffer ( m_UDPRequestHeader , 1 ) ,
[ this ] ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
( void ) bytes_transferred ;
if ( ecode )
{
2022-10-28 18:06:45 +00:00
LogPrint ( eLogWarning , " SSU2: Proxy UDP Associate socket error " , ecode . message ( ) ) ;
2022-10-19 01:11:06 +00:00
m_UDPAssociateSocket . reset ( nullptr ) ;
2022-10-28 18:06:45 +00:00
m_ProxyRelayEndpoint . reset ( nullptr ) ;
2022-10-28 22:54:04 +00:00
m_SocketV4 . close ( ) ;
2022-10-28 18:06:45 +00:00
ConnectToProxy ( ) ; // try to reconnect immediately
2022-12-13 18:41:58 +00:00
}
2022-10-19 01:11:06 +00:00
else
ReadUDPAssociateSocket ( ) ;
2022-12-13 18:41:58 +00:00
} ) ;
}
2022-10-26 20:05:40 +00:00
2022-10-28 18:06:45 +00:00
void SSU2Server : : ReconnectToProxy ( )
{
LogPrint ( eLogInfo , " SSU2: Reconnect to proxy after " , SSU2_PROXY_CONNECT_RETRY_TIMEOUT , " seconds " ) ;
if ( m_ProxyConnectRetryTimer )
m_ProxyConnectRetryTimer - > cancel ( ) ;
2022-12-13 18:41:58 +00:00
else
2022-10-28 18:06:45 +00:00
m_ProxyConnectRetryTimer . reset ( new boost : : asio : : deadline_timer ( m_ReceiveService . GetService ( ) ) ) ;
m_ProxyConnectRetryTimer - > expires_from_now ( boost : : posix_time : : seconds ( SSU2_PROXY_CONNECT_RETRY_TIMEOUT ) ) ;
m_ProxyConnectRetryTimer - > async_wait (
[ this ] ( const boost : : system : : error_code & ecode )
{
if ( ecode ! = boost : : asio : : error : : operation_aborted )
{
m_UDPAssociateSocket . reset ( nullptr ) ;
m_ProxyRelayEndpoint . reset ( nullptr ) ;
2022-10-28 22:54:04 +00:00
LogPrint ( eLogInfo , " SSU2: Reconnecting to proxy " ) ;
2022-10-28 18:06:45 +00:00
ConnectToProxy ( ) ;
}
2023-01-03 18:25:19 +00:00
} ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-26 20:05:40 +00:00
bool SSU2Server : : SetProxy ( const std : : string & address , uint16_t port )
{
boost : : system : : error_code ecode ;
auto addr = boost : : asio : : ip : : address : : from_string ( address , ecode ) ;
if ( ! ecode & & ! addr . is_unspecified ( ) & & port )
{
m_IsThroughProxy = true ;
m_ProxyEndpoint . reset ( new boost : : asio : : ip : : tcp : : endpoint ( addr , port ) ) ;
2022-12-13 18:41:58 +00:00
}
2022-10-26 20:05:40 +00:00
else
{
if ( ecode )
LogPrint ( eLogError , " SSU2: Invalid proxy address " , address , " " , ecode . message ( ) ) ;
return false ;
2022-12-13 18:41:58 +00:00
}
2022-10-26 20:05:40 +00:00
return true ;
2022-12-13 18:41:58 +00:00
}
2022-02-04 20:01:18 +00:00
}
}