@ -34,45 +34,26 @@ namespace proxy {
@@ -34,45 +34,26 @@ namespace proxy {
class HTTPReqHandler : public i2p : : client : : I2PServiceHandler , public std : : enable_shared_from_this < HTTPReqHandler >
{
private :
enum state
{
GET_METHOD ,
GET_HOSTNAME ,
GET_HTTPV ,
GET_HTTPVNL , //TODO: fallback to finding HOst: header if needed
DONE
} ;
void EnterState ( state nstate ) ;
bool HandleData ( uint8_t * http_buff , std : : size_t len ) ;
bool HandleRequest ( std : : size_t len ) ;
void HandleSockRecv ( const boost : : system : : error_code & ecode , std : : size_t bytes_transfered ) ;
void Terminate ( ) ;
void AsyncSockRead ( ) ;
void HTTPRequestFailed ( const char * message ) ;
void RedirectToJumpService ( std : : string & host ) ;
bool ValidateHTTPRequest ( ) ;
bool ExtractAddressHelper ( i2p : : http : : URL & url , std : : string & b64 ) ;
bool CreateHTTPRequest ( uint8_t * http_buff , std : : size_t len ) ;
void SanitizeHTTPRequest ( i2p : : http : : HTTPReq & req ) ;
void SentHTTPFailed ( const boost : : system : : error_code & ecode ) ;
void HandleStreamRequestComplete ( std : : shared_ptr < i2p : : stream : : Stream > stream ) ;
uint8_t m_downstream_recv_buf [ 8192 ] ;
std : : shared_ptr < boost : : asio : : ip : : tcp : : socket > m_sock ;
std : : string m_request ; //Data left to be sent
std : : string m_Response ;
std : : string m_url ; //URL
std : : string m_method ; //Method
std : : string m_version ; //HTTP version
std : : string m_address ; //Address
std : : string m_path ; //Path
int m_port ; //Port
state m_state ; //Parsing state
std : : vector < unsigned char > m_recv_buf ; /* as "downstream recieve buffer", from client to me */
std : : vector < unsigned char > m_send_buf ; /* as "upstream send buffer", from me to remote host */
public :
HTTPReqHandler ( HTTPProxy * parent , std : : shared_ptr < boost : : asio : : ip : : tcp : : socket > sock ) :
I2PServiceHandler ( parent ) , m_sock ( sock )
{ EnterState ( GET_METHOD ) ; }
I2PServiceHandler ( parent ) , m_sock ( sock ) , m_recv_buf ( 8192 ) , m_send_buf ( 0 ) { } ;
~ HTTPReqHandler ( ) { Terminate ( ) ; }
void Handle ( ) { AsyncSockRead ( ) ; }
} ;
@ -84,7 +65,7 @@ namespace proxy {
@@ -84,7 +65,7 @@ namespace proxy {
LogPrint ( eLogError , " HTTPProxy: no socket for read " ) ;
return ;
}
m_sock - > async_receive ( boost : : asio : : buffer ( m_downstream_ recv_buf , sizeof ( m_downstream_recv_buf ) ) ,
m_sock - > async_receive ( boost : : asio : : buffer ( m_recv_buf ) ,
std : : bind ( & HTTPReqHandler : : HandleSockRecv , shared_from_this ( ) ,
std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
}
@ -100,8 +81,6 @@ namespace proxy {
@@ -100,8 +81,6 @@ namespace proxy {
Done ( shared_from_this ( ) ) ;
}
/* All hope is lost beyond this point */
//TODO: handle this apropriately
void HTTPReqHandler : : HTTPRequestFailed ( const char * message )
{
i2p : : http : : HTTPRes res ;
@ -136,22 +115,6 @@ namespace proxy {
@@ -136,22 +115,6 @@ namespace proxy {
std : : bind ( & HTTPReqHandler : : SentHTTPFailed , shared_from_this ( ) , std : : placeholders : : _1 ) ) ;
}
void HTTPReqHandler : : EnterState ( HTTPReqHandler : : state nstate )
{
m_state = nstate ;
}
bool HTTPReqHandler : : ValidateHTTPRequest ( )
{
if ( m_version ! = " HTTP/1.0 " & & m_version ! = " HTTP/1.1 " )
{
LogPrint ( eLogError , " HTTPProxy: unsupported version: " , m_version ) ;
HTTPRequestFailed ( " unsupported HTTP version " ) ;
return false ;
}
return true ;
}
bool HTTPReqHandler : : ExtractAddressHelper ( i2p : : http : : URL & url , std : : string & b64 )
{
const char * param = " i2paddresshelper= " ;
@ -171,118 +134,74 @@ namespace proxy {
@@ -171,118 +134,74 @@ namespace proxy {
return true ;
}
bool HTTPReqHandler : : CreateHTTPRequest ( uint8_t * http_buff , std : : size_t len )
void HTTPReqHandler : : SanitizeHTTPRequest ( i2p : : http : : HTTPReq & req )
{
std : : string b64 ;
req . del_header ( " Referer " ) ;
req . add_header ( " Connection " , " close " , true ) ;
req . add_header ( " User-Agent " , " MYOB/6.66 (AN/ON) " , true ) ;
}
/**
* @ param len length of data in m_recv_buf
* @ return true on processed request or false if more data needed
*/
bool HTTPReqHandler : : HandleRequest ( std : : size_t len )
{
i2p : : http : : HTTPReq req ;
i2p : : http : : URL url ;
url . parse ( m_url ) ;
m_address = url . host ; /* < compatibility */
m_port = url . port ; /* < compatibility */
m_path = url . path ; /* < compatibility */
if ( ! ValidateHTTPRequest ( ) ) return false ;
std : : string b64 ;
int req_len = 0 ;
req_len = req . parse ( ( const char * ) m_recv_buf . data ( ) , len ) ;
if ( req_len = = 0 )
return false ; /* need more data */
if ( req_len < 0 ) {
LogPrint ( eLogError , " HTTPProxy: unable to parse request " ) ;
HTTPRequestFailed ( " invalid request " ) ;
return true ; /* parse error */
}
/* parsing success, now let's look inside request */
LogPrint ( eLogDebug , " HTTPProxy: requested: " , req . uri ) ;
url . parse ( req . uri ) ;
/* TODO: notify user */
if ( ExtractAddressHelper ( url , b64 ) ) {
i2p : : client : : context . GetAddressBook ( ) . InsertAddress ( url . host , b64 ) ;
LogPrint ( eLogInfo , " HTTPProxy: added b64 from addresshelper for " , url . host , " to address book " ) ;
std : : string message = " added b64 from addresshelper for " + url . host + " to address book " ;
LogPrint ( eLogInfo , " HTTPProxy: " , message ) ;
message + = " , please reload page " ;
HTTPRequestFailed ( message . c_str ( ) ) ;
return true ; /* request processed */
}
i2p : : data : : IdentHash identHash ;
if ( str_rmatch ( url . host , " .i2p " ) )
{
if ( str_rmatch ( url . host , " .i2p " ) ) {
if ( ! i2p : : client : : context . GetAddressBook ( ) . GetIdentHash ( url . host , identHash ) ) {
RedirectToJumpService ( url . host ) ;
return false ;
}
}
m_request = m_method ;
m_request . push_back ( ' ' ) ;
m_request + = m_path ;
m_request . push_back ( ' ' ) ;
m_request + = m_version ;
m_request . push_back ( ' \r ' ) ;
m_request . push_back ( ' \n ' ) ;
m_request . append ( " Connection: close \r \n " ) ;
// TODO: temporary shortcut. Must be implemented properly
uint8_t * eol = nullptr ;
bool isEndOfHeader = false ;
while ( ! isEndOfHeader & & len & & ( eol = ( uint8_t * ) memchr ( http_buff , ' \r ' , len ) ) )
{
if ( eol )
{
* eol = 0 ; eol + + ;
if ( strncmp ( ( const char * ) http_buff , " Referer " , 7 ) & & strncmp ( ( const char * ) http_buff , " Connection " , 10 ) ) // strip out referer and connection
{
if ( ! strncmp ( ( const char * ) http_buff , " User-Agent " , 10 ) ) // replace UserAgent
m_request . append ( " User-Agent: MYOB/6.66 (AN/ON) " ) ;
else
m_request . append ( ( const char * ) http_buff ) ;
m_request . append ( " \r \n " ) ;
return true ; /* request processed */
}
isEndOfHeader = ! http_buff [ 0 ] ;
auto l = eol - http_buff ;
http_buff = eol ;
len - = l ;
if ( len > 0 ) // \r
{
http_buff + + ;
len - - ;
}
}
}
m_request . append ( reinterpret_cast < const char * > ( http_buff ) , len ) ;
/* TODO: outproxy handler here */
} else {
std : : string message = " Host " + url . host + " not inside i2p network, but outproxy support still missing " ;
HTTPRequestFailed ( message . c_str ( ) ) ;
LogPrint ( eLogWarning , " HTTPProxy: " , message ) ;
return true ;
}
SanitizeHTTPRequest ( req ) ;
/* drop original request from input buffer */
m_recv_buf . erase ( m_recv_buf . begin ( ) , m_recv_buf . begin ( ) + req_len ) ;
/* build new buffer from modified request and data from original request */
std : : string request = req . to_string ( ) ;
m_send_buf . assign ( request . begin ( ) , request . end ( ) ) ;
m_send_buf . insert ( m_send_buf . end ( ) , m_recv_buf . begin ( ) , m_recv_buf . end ( ) ) ;
/* connect to destination */
GetOwner ( ) - > CreateStream ( std : : bind ( & HTTPReqHandler : : HandleStreamRequestComplete ,
shared_from_this ( ) , std : : placeholders : : _1 ) , url . host , url . port ) ;
bool HTTPReqHandler : : HandleData ( uint8_t * http_buff , std : : size_t len )
{
while ( len > 0 )
{
//TODO: fallback to finding HOst: header if needed
switch ( m_state )
{
case GET_METHOD :
switch ( * http_buff )
{
case ' ' : EnterState ( GET_HOSTNAME ) ; break ;
default : m_method . push_back ( * http_buff ) ; break ;
}
break ;
case GET_HOSTNAME :
switch ( * http_buff )
{
case ' ' : EnterState ( GET_HTTPV ) ; break ;
default : m_url . push_back ( * http_buff ) ; break ;
}
break ;
case GET_HTTPV :
switch ( * http_buff )
{
case ' \r ' : EnterState ( GET_HTTPVNL ) ; break ;
default : m_version . push_back ( * http_buff ) ; break ;
}
break ;
case GET_HTTPVNL :
switch ( * http_buff )
{
case ' \n ' : EnterState ( DONE ) ; break ;
default :
LogPrint ( eLogError , " HTTPProxy: rejected invalid request ending with: " , ( ( int ) * http_buff ) ) ;
HTTPRequestFailed ( " rejected invalid request " ) ;
return false ;
}
break ;
default :
LogPrint ( eLogError , " HTTPProxy: invalid state: " , m_state ) ;
HTTPRequestFailed ( " invalid parser state " ) ;
return false ;
}
http_buff + + ;
len - - ;
if ( m_state = = DONE )
return CreateHTTPRequest ( http_buff , len ) ;
}
return true ;
}
@ -296,18 +215,10 @@ namespace proxy {
@@ -296,18 +215,10 @@ namespace proxy {
return ;
}
if ( HandleData ( m_downstream_recv_buf , len ) )
{
if ( m_state = = DONE )
{
LogPrint ( eLogDebug , " HTTPProxy: requested: " , m_url ) ;
GetOwner ( ) - > CreateStream ( std : : bind ( & HTTPReqHandler : : HandleStreamRequestComplete ,
shared_from_this ( ) , std : : placeholders : : _1 ) , m_address , m_port ) ;
}
else
if ( HandleRequest ( len ) )
return ; /* request processed */
AsyncSockRead ( ) ;
}
}
void HTTPReqHandler : : SentHTTPFailed ( const boost : : system : : error_code & ecode )
{
@ -328,7 +239,7 @@ namespace proxy {
@@ -328,7 +239,7 @@ namespace proxy {
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 ( ) ) ;
connection - > I2PConnect ( m_send_buf . data ( ) , m_send_buf . size ( ) ) ;
Done ( shared_from_this ( ) ) ;
}