/*
* Copyright ( c ) 2013 - 2024 , The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
# include <string.h>
# include "Log.h"
# include "ClientContext.h"
# include "util.h"
# include "BOB.h"
namespace i2p
{
namespace client
{
BOBI2PInboundTunnel : : BOBI2PInboundTunnel ( const boost : : asio : : ip : : tcp : : endpoint & ep , std : : shared_ptr < ClientDestination > localDestination ) :
BOBI2PTunnel ( localDestination ) , m_Acceptor ( localDestination - > GetService ( ) , ep )
{
}
BOBI2PInboundTunnel : : ~ BOBI2PInboundTunnel ( )
{
Stop ( ) ;
}
void BOBI2PInboundTunnel : : Start ( )
{
m_Acceptor . listen ( ) ;
Accept ( ) ;
}
void BOBI2PInboundTunnel : : Stop ( )
{
m_Acceptor . close ( ) ;
ClearHandlers ( ) ;
}
void BOBI2PInboundTunnel : : Accept ( )
{
auto receiver = std : : make_shared < AddressReceiver > ( ) ;
receiver - > socket = std : : make_shared < boost : : asio : : ip : : tcp : : socket > ( GetService ( ) ) ;
m_Acceptor . async_accept ( * receiver - > socket , std : : bind ( & BOBI2PInboundTunnel : : HandleAccept , this ,
std : : placeholders : : _1 , receiver ) ) ;
}
void BOBI2PInboundTunnel : : HandleAccept ( const boost : : system : : error_code & ecode , std : : shared_ptr < AddressReceiver > receiver )
{
if ( ! ecode )
{
Accept ( ) ;
ReceiveAddress ( receiver ) ;
}
}
void BOBI2PInboundTunnel : : ReceiveAddress ( std : : shared_ptr < AddressReceiver > receiver )
{
receiver - > socket - > async_read_some ( boost : : asio : : buffer (
receiver - > buffer + receiver - > bufferOffset ,
BOB_COMMAND_BUFFER_SIZE - receiver - > bufferOffset ) ,
std : : bind ( & BOBI2PInboundTunnel : : HandleReceivedAddress , this ,
std : : placeholders : : _1 , std : : placeholders : : _2 , receiver ) ) ;
}
void BOBI2PInboundTunnel : : HandleReceivedAddress ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred ,
std : : shared_ptr < AddressReceiver > receiver )
{
if ( ecode )
LogPrint ( eLogError , " BOB: Inbound tunnel read error: " , ecode . message ( ) ) ;
else
{
receiver - > bufferOffset + = bytes_transferred ;
receiver - > buffer [ receiver - > bufferOffset ] = 0 ;
char * eol = strchr ( receiver - > buffer , ' \n ' ) ;
if ( eol )
{
* eol = 0 ;
if ( eol ! = receiver - > buffer & & eol [ - 1 ] = = ' \r ' ) eol [ - 1 ] = 0 ; // workaround for Transmission, it sends '\r\n' terminated address
receiver - > data = ( uint8_t * ) eol + 1 ;
receiver - > dataLen = receiver - > bufferOffset - ( eol - receiver - > buffer + 1 ) ;
auto addr = context . GetAddressBook ( ) . GetAddress ( receiver - > buffer ) ;
if ( ! addr )
{
LogPrint ( eLogError , " BOB: Address " , receiver - > buffer , " not found " ) ;
return ;
}
if ( addr - > IsIdentHash ( ) )
{
auto leaseSet = GetLocalDestination ( ) - > FindLeaseSet ( addr - > identHash ) ;
if ( leaseSet )
CreateConnection ( receiver , leaseSet ) ;
else
GetLocalDestination ( ) - > RequestDestination ( addr - > identHash ,
std : : bind ( & BOBI2PInboundTunnel : : HandleDestinationRequestComplete ,
this , std : : placeholders : : _1 , receiver ) ) ;
}
else
GetLocalDestination ( ) - > RequestDestinationWithEncryptedLeaseSet ( addr - > blindedPublicKey ,
std : : bind ( & BOBI2PInboundTunnel : : HandleDestinationRequestComplete ,
this , std : : placeholders : : _1 , receiver ) ) ;
}
else
{
if ( receiver - > bufferOffset < BOB_COMMAND_BUFFER_SIZE )
ReceiveAddress ( receiver ) ;
else
LogPrint ( eLogError , " BOB: Missing inbound address " ) ;
}
}
}
void BOBI2PInboundTunnel : : HandleDestinationRequestComplete ( std : : shared_ptr < i2p : : data : : LeaseSet > leaseSet , std : : shared_ptr < AddressReceiver > receiver )
{
if ( leaseSet )
CreateConnection ( receiver , leaseSet ) ;
else
LogPrint ( eLogError , " BOB: LeaseSet for inbound destination not found " ) ;
}
void BOBI2PInboundTunnel : : CreateConnection ( std : : shared_ptr < AddressReceiver > receiver , std : : shared_ptr < const i2p : : data : : LeaseSet > leaseSet )
{
LogPrint ( eLogDebug , " BOB: New inbound connection " ) ;
auto connection = std : : make_shared < I2PTunnelConnection > ( this , receiver - > socket , leaseSet ) ;
AddHandler ( connection ) ;
connection - > I2PConnect ( receiver - > data , receiver - > dataLen ) ;
}
BOBI2POutboundTunnel : : BOBI2POutboundTunnel ( const std : : string & outhost , uint16_t port ,
std : : shared_ptr < ClientDestination > localDestination , bool quiet ) : BOBI2PTunnel ( localDestination ) ,
m_Endpoint ( boost : : asio : : ip : : make_address ( outhost ) , port ) , m_IsQuiet ( quiet )
{
}
void BOBI2POutboundTunnel : : Start ( )
{
Accept ( ) ;
}
void BOBI2POutboundTunnel : : Stop ( )
{
ClearHandlers ( ) ;
}
void BOBI2POutboundTunnel : : Accept ( )
{
auto localDestination = GetLocalDestination ( ) ;
if ( localDestination )
localDestination - > AcceptStreams ( std : : bind ( & BOBI2POutboundTunnel : : HandleAccept , this , std : : placeholders : : _1 ) ) ;
else
LogPrint ( eLogError , " BOB: Local destination not set for server tunnel " ) ;
}
void BOBI2POutboundTunnel : : HandleAccept ( std : : shared_ptr < i2p : : stream : : Stream > stream )
{
if ( stream )
{
auto conn = std : : make_shared < I2PTunnelConnection > ( this , stream , m_Endpoint , m_IsQuiet ) ;
AddHandler ( conn ) ;
conn - > Connect ( ) ;
}
}
BOBDestination : : BOBDestination ( std : : shared_ptr < ClientDestination > localDestination ,
const std : : string & nickname , const std : : string & inhost , const std : : string & outhost ,
const uint16_t inport , const uint16_t outport , const bool quiet ) :
m_LocalDestination ( localDestination ) ,
m_OutboundTunnel ( nullptr ) , m_InboundTunnel ( nullptr ) ,
m_Nickname ( nickname ) , m_InHost ( inhost ) , m_OutHost ( outhost ) ,
m_InPort ( inport ) , m_OutPort ( outport ) , m_Quiet ( quiet ) , m_IsRunning ( false )
{
}
BOBDestination : : ~ BOBDestination ( )
{
delete m_OutboundTunnel ;
delete m_InboundTunnel ;
i2p : : client : : context . DeleteLocalDestination ( m_LocalDestination ) ;
}
void BOBDestination : : Start ( )
{
if ( m_OutboundTunnel ) m_OutboundTunnel - > Start ( ) ;
if ( m_InboundTunnel ) m_InboundTunnel - > Start ( ) ;
m_IsRunning = true ;
}
void BOBDestination : : Stop ( )
{
StopTunnels ( ) ;
m_LocalDestination - > Stop ( ) ;
}
void BOBDestination : : StopTunnels ( )
{
m_IsRunning = false ;
if ( m_OutboundTunnel )
{
m_OutboundTunnel - > Stop ( ) ;
delete m_OutboundTunnel ;
m_OutboundTunnel = nullptr ;
}
if ( m_InboundTunnel )
{
m_InboundTunnel - > Stop ( ) ;
delete m_InboundTunnel ;
m_InboundTunnel = nullptr ;
}
}
void BOBDestination : : CreateInboundTunnel ( uint16_t port , const std : : string & inhost )
{
if ( ! m_InboundTunnel )
{
// update inport and inhost (user can stop tunnel and change)
m_InPort = port ;
m_InHost = inhost ;
boost : : asio : : ip : : tcp : : endpoint ep ( boost : : asio : : ip : : tcp : : v4 ( ) , port ) ;
if ( ! inhost . empty ( ) )
{
boost : : system : : error_code ec ;
auto addr = boost : : asio : : ip : : make_address ( inhost , ec ) ;
if ( ! ec )
ep . address ( addr ) ;
else
LogPrint ( eLogError , " BOB: " , ec . message ( ) ) ;
}
m_InboundTunnel = new BOBI2PInboundTunnel ( ep , m_LocalDestination ) ;
}
}
void BOBDestination : : CreateOutboundTunnel ( const std : : string & outhost , uint16_t port , bool quiet )
{
if ( ! m_OutboundTunnel )
{
// update outport and outhost (user can stop tunnel and change)
m_OutPort = port ;
m_OutHost = outhost ;
m_OutboundTunnel = new BOBI2POutboundTunnel ( outhost , port , m_LocalDestination , quiet ) ;
}
}
BOBCommandSession : : BOBCommandSession ( BOBCommandChannel & owner ) :
m_Owner ( owner ) , m_Socket ( m_Owner . GetService ( ) ) ,
m_ReceiveBuffer ( BOB_COMMAND_BUFFER_SIZE + 1 ) , m_SendBuffer ( BOB_COMMAND_BUFFER_SIZE + 1 ) ,
m_IsOpen ( true ) , m_IsQuiet ( false ) , m_IsActive ( false ) ,
m_InPort ( 0 ) , m_OutPort ( 0 ) , m_CurrentDestination ( nullptr )
{
}
BOBCommandSession : : ~ BOBCommandSession ( )
{
}
void BOBCommandSession : : Terminate ( )
{
m_Socket . close ( ) ;
m_IsOpen = false ;
}
void BOBCommandSession : : Receive ( )
{
boost : : asio : : async_read_until ( m_Socket , m_ReceiveBuffer , ' \n ' ,
std : : bind ( & BOBCommandSession : : HandleReceivedLine , shared_from_this ( ) ,
std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
}
void BOBCommandSession : : HandleReceivedLine ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
if ( ecode )
{
LogPrint ( eLogError , " BOB: Command channel read error: " , ecode . message ( ) ) ;
if ( ecode ! = boost : : asio : : error : : operation_aborted )
Terminate ( ) ;
}
else
{
std : : string line ;
std : : istream is ( & m_ReceiveBuffer ) ;
std : : getline ( is , line ) ;
std : : string command , operand ;
std : : istringstream iss ( line ) ;
iss > > command > > operand ;
// process command
auto & handlers = m_Owner . GetCommandHandlers ( ) ;
auto it = handlers . find ( command ) ;
if ( it ! = handlers . end ( ) )
{
( this - > * ( it - > second ) ) ( operand . c_str ( ) , operand . length ( ) ) ;
}
else
{
LogPrint ( eLogError , " BOB: Unknown command " , command . c_str ( ) ) ;
SendReplyError ( " unknown command " ) ;
}
}
}
void BOBCommandSession : : Send ( )
{
boost : : asio : : async_write ( m_Socket , m_SendBuffer ,
boost : : asio : : transfer_all ( ) ,
std : : bind ( & BOBCommandSession : : HandleSent , shared_from_this ( ) ,
std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
}
void BOBCommandSession : : HandleSent ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
if ( ecode )
{
LogPrint ( eLogError , " BOB: Command channel send error: " , ecode . message ( ) ) ;
if ( ecode ! = boost : : asio : : error : : operation_aborted )
Terminate ( ) ;
}
else
{
if ( m_IsOpen )
Receive ( ) ;
else
Terminate ( ) ;
}
}
void BOBCommandSession : : SendReplyOK ( const char * msg )
{
std : : ostream os ( & m_SendBuffer ) ;
os < < " OK " ;
if ( msg )
{
os < < " " < < msg ;
}
os < < std : : endl ;
Send ( ) ;
}
void BOBCommandSession : : SendReplyError ( const char * msg )
{
std : : ostream os ( & m_SendBuffer ) ;
os < < " ERROR " < < msg < < std : : endl ;
Send ( ) ;
}
void BOBCommandSession : : SendVersion ( )
{
std : : ostream os ( & m_SendBuffer ) ;
os < < " BOB 00.00.10 " < < std : : endl ;
SendReplyOK ( ) ;
}
void BOBCommandSession : : SendRaw ( const char * data )
{
std : : ostream os ( & m_SendBuffer ) ;
os < < data < < std : : endl ;
}
void BOBCommandSession : : BuildStatusLine ( bool currentTunnel , std : : shared_ptr < BOBDestination > dest , std : : string & out )
{
// helper lambdas
const auto issetStr = [ ] ( const std : : string & str ) { return str . empty ( ) ? " not_set " : str ; } ; // for inhost, outhost
const auto issetNum = [ & issetStr ] ( const int p ) { return issetStr ( p = = 0 ? " " : std : : to_string ( p ) ) ; } ; // for inport, outport
const auto destExists = [ ] ( const BOBDestination * const dest ) { return dest ! = nullptr ; } ;
const auto destReady = [ ] ( const BOBDestination * const dest ) { return dest & & dest - > IsRunning ( ) ; } ;
const auto bool_str = [ ] ( const bool v ) { return v ? " true " : " false " ; } ; // bool -> str
// tunnel info
const std : : string nickname = currentTunnel ? m_Nickname : dest - > GetNickname ( ) ;
const bool quiet = currentTunnel ? m_IsQuiet : dest - > GetQuiet ( ) ;
const std : : string inhost = issetStr ( currentTunnel ? m_InHost : dest - > GetInHost ( ) ) ;
const std : : string outhost = issetStr ( currentTunnel ? m_OutHost : dest - > GetOutHost ( ) ) ;
const std : : string inport = issetNum ( currentTunnel ? m_InPort : dest - > GetInPort ( ) ) ;
const std : : string outport = issetNum ( currentTunnel ? m_OutPort : dest - > GetOutPort ( ) ) ;
const bool keys = destExists ( dest . get ( ) ) ; // key must exist when destination is created
const bool starting = destExists ( dest . get ( ) ) & & ! destReady ( dest . get ( ) ) ;
const bool running = destExists ( dest . get ( ) ) & & destReady ( dest . get ( ) ) ;
const bool stopping = false ;
// build line
std : : stringstream ss ;
ss < < " DATA "
< < " NICKNAME: " < < nickname < < " " < < " STARTING: " < < bool_str ( starting ) < < " "
< < " RUNNING: " < < bool_str ( running ) < < " " < < " STOPPING: " < < bool_str ( stopping ) < < " "
< < " KEYS: " < < bool_str ( keys ) < < " " < < " QUIET: " < < bool_str ( quiet ) < < " "
< < " INPORT: " < < inport < < " " < < " INHOST: " < < inhost < < " "
< < " OUTPORT: " < < outport < < " " < < " OUTHOST: " < < outhost ;
out = ss . str ( ) ;
}
void BOBCommandSession : : ZapCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: zap " ) ;
Terminate ( ) ;
}
void BOBCommandSession : : QuitCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: quit " ) ;
m_IsOpen = false ;
SendReplyOK ( " Bye! " ) ;
}
void BOBCommandSession : : StartCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: start " , m_Nickname ) ;
if ( m_IsActive )
{
SendReplyError ( " tunnel is active " ) ;
return ;
}
if ( ! m_Keys . GetPublic ( ) ) // keys are set ?
{
SendReplyError ( " Keys must be set. " ) ;
return ;
}
if ( m_InPort = = 0
& & m_OutHost . empty ( ) & & m_OutPort = = 0 )
{
SendReplyError ( " (inhost):inport or outhost:outport must be set. " ) ;
return ;
}
if ( ! m_InHost . empty ( ) )
{
// TODO: FIXME: temporary validation, until hostname support is added
boost : : system : : error_code ec ;
boost : : asio : : ip : : make_address ( m_InHost , ec ) ;
if ( ec )
{
SendReplyError ( " inhost must be a valid IPv4 address. " ) ;
return ;
}
}
if ( ! m_OutHost . empty ( ) )
{
// TODO: FIXME: temporary validation, until hostname support is added
boost : : system : : error_code ec ;
boost : : asio : : ip : : make_address ( m_OutHost , ec ) ;
if ( ec )
{
SendReplyError ( " outhost must be a IPv4 address. " ) ;
return ;
}
}
if ( ! m_CurrentDestination )
{
m_CurrentDestination = std : : make_shared < BOBDestination > ( i2p : : client : : context . CreateNewLocalDestination ( m_Keys , true , & m_Options ) , // deleted in clear command
m_Nickname , m_InHost , m_OutHost , m_InPort , m_OutPort , m_IsQuiet ) ;
m_Owner . AddDestination ( m_Nickname , m_CurrentDestination ) ;
}
if ( m_InPort )
m_CurrentDestination - > CreateInboundTunnel ( m_InPort , m_InHost ) ;
if ( m_OutPort & & ! m_OutHost . empty ( ) )
m_CurrentDestination - > CreateOutboundTunnel ( m_OutHost , m_OutPort , m_IsQuiet ) ;
m_CurrentDestination - > Start ( ) ;
SendReplyOK ( " Tunnel starting " ) ;
m_IsActive = true ;
}
void BOBCommandSession : : StopCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: stop " , m_Nickname ) ;
if ( ! m_IsActive )
{
SendReplyError ( " tunnel is inactive " ) ;
return ;
}
auto dest = m_Owner . FindDestination ( m_Nickname ) ;
if ( dest )
{
dest - > StopTunnels ( ) ;
SendReplyOK ( " Tunnel stopping " ) ;
}
else
SendReplyError ( " tunnel not found " ) ;
m_IsActive = false ;
}
void BOBCommandSession : : SetNickCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: setnick " , operand ) ;
if ( * operand )
{
auto dest = m_Owner . FindDestination ( operand ) ;
if ( ! dest )
{
m_Nickname = operand ;
std : : string msg ( " Nickname set to " ) ;
msg + = m_Nickname ;
SendReplyOK ( msg . c_str ( ) ) ;
}
else
SendReplyError ( " tunnel is active " ) ;
}
else
SendReplyError ( " no nickname has been set " ) ;
}
void BOBCommandSession : : GetNickCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: getnick " , operand ) ;
if ( * operand )
{
m_CurrentDestination = m_Owner . FindDestination ( operand ) ;
if ( m_CurrentDestination )
{
m_Keys = m_CurrentDestination - > GetKeys ( ) ;
m_IsActive = m_CurrentDestination - > IsRunning ( ) ;
m_Nickname = operand ;
}
if ( m_Nickname = = operand )
{
std : : string msg ( " Nickname set to " ) ;
msg + = m_Nickname ;
SendReplyOK ( msg . c_str ( ) ) ;
}
else
SendReplyError ( " no nickname has been set " ) ;
}
else
SendReplyError ( " no nickname has been set " ) ;
}
void BOBCommandSession : : NewkeysCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: newkeys " ) ;
i2p : : data : : SigningKeyType signatureType = i2p : : data : : SIGNING_KEY_TYPE_DSA_SHA1 ;
i2p : : data : : CryptoKeyType cryptoType = i2p : : data : : CRYPTO_KEY_TYPE_ELGAMAL ;
if ( * operand )
{
try
{
char * operand1 = ( char * ) strchr ( operand , ' ' ) ;
if ( operand1 )
{
* operand1 = 0 ; operand1 + + ;
cryptoType = std : : stoi ( operand1 ) ;
}
signatureType = std : : stoi ( operand ) ;
}
catch ( std : : invalid_argument & ex )
{
LogPrint ( eLogWarning , " BOB: Error on newkeys: " , ex . what ( ) ) ;
}
}
m_Keys = i2p : : data : : PrivateKeys : : CreateRandomKeys ( signatureType , cryptoType , true ) ;
SendReplyOK ( m_Keys . GetPublic ( ) - > ToBase64 ( ) . c_str ( ) ) ;
}
void BOBCommandSession : : SetkeysCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: setkeys " , operand ) ;
if ( * operand & & m_Keys . FromBase64 ( operand ) )
SendReplyOK ( m_Keys . GetPublic ( ) - > ToBase64 ( ) . c_str ( ) ) ;
else
SendReplyError ( " invalid keys " ) ;
}
void BOBCommandSession : : GetkeysCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: getkeys " ) ;
if ( m_Keys . GetPublic ( ) ) // keys are set ?
SendReplyOK ( m_Keys . ToBase64 ( ) . c_str ( ) ) ;
else
SendReplyError ( " keys are not set " ) ;
}
void BOBCommandSession : : GetdestCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: getdest " ) ;
if ( m_Keys . GetPublic ( ) ) // keys are set ?
SendReplyOK ( m_Keys . GetPublic ( ) - > ToBase64 ( ) . c_str ( ) ) ;
else
SendReplyError ( " keys are not set " ) ;
}
void BOBCommandSession : : OuthostCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: outhost " , operand ) ;
if ( * operand )
{
m_OutHost = operand ;
SendReplyOK ( " outhost set " ) ;
}
else
SendReplyError ( " empty outhost " ) ;
}
void BOBCommandSession : : OutportCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: outport " , operand ) ;
if ( * operand )
{
int port = std : : stoi ( operand ) ;
if ( port > = 0 & & port < 65536 )
{
m_OutPort = port ;
SendReplyOK ( " outbound port set " ) ;
}
else
SendReplyError ( " port out of range " ) ;
}
else
SendReplyError ( " empty outport " ) ;
}
void BOBCommandSession : : InhostCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: inhost " , operand ) ;
if ( * operand )
{
m_InHost = operand ;
SendReplyOK ( " inhost set " ) ;
}
else
SendReplyError ( " empty inhost " ) ;
}
void BOBCommandSession : : InportCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: inport " , operand ) ;
if ( * operand )
{
int port = std : : stoi ( operand ) ;
if ( port > = 0 & & port < 65536 )
{
m_InPort = port ;
SendReplyOK ( " inbound port set " ) ;
}
else
SendReplyError ( " port out of range " ) ;
}
else
SendReplyError ( " empty inport " ) ;
}
void BOBCommandSession : : QuietCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: quiet " ) ;
if ( m_Nickname . length ( ) > 0 )
{
if ( ! m_IsActive )
{
m_IsQuiet = true ;
SendReplyOK ( " Quiet set " ) ;
}
else
SendReplyError ( " tunnel is active " ) ;
}
else
SendReplyError ( " no nickname has been set " ) ;
}
void BOBCommandSession : : LookupCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: lookup " , operand ) ;
if ( * operand )
{
auto addr = context . GetAddressBook ( ) . GetAddress ( operand ) ;
if ( ! addr )
{
SendReplyError ( " Address Not found " ) ;
return ;
}
auto localDestination = ( m_CurrentDestination & & m_CurrentDestination - > IsRunning ( ) ) ?
m_CurrentDestination - > GetLocalDestination ( ) : i2p : : client : : context . GetSharedLocalDestination ( ) ;
if ( ! localDestination )
{
SendReplyError ( " No local destination " ) ;
return ;
}
if ( addr - > IsIdentHash ( ) )
{
// we might have leaseset already
auto leaseSet = localDestination - > FindLeaseSet ( addr - > identHash ) ;
if ( leaseSet )
{
SendReplyOK ( leaseSet - > GetIdentity ( ) - > ToBase64 ( ) . c_str ( ) ) ;
return ;
}
}
// trying to request
auto s = shared_from_this ( ) ;
auto requstCallback = [ s ] ( std : : shared_ptr < i2p : : data : : LeaseSet > ls )
{
if ( ls )
s - > SendReplyOK ( ls - > GetIdentity ( ) - > ToBase64 ( ) . c_str ( ) ) ;
else
s - > SendReplyError ( " LeaseSet Not found " ) ;
} ;
if ( addr - > IsIdentHash ( ) )
localDestination - > RequestDestination ( addr - > identHash , requstCallback ) ;
else
localDestination - > RequestDestinationWithEncryptedLeaseSet ( addr - > blindedPublicKey , requstCallback ) ;
}
else
SendReplyError ( " empty lookup address " ) ;
}
void BOBCommandSession : : LookupLocalCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: lookup local " , operand ) ;
if ( * operand )
{
auto addr = context . GetAddressBook ( ) . GetAddress ( operand ) ;
if ( ! addr )
{
SendReplyError ( " Address Not found " ) ;
return ;
}
auto ls = i2p : : data : : netdb . FindLeaseSet ( addr - > identHash ) ;
if ( ls )
SendReplyOK ( ls - > GetIdentity ( ) - > ToBase64 ( ) . c_str ( ) ) ;
else
SendReplyError ( " Local LeaseSet Not found " ) ;
}
else
SendReplyError ( " empty lookup address " ) ;
}
void BOBCommandSession : : ClearCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: clear " ) ;
m_Owner . DeleteDestination ( m_Nickname ) ;
m_Nickname = " " ;
SendReplyOK ( " cleared " ) ;
}
void BOBCommandSession : : ListCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: list " ) ;
std : : string statusLine ;
bool sentCurrent = false ;
const auto & destinations = m_Owner . GetDestinations ( ) ;
for ( const auto & it : destinations )
{
BuildStatusLine ( false , it . second , statusLine ) ;
SendRaw ( statusLine . c_str ( ) ) ;
if ( m_Nickname . compare ( it . second - > GetNickname ( ) ) = = 0 )
sentCurrent = true ;
}
if ( ! sentCurrent & & ! m_Nickname . empty ( ) )
{
// add the current tunnel to the list.
// this is for the incomplete tunnel which has not been started yet.
BuildStatusLine ( true , m_CurrentDestination , statusLine ) ;
SendRaw ( statusLine . c_str ( ) ) ;
}
SendReplyOK ( " Listing done " ) ;
}
void BOBCommandSession : : OptionCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: option " , operand ) ;
const char * value = strchr ( operand , ' = ' ) ;
if ( value )
{
std : : string msg ( " option " ) ;
* ( const_cast < char * > ( value ) ) = 0 ;
m_Options [ operand ] = value + 1 ;
msg + = operand ;
* ( const_cast < char * > ( value ) ) = ' = ' ;
msg + = " set to " ;
msg + = value + 1 ;
SendReplyOK ( msg . c_str ( ) ) ;
}
else
SendReplyError ( " malformed " ) ;
}
void BOBCommandSession : : StatusCommandHandler ( const char * operand , size_t len )
{
LogPrint ( eLogDebug , " BOB: status " , operand ) ;
const std : : string name = operand ;
std : : string statusLine ;
// always prefer destination
auto dest = m_Owner . FindDestination ( name ) ;
if ( dest )
{
// tunnel destination exists
BuildStatusLine ( false , dest , statusLine ) ;
SendReplyOK ( statusLine . c_str ( ) ) ;
}
else
{
if ( m_Nickname = = name & & ! name . empty ( ) )
{
// tunnel is incomplete / has not been started yet
BuildStatusLine ( true , nullptr , statusLine ) ;
SendReplyOK ( statusLine . c_str ( ) ) ;
}
else
{
SendReplyError ( " no nickname has been set " ) ;
}
}
}
void BOBCommandSession : : HelpCommandHandler ( const char * operand , size_t len )
{
auto helpStrings = m_Owner . GetHelpStrings ( ) ;
if ( ! * operand )
{
std : : stringstream ss ;
ss < < " COMMANDS: " ;
for ( auto const & x : helpStrings )
{
ss < < " " < < x . first ;
}
const std : : string & str = ss . str ( ) ;
SendReplyOK ( str . c_str ( ) ) ;
}
else
{
auto it = helpStrings . find ( operand ) ;
if ( it ! = helpStrings . end ( ) )
{
SendReplyOK ( it - > second . c_str ( ) ) ;
return ;
}
SendReplyError ( " No such command " ) ;
}
}
BOBCommandChannel : : BOBCommandChannel ( const std : : string & address , uint16_t port ) :
RunnableService ( " BOB " ) ,
m_Acceptor ( GetIOService ( ) , boost : : asio : : ip : : tcp : : endpoint ( boost : : asio : : ip : : make_address ( address ) , port ) )
{
// command -> handler
m_CommandHandlers [ BOB_COMMAND_ZAP ] = & BOBCommandSession : : ZapCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_QUIT ] = & BOBCommandSession : : QuitCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_START ] = & BOBCommandSession : : StartCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_STOP ] = & BOBCommandSession : : StopCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_SETNICK ] = & BOBCommandSession : : SetNickCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_GETNICK ] = & BOBCommandSession : : GetNickCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_NEWKEYS ] = & BOBCommandSession : : NewkeysCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_GETKEYS ] = & BOBCommandSession : : GetkeysCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_SETKEYS ] = & BOBCommandSession : : SetkeysCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_GETDEST ] = & BOBCommandSession : : GetdestCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_OUTHOST ] = & BOBCommandSession : : OuthostCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_OUTPORT ] = & BOBCommandSession : : OutportCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_INHOST ] = & BOBCommandSession : : InhostCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_INPORT ] = & BOBCommandSession : : InportCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_QUIET ] = & BOBCommandSession : : QuietCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_LOOKUP ] = & BOBCommandSession : : LookupCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_LOOKUP_LOCAL ] = & BOBCommandSession : : LookupLocalCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_CLEAR ] = & BOBCommandSession : : ClearCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_LIST ] = & BOBCommandSession : : ListCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_OPTION ] = & BOBCommandSession : : OptionCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_STATUS ] = & BOBCommandSession : : StatusCommandHandler ;
m_CommandHandlers [ BOB_COMMAND_HELP ] = & BOBCommandSession : : HelpCommandHandler ;
// command -> help string
m_HelpStrings [ BOB_COMMAND_ZAP ] = BOB_HELP_ZAP ;
m_HelpStrings [ BOB_COMMAND_QUIT ] = BOB_HELP_QUIT ;
m_HelpStrings [ BOB_COMMAND_START ] = BOB_HELP_START ;
m_HelpStrings [ BOB_COMMAND_STOP ] = BOB_HELP_STOP ;
m_HelpStrings [ BOB_COMMAND_SETNICK ] = BOB_HELP_SETNICK ;
m_HelpStrings [ BOB_COMMAND_GETNICK ] = BOB_HELP_GETNICK ;
m_HelpStrings [ BOB_COMMAND_NEWKEYS ] = BOB_HELP_NEWKEYS ;
m_HelpStrings [ BOB_COMMAND_GETKEYS ] = BOB_HELP_GETKEYS ;
m_HelpStrings [ BOB_COMMAND_SETKEYS ] = BOB_HELP_SETKEYS ;
m_HelpStrings [ BOB_COMMAND_GETDEST ] = BOB_HELP_GETDEST ;
m_HelpStrings [ BOB_COMMAND_OUTHOST ] = BOB_HELP_OUTHOST ;
m_HelpStrings [ BOB_COMMAND_OUTPORT ] = BOB_HELP_OUTPORT ;
m_HelpStrings [ BOB_COMMAND_INHOST ] = BOB_HELP_INHOST ;
m_HelpStrings [ BOB_COMMAND_INPORT ] = BOB_HELP_INPORT ;
m_HelpStrings [ BOB_COMMAND_QUIET ] = BOB_HELP_QUIET ;
m_HelpStrings [ BOB_COMMAND_LOOKUP ] = BOB_HELP_LOOKUP ;
m_HelpStrings [ BOB_COMMAND_CLEAR ] = BOB_HELP_CLEAR ;
m_HelpStrings [ BOB_COMMAND_LIST ] = BOB_HELP_LIST ;
m_HelpStrings [ BOB_COMMAND_OPTION ] = BOB_HELP_OPTION ;
m_HelpStrings [ BOB_COMMAND_STATUS ] = BOB_HELP_STATUS ;
m_HelpStrings [ BOB_COMMAND_HELP ] = BOB_HELP_HELP ;
}
BOBCommandChannel : : ~ BOBCommandChannel ( )
{
if ( IsRunning ( ) )
Stop ( ) ;
}
void BOBCommandChannel : : Start ( )
{
Accept ( ) ;
StartIOService ( ) ;
}
void BOBCommandChannel : : Stop ( )
{
for ( auto & it : m_Destinations )
it . second - > Stop ( ) ;
m_Acceptor . cancel ( ) ;
StopIOService ( ) ;
}
void BOBCommandChannel : : AddDestination ( const std : : string & name , std : : shared_ptr < BOBDestination > dest )
{
m_Destinations . emplace ( name , dest ) ;
}
void BOBCommandChannel : : DeleteDestination ( const std : : string & name )
{
auto it = m_Destinations . find ( name ) ;
if ( it ! = m_Destinations . end ( ) )
{
it - > second - > Stop ( ) ;
m_Destinations . erase ( it ) ;
}
}
std : : shared_ptr < BOBDestination > BOBCommandChannel : : FindDestination ( const std : : string & name )
{
auto it = m_Destinations . find ( name ) ;
if ( it ! = m_Destinations . end ( ) )
return it - > second ;
return nullptr ;
}
void BOBCommandChannel : : Accept ( )
{
auto newSession = std : : make_shared < BOBCommandSession > ( * this ) ;
m_Acceptor . async_accept ( newSession - > GetSocket ( ) , std : : bind ( & BOBCommandChannel : : HandleAccept , this ,
std : : placeholders : : _1 , newSession ) ) ;
}
void BOBCommandChannel : : HandleAccept ( const boost : : system : : error_code & ecode , std : : shared_ptr < BOBCommandSession > session )
{
if ( ecode ! = boost : : asio : : error : : operation_aborted )
Accept ( ) ;
if ( ! ecode )
{
LogPrint ( eLogInfo , " BOB: New command connection from " , session - > GetSocket ( ) . remote_endpoint ( ) ) ;
session - > SendVersion ( ) ;
}
else
LogPrint ( eLogError , " BOB: Accept error: " , ecode . message ( ) ) ;
}
}
}