@ -3,6 +3,13 @@
@@ -3,6 +3,13 @@
# include <boost/lexical_cast.hpp>
# include <string>
# include <atomic>
# include <memory>
# include <set>
# include <boost/asio.hpp>
# include <mutex>
# include "I2PService.h"
# include "Destination.h"
# include "HTTPProxy.h"
# include "util.h"
# include "Identity.h"
@ -14,12 +21,19 @@
@@ -14,12 +21,19 @@
# include "Config.h"
# include "HTTP.h"
namespace i2p
{
namespace proxy
{
namespace i2p {
namespace proxy {
bool str_rmatch ( std : : string & str , const char * suffix ) {
auto pos = str . rfind ( suffix ) ;
if ( pos = = std : : string : : npos )
return false ; /* not found */
if ( str . length ( ) = = ( pos + std : : strlen ( suffix ) ) )
return true ; /* match */
return false ;
}
static const size_t http_buffer_size = 8192 ;
class HTTPProxyHandler : public i2p : : client : : I2PServiceHandler , public std : : enable_shared_from_this < HTTPProxyHandler >
class HTTPReq Handler : public i2p : : client : : I2PServiceHandler , public std : : enable_shared_from_this < HTTPReqHandler >
{
private :
enum state
@ -36,10 +50,9 @@ namespace proxy
@@ -36,10 +50,9 @@ namespace proxy
void HandleSockRecv ( const boost : : system : : error_code & ecode , std : : size_t bytes_transfered ) ;
void Terminate ( ) ;
void AsyncSockRead ( ) ;
void HTTPRequestFailed ( /*std::string message*/ ) ;
void RedirectToJumpService ( ) ;
void HTTPRequestFailed ( const char * message ) ;
void RedirectToJumpService ( std : : string & host ) ;
void ExtractRequest ( ) ;
bool IsI2PAddress ( ) ;
bool ValidateHTTPRequest ( ) ;
void HandleJumpServices ( ) ;
bool CreateHTTPRequest ( uint8_t * http_buff , std : : size_t len ) ;
@ -59,26 +72,26 @@ namespace proxy
@@ -59,26 +72,26 @@ namespace proxy
public :
HTTPProxy Handler ( HTTPProxyServer * parent , std : : shared_ptr < boost : : asio : : ip : : tcp : : socket > sock ) :
HTTPReq Handler ( HTTPProxy * parent , std : : shared_ptr < boost : : asio : : ip : : tcp : : socket > sock ) :
I2PServiceHandler ( parent ) , m_sock ( sock )
{ EnterState ( GET_METHOD ) ; }
~ HTTPProxy Handler ( ) { Terminate ( ) ; }
~ HTTPReq Handler ( ) { Terminate ( ) ; }
void Handle ( ) { AsyncSockRead ( ) ; }
} ;
void HTTPProxy Handler : : AsyncSockRead ( )
void HTTPReq Handler : : AsyncSockRead ( )
{
LogPrint ( eLogDebug , " HTTPProxy: async sock read " ) ;
if ( m_sock ) {
m_sock - > async_receive ( boost : : asio : : buffer ( m_http_buff , http_buffer_size ) ,
std : : bind ( & HTTPProxyHandler : : HandleSockRecv , shared_from_this ( ) ,
std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
} else {
if ( ! m_sock ) {
LogPrint ( eLogError , " HTTPProxy: no socket for read " ) ;
return ;
}
m_sock - > async_receive ( boost : : asio : : buffer ( m_http_buff , http_buffer_size ) ,
std : : bind ( & HTTPReqHandler : : HandleSockRecv , shared_from_this ( ) ,
std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
}
void HTTPProxyHandler : : Terminate ( ) {
void HTTPReq Handler : : Terminate ( ) {
if ( Kill ( ) ) return ;
if ( m_sock )
{
@ -91,30 +104,45 @@ namespace proxy
@@ -91,30 +104,45 @@ namespace proxy
/* All hope is lost beyond this point */
//TODO: handle this apropriately
void HTTPProxy Handler : : HTTPRequestFailed ( /*HTTPProxyHandler::errTypes error*/ )
void HTTPReq Handler : : HTTPRequestFailed ( const char * message )
{
static std : : string response = " HTTP/1.0 500 Internal Server Error \r \n Content-type: text/html \r \n Content-length: 0 \r \n \r \n " ;
boost : : asio : : async_write ( * m_sock , boost : : asio : : buffer ( response , response . size ( ) ) ,
std : : bind ( & HTTPProxyHandler : : SentHTTPFailed , shared_from_this ( ) , std : : placeholders : : _1 ) ) ;
i2p : : http : : HTTPRes res ;
res . code = 500 ;
res . add_header ( " Content-Type " , " text/plain " ) ;
res . add_header ( " Connection " , " close " ) ;
res . body = message ;
res . body + = " \r \n " ;
std : : string response = res . to_string ( ) ;
boost : : asio : : async_write ( * m_sock , boost : : asio : : buffer ( response ) ,
std : : bind ( & HTTPReqHandler : : SentHTTPFailed , shared_from_this ( ) , std : : placeholders : : _1 ) ) ;
}
void HTTPProxyHandler : : RedirectToJumpService ( /*HTTPProxyHandler::errTypes error*/ )
void HTTPReq Handler : : RedirectToJumpService ( std : : string & host )
{
std : : stringstream response ;
std : : string httpAddr ; i2p : : config : : GetOption ( " http.address " , httpAddr ) ;
uint16_t httpPort ; i2p : : config : : GetOption ( " http.port " , httpPort ) ;
i2p : : http : : HTTPRes res ;
i2p : : http : : URL url ;
/* TODO: don't redirect to webconsole, it's not always work, handle jumpservices here */
i2p : : config : : GetOption ( " http.address " , url . host ) ;
i2p : : config : : GetOption ( " http.port " , url . port ) ;
url . path = " / " ;
url . query = " page=jumpservices&address= " ;
url . query + = host ;
res . code = 302 ; /* redirect */
res . add_header ( " Location " , url . to_string ( ) . c_str ( ) ) ;
response < < " HTTP/1.1 302 Found \r \n Location: http:// " < < httpAddr < < " : " < < httpPort < < " /?page=jumpservices&address= " < < m_address < < " \r \n \r \n " ;
boost : : asio : : async_write ( * m_sock , boost : : asio : : buffer ( response . str ( ) , response . str ( ) . length ( ) ) ,
std : : bind ( & HTTPProxyHandler : : SentHTTPFailed , shared_from_this ( ) , std : : placeholders : : _1 ) ) ;
std : : string response = res . to_string ( ) ;
boost : : asio : : async_write ( * m_sock , boost : : asio : : buffer ( response ) ,
std : : bind ( & HTTPReq Handler : : SentHTTPFailed , shared_from_this ( ) , std : : placeholders : : _1 ) ) ;
}
void HTTPProxyHandler : : EnterState ( HTTPProxyHandler : : state nstate )
void HTTPReq Handler : : EnterState ( HTTPReq Handler : : state nstate )
{
m_state = nstate ;
}
void HTTPProxy Handler : : ExtractRequest ( )
void HTTPReq Handler : : ExtractRequest ( )
{
LogPrint ( eLogDebug , " HTTPProxy: request: " , m_method , " " , m_url ) ;
i2p : : http : : URL url ;
@ -127,18 +155,18 @@ namespace proxy
@@ -127,18 +155,18 @@ namespace proxy
LogPrint ( eLogDebug , " HTTPProxy: server: " , m_address , " , port: " , m_port , " , path: " , m_path ) ;
}
bool HTTPProxy Handler : : ValidateHTTPRequest ( )
bool HTTPReq Handler : : ValidateHTTPRequest ( )
{
if ( m_version ! = " HTTP/1.0 " & & m_version ! = " HTTP/1.1 " )
{
LogPrint ( eLogError , " HTTPProxy: unsupported version: " , m_version ) ;
HTTPRequestFailed ( ) ; //TODO: send right stuff
HTTPRequestFailed ( " unsupported HTTP version " ) ;
return false ;
}
return true ;
}
void HTTPProxy Handler : : HandleJumpServices ( )
void HTTPReq Handler : : HandleJumpServices ( )
{
static const char * helpermark1 = " ?i2paddresshelper= " ;
static const char * helpermark2 = " &i2paddresshelper= " ;
@ -170,32 +198,21 @@ namespace proxy
@@ -170,32 +198,21 @@ namespace proxy
m_path . erase ( addressHelperPos ) ;
}
bool HTTPProxyHandler : : IsI2PAddress ( )
{
auto pos = m_address . rfind ( " .i2p " ) ;
if ( pos ! = std : : string : : npos & & ( pos + 4 ) = = m_address . length ( ) )
{
return true ;
}
return false ;
}
bool HTTPProxyHandler : : CreateHTTPRequest ( uint8_t * http_buff , std : : size_t len )
bool HTTPReqHandler : : CreateHTTPRequest ( uint8_t * http_buff , std : : size_t len )
{
ExtractRequest ( ) ; //TODO: parse earlier
if ( ! ValidateHTTPRequest ( ) ) return false ;
HandleJumpServices ( ) ;
i2p : : data : : IdentHash identHash ;
if ( IsI2PAddress ( ) )
if ( str_rmatch ( m_address , " .i2p " ) )
{
if ( ! i2p : : client : : context . GetAddressBook ( ) . GetIdentHash ( m_address , identHash ) ) {
RedirectToJumpService ( ) ;
RedirectToJumpService ( m_address ) ;
return false ;
}
}
m_request = m_method ;
m_request . push_back ( ' ' ) ;
m_request + = m_path ;
@ -235,7 +252,7 @@ namespace proxy
@@ -235,7 +252,7 @@ namespace proxy
return true ;
}
bool HTTPProxy Handler : : HandleData ( uint8_t * http_buff , std : : size_t len )
bool HTTPReq Handler : : HandleData ( uint8_t * http_buff , std : : size_t len )
{
while ( len > 0 )
{
@ -269,13 +286,13 @@ namespace proxy
@@ -269,13 +286,13 @@ namespace proxy
case ' \n ' : EnterState ( DONE ) ; break ;
default :
LogPrint ( eLogError , " HTTPProxy: rejected invalid request ending with: " , ( ( int ) * http_buff ) ) ;
HTTPRequestFailed ( ) ; //TODO: add correct code
HTTPRequestFailed ( " rejected invalid request " ) ;
return false ;
}
break ;
default :
LogPrint ( eLogError , " HTTPProxy: invalid state: " , m_state ) ;
HTTPRequestFailed ( ) ; //TODO: add correct code 500
HTTPRequestFailed ( " invalid parser state " ) ;
return false ;
}
http_buff + + ;
@ -286,7 +303,7 @@ namespace proxy
@@ -286,7 +303,7 @@ namespace proxy
return true ;
}
void HTTPProxy Handler : : HandleSockRecv ( const boost : : system : : error_code & ecode , std : : size_t len )
void HTTPReq Handler : : HandleSockRecv ( const boost : : system : : error_code & ecode , std : : size_t len )
{
LogPrint ( eLogDebug , " HTTPProxy: sock recv: " , len , " bytes " ) ;
if ( ecode )
@ -301,49 +318,45 @@ namespace proxy
@@ -301,49 +318,45 @@ namespace proxy
if ( m_state = = DONE )
{
LogPrint ( eLogDebug , " HTTPProxy: requested: " , m_url ) ;
GetOwner ( ) - > CreateStream ( std : : bind ( & HTTPProxy Handler : : HandleStreamRequestComplete ,
GetOwner ( ) - > CreateStream ( std : : bind ( & HTTPReq Handler : : HandleStreamRequestComplete ,
shared_from_this ( ) , std : : placeholders : : _1 ) , m_address , m_port ) ;
}
else
AsyncSockRead ( ) ;
}
}
void HTTPProxy Handler : : SentHTTPFailed ( const boost : : system : : error_code & ecode )
void HTTPReq Handler : : SentHTTPFailed ( const boost : : system : : error_code & ecode )
{
if ( ecode )
LogPrint ( eLogError , " HTTPProxy: Closing socket after sending failure because: " , ecode . message ( ) ) ;
Terminate ( ) ;
}
void HTTPProxy Handler : : HandleStreamRequestComplete ( std : : shared_ptr < i2p : : stream : : Stream > stream )
void HTTPReq Handler : : HandleStreamRequestComplete ( std : : shared_ptr < i2p : : stream : : Stream > stream )
{
if ( stream )
{
if ( Kill ( ) ) return ;
LogPrint ( eLogInfo , " HTTPProxy: New I2PTunnel connection " ) ;
auto connection = std : : make_shared < i2p : : client : : I2PTunnelConnection > ( GetOwner ( ) , m_sock , stream ) ;
GetOwner ( ) - > AddHandler ( connection ) ;
connection - > I2PConnect ( reinterpret_cast < const uint8_t * > ( m_request . data ( ) ) , m_request . size ( ) ) ;
Done ( shared_from_this ( ) ) ;
}
else
{
if ( ! stream ) {
LogPrint ( eLogError , " HTTPProxy: error when creating the stream, check the previous warnings for more info " ) ;
HTTPRequestFailed ( ) ; // TODO: Send correct error message host unreachable
HTTPRequestFailed ( " error when creating the stream, check logs " ) ;
return ;
}
if ( Kill ( ) )
return ;
LogPrint ( eLogDebug , " HTTPProxy: New I2PTunnel connection " ) ;
auto connection = std : : make_shared < i2p : : client : : I2PTunnelConnection > ( GetOwner ( ) , m_sock , stream ) ;
GetOwner ( ) - > AddHandler ( connection ) ;
connection - > I2PConnect ( reinterpret_cast < const uint8_t * > ( m_request . data ( ) ) , m_request . size ( ) ) ;
Done ( shared_from_this ( ) ) ;
}
HTTPProxyServer : : HTTPProxyServer ( const std : : string & address , int port , std : : shared_ptr < i2p : : client : : ClientDestination > localDestination ) :
HTTPProxy : : HTTPProxy ( const std : : string & address , int port , std : : shared_ptr < i2p : : client : : ClientDestination > localDestination ) :
TCPIPAcceptor ( address , port , localDestination ? localDestination : i2p : : client : : context . GetSharedLocalDestination ( ) )
{
}
std : : shared_ptr < i2p : : client : : I2PServiceHandler > HTTPProxyServer : : CreateHandler ( std : : shared_ptr < boost : : asio : : ip : : tcp : : socket > socket )
std : : shared_ptr < i2p : : client : : I2PServiceHandler > HTTPProxy : : CreateHandler ( std : : shared_ptr < boost : : asio : : ip : : tcp : : socket > socket )
{
return std : : make_shared < HTTPProxy Handler > ( this , socket ) ;
return std : : make_shared < HTTPReq Handler > ( this , socket ) ;
}
}
}
} // http
} // i2p