From d0ffaab339b6aa21d5f4ec4be2b71c7e5e051ecb Mon Sep 17 00:00:00 2001 From: hagen Date: Fri, 27 May 2016 00:00:00 +0000 Subject: [PATCH] * HTTPProxy: * use new http classes instead homemade parser * proper error handling for "address not found", "addresshelper" and "not .i2p domain" cases * use std::vector instead uint8_t[] for buffers * general code cleanup --- HTTPProxy.cpp | 221 +++++++++++++++----------------------------------- 1 file changed, 66 insertions(+), 155 deletions(-) diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index 43ee8631..cc534470 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -34,45 +34,26 @@ namespace proxy { class HTTPReqHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this { 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 stream); - uint8_t m_downstream_recv_buf[8192]; std::shared_ptr 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 m_recv_buf; /* as "downstream recieve buffer", from client to me */ + std::vector m_send_buf; /* as "upstream send buffer", from me to remote host */ public: HTTPReqHandler(HTTPProxy * parent, std::shared_ptr 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 { 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 { 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 { 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 { 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 (!i2p::client::context.GetAddressBook ().GetIdentHash (url.host, identHash)){ + if (str_rmatch(url.host, ".i2p")) { + if (!i2p::client::context.GetAddressBook ().GetIdentHash (url.host, identHash)) { RedirectToJumpService(url.host); - return false; + return true; /* request processed */ } + /* 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); - 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"); - } - 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(http_buff),len); - return true; - } + /* 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,17 +215,9 @@ 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 - AsyncSockRead(); - } + if (HandleRequest(len)) + return; /* request processed */ + AsyncSockRead(); } void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code & ecode) @@ -328,7 +239,7 @@ namespace proxy { LogPrint (eLogDebug, "HTTPProxy: New I2PTunnel connection"); auto connection = std::make_shared(GetOwner(), m_sock, stream); GetOwner()->AddHandler (connection); - connection->I2PConnect (reinterpret_cast(m_request.data()), m_request.size()); + connection->I2PConnect (m_send_buf.data(), m_send_buf.size()); Done (shared_from_this()); }