From d0a8cc933b8f80e03b036662a5a3fd3805879153 Mon Sep 17 00:00:00 2001 From: Meeh Date: Mon, 17 Mar 2014 23:31:29 +0100 Subject: [PATCH 1/3] Adding a simple GET HTTP Proxy --- HTTPProxy.cpp | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++ HTTPProxy.h | 91 +++++++++++++++++ Makefile | 2 +- README.md | 2 +- i2p.cpp | 4 + 5 files changed, 366 insertions(+), 2 deletions(-) create mode 100644 HTTPProxy.cpp create mode 100644 HTTPProxy.h diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp new file mode 100644 index 00000000..60500a75 --- /dev/null +++ b/HTTPProxy.cpp @@ -0,0 +1,269 @@ +#include +#include + +#include "base64.h" +#include "Log.h" +#include "Tunnel.h" +#include "TransitTunnel.h" +#include "Transports.h" +#include "NetDb.h" +#include "Streaming.h" +#include "HTTPProxy.h" + +namespace i2p +{ +namespace proxy +{ + namespace misc_strings + { + + const char name_value_separator[] = { ':', ' ' }; + const char crlf[] = { '\r', '\n' }; + + } // namespace misc_strings + + std::vector HTTPConnection::reply::to_buffers() + { + std::vector buffers; + if (headers.size () > 0) + { + buffers.push_back (boost::asio::buffer ("HTTP/1.0 200 OK\r\n")); // always OK + for (std::size_t i = 0; i < headers.size(); ++i) + { + header& h = headers[i]; + buffers.push_back(boost::asio::buffer(h.name)); + buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator)); + buffers.push_back(boost::asio::buffer(h.value)); + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + } + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + } + buffers.push_back(boost::asio::buffer(content)); + return buffers; + } + + void HTTPConnection::Terminate () + { + m_Socket->close (); + delete this; + } + + void HTTPConnection::Receive () + { + m_Socket->async_read_some (boost::asio::buffer (m_Buffer, 8192), + boost::bind(&HTTPConnection::HandleReceive, this, + boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + } + + void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (!ecode) + { + m_Buffer[bytes_transferred] = 0; + + std::pair requestInfo = ExtractRequest (); + request m_Request; + parseHeaders(m_Buffer, m_Request.headers); + + LogPrint("Requesting ", requestInfo.first, " with path ", requestInfo.second); + HandleDestinationRequest (requestInfo.first, requestInfo.second); + + boost::asio::async_write (*m_Socket, m_Reply.to_buffers(), + boost::bind (&HTTPConnection::HandleWrite, this, + boost::asio::placeholders::error)); + //Receive (); + } + else if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + +void HTTPConnection::parseHeaders(const std::string& h, std::vector
& hm) { + std::string str(h); + std::string::size_type idx; + std::string t; + int i = 0; + while((idx=str.find("\r\n")) != std::string::npos) { + t=str.substr(0,idx); + str.erase(0,idx+2); + if(t == "") + break; + idx=t.find(": "); + if(idx == std::string::npos) { + std::cout << "Bad header line: " << t << std::endl; + break; + } + LogPrint ("Name: ", t.substr (0,idx), " Value: ", t.substr (idx+2)); + hm[i].name = t.substr(0,idx); + hm[i].value = t.substr(idx+2); + i++; + } +} + +// TODO: Support other requests than GET. +std::pair HTTPConnection::ExtractRequest () +{ + char * get = strstr (m_Buffer, "GET"); + if (get) + { + char * http = strstr (get, "HTTP"); + if (http) + { + std::string url (get + 4, http - get - 5); + size_t sp = url.find_first_of( '/', 7 /* skip http:// part */ ); + if ( sp != std::string::npos ) + { + std::string base_url( url.begin()+7, url.begin()+sp ); + LogPrint("Base URL is: ", base_url); + if ( sp != std::string::npos ) + { + std::string query( url.begin()+sp+1, url.end() ); + LogPrint("Query is: ", "/" + query); + + return std::make_pair(base_url, "/" + query); + } + return std::make_pair(base_url, "/"); + } + } + } + return std::make_pair("",""); +} + + void HTTPConnection::HandleWrite (const boost::system::error_code& ecode) + { + Terminate (); + } + + void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& uri) + { + i2p::data::IdentHash destination; + std::string fullAddress; + if (address.find (".b32.i2p") != std::string::npos) + { + int li = address.find_first_of("."); + std::string newaddress = address.substr(0, li); + if (i2p::data::Base32ToByteStream (newaddress.c_str (), newaddress.length (), (uint8_t *)destination, 32) != 32) + { + LogPrint ("Invalid Base32 address ", newaddress); + return; + } + fullAddress = newaddress + ".b32.i2p"; + } + else + { + auto addr = i2p::data::netdb.FindAddress(address); + if (!addr) + { + LogPrint ("Unknown address ", address); + return; + } + destination = *addr; + fullAddress = address; + } + + auto leaseSet = i2p::data::netdb.FindLeaseSet (destination); + if (!leaseSet || !leaseSet->HasNonExpiredLeases ()) + { + i2p::data::netdb.Subscribe(destination); + std::this_thread::sleep_for (std::chrono::seconds(10)); // wait for 10 seconds + leaseSet = i2p::data::netdb.FindLeaseSet (destination); + if (!leaseSet || !leaseSet->HasNonExpiredLeases ()) // still no LeaseSet + { + m_Reply.content = leaseSet ? "Leases expired" : "LeaseSet not found"; + m_Reply.headers.resize(2); + m_Reply.headers[0].name = "Content-Length"; + m_Reply.headers[0].value = boost::lexical_cast(m_Reply.content.size()); + m_Reply.headers[1].name = "Content-Type"; + m_Reply.headers[1].value = "text/html"; + return; + } + } + auto s = i2p::stream::CreateStream (*leaseSet); + if (s) + { + std::string request = "GET " + uri + " HTTP/1.1\n Host:" + fullAddress + "\n"; + s->Send ((uint8_t *)request.c_str (), request.length (), 10); + std::stringstream ss; + uint8_t buf[8192]; + size_t r = s->Receive (buf, 8192, 30); // 30 seconds + if (!r && s->IsEstablished ()) // nothing received but connection is established + r = s->Receive (buf, 8192, 30); // wait for another 30 secondd + if (r) // we recieved data + { + ss << std::string ((char *)buf, r); + while (s->IsOpen () && (r = s->Receive (buf, 8192, 30)) > 0) + ss << std::string ((char *)buf,r); + + m_Reply.content = ss.str (); // send "as is" + m_Reply.headers.resize(0); // no headers + return; + } + else // nothing received + ss << "Not responding"; + s->Close (); + DeleteStream (s); + + m_Reply.content = ss.str (); + m_Reply.headers.resize(2); + m_Reply.headers[0].name = "Content-Length"; + m_Reply.headers[0].value = boost::lexical_cast(m_Reply.content.size()); + m_Reply.headers[1].name = "Content-Type"; + m_Reply.headers[1].value = "text/html"; + } + } + + + HTTPProxy::HTTPProxy (int port): + m_Thread (nullptr), m_Work (m_Service), + m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), + m_NewSocket (nullptr) + { + + } + + HTTPProxy::~HTTPProxy () + { + Stop (); + } + + void HTTPProxy::Start () + { + m_Thread = new std::thread (std::bind (&HTTPProxy::Run, this)); + m_Acceptor.listen (); + Accept (); + } + + void HTTPProxy::Stop () + { + m_Acceptor.close(); + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + + void HTTPProxy::Run () + { + m_Service.run (); + } + + void HTTPProxy::Accept () + { + m_NewSocket = new boost::asio::ip::tcp::socket (m_Service); + m_Acceptor.async_accept (*m_NewSocket, boost::bind (&HTTPProxy::HandleAccept, this, + boost::asio::placeholders::error)); + } + + void HTTPProxy::HandleAccept(const boost::system::error_code& ecode) + { + if (!ecode) + { + new HTTPConnection (m_NewSocket); + Accept (); + } + } +} +} + diff --git a/HTTPProxy.h b/HTTPProxy.h new file mode 100644 index 00000000..1c2a7168 --- /dev/null +++ b/HTTPProxy.h @@ -0,0 +1,91 @@ +#ifndef HTTP_PROXY_H__ +#define HTTP_PROXY_H__ + +#include +#include +#include +#include + +namespace i2p +{ +namespace proxy +{ + class HTTPConnection + { + struct header + { + std::string name; + std::string value; + }; + + struct request + { + std::string method; + std::string uri; + int http_version_major; + int http_version_minor; + std::vector
headers; + }; + + struct reply + { + std::vector
headers; + std::string content; + + std::vector to_buffers(); + }; + + public: + + HTTPConnection (boost::asio::ip::tcp::socket * socket): m_Socket (socket) { Receive (); }; + ~HTTPConnection () { delete m_Socket; } + + private: + + void Terminate (); + void Receive (); + void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleWrite(const boost::system::error_code& ecode); + + void HandleDestinationRequest (const std::string& address, const std::string& uri); + std::pair ExtractRequest (); + void parseHeaders(const std::string& h, std::vector
& hm); + + private: + + boost::asio::ip::tcp::socket * m_Socket; + char m_Buffer[8192]; + request m_Request; + reply m_Reply; + }; + + class HTTPProxy + { + public: + + HTTPProxy (int port); + ~HTTPProxy (); + + void Start (); + void Stop (); + + private: + + void Run (); + void Accept (); + void HandleAccept(const boost::system::error_code& ecode); + + private: + + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::io_service::work m_Work; + boost::asio::ip::tcp::acceptor m_Acceptor; + boost::asio::ip::tcp::socket * m_NewSocket; + }; +} +} + +#endif + + diff --git a/Makefile b/Makefile index 84a1acc0..d13d41b7 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ OBJECTS = obj/i2p.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transpor obj/RouterContext.o obj/NetDb.o obj/LeaseSet.o obj/Tunnel.o obj/TunnelEndpoint.o \ obj/TunnelGateway.o obj/TransitTunnel.o obj/I2NPProtocol.o obj/Log.o obj/Garlic.o \ obj/HTTPServer.o obj/Streaming.o obj/Identity.o obj/SSU.o obj/util.o obj/Reseed.o \ - obj/UPnP.o obj/TunnelPool.o + obj/UPnP.o obj/TunnelPool.o obj/HTTPProxy.o INCFLAGS = LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread LIBS = diff --git a/README.md b/README.md index 20d84e5b..d19a666c 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,6 @@ Options * --httpport= - The http port to listen on * --log= - Enable or disable logging to file. 1 for yes, 0 for no. * --daemon= - Eanble or disable daemon mode. 1 for yes, 0 for no. - +* --httpproxyport= - The port to listen on (HTTP Proxy) diff --git a/i2p.cpp b/i2p.cpp index b13b1791..cc2e5bcf 100644 --- a/i2p.cpp +++ b/i2p.cpp @@ -22,6 +22,7 @@ #include "Tunnel.h" #include "NetDb.h" #include "HTTPServer.h" +#include "HTTPProxy.h" #include "Garlic.h" #include "util.h" #include "Streaming.h" @@ -157,6 +158,9 @@ int main( int argc, char* argv[] ) i2p::garlic::routing.Start (); i2p::stream::StartStreaming (); + i2p::proxy::HTTPProxy httpProxy (i2p::util::config::GetArg("-httpproxyport", 4446)); + httpProxy.Start(); + while (running) { //TODO Meeh: Find something better to do here. From 15f0f20e99ea3c15cd29bf70c43a0983933addd2 Mon Sep 17 00:00:00 2001 From: Meeh Date: Mon, 17 Mar 2014 23:45:25 +0100 Subject: [PATCH 2/3] Code cleanup --- HTTPProxy.cpp | 101 +++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index 60500a75..5cdf70b4 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -58,75 +58,76 @@ namespace proxy void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (!ecode) - { + { m_Buffer[bytes_transferred] = 0; std::pair requestInfo = ExtractRequest (); request m_Request; - parseHeaders(m_Buffer, m_Request.headers); + parseHeaders (m_Buffer, m_Request.headers); LogPrint("Requesting ", requestInfo.first, " with path ", requestInfo.second); HandleDestinationRequest (requestInfo.first, requestInfo.second); boost::asio::async_write (*m_Socket, m_Reply.to_buffers(), - boost::bind (&HTTPConnection::HandleWrite, this, - boost::asio::placeholders::error)); + boost::bind (&HTTPConnection::HandleWrite, this, + boost::asio::placeholders::error)); //Receive (); } else if (ecode != boost::asio::error::operation_aborted) Terminate (); } -void HTTPConnection::parseHeaders(const std::string& h, std::vector
& hm) { - std::string str(h); - std::string::size_type idx; - std::string t; - int i = 0; - while((idx=str.find("\r\n")) != std::string::npos) { - t=str.substr(0,idx); - str.erase(0,idx+2); - if(t == "") - break; - idx=t.find(": "); - if(idx == std::string::npos) { - std::cout << "Bad header line: " << t << std::endl; - break; + void HTTPConnection::parseHeaders(const std::string& h, std::vector
& hm) { + std::string str (h); + std::string::size_type idx; + std::string t; + int i = 0; + while( (idx=str.find ("\r\n")) != std::string::npos) { + t=str.substr (0,idx); + str.erase (0,idx+2); + if (t == "") + break; + idx=t.find(": "); + if (idx == std::string::npos) + { + std::cout << "Bad header line: " << t << std::endl; + break; + } + LogPrint ("Name: ", t.substr (0,idx), " Value: ", t.substr (idx+2)); + hm[i].name = t.substr (0,idx); + hm[i].value = t.substr (idx+2); + i++; } - LogPrint ("Name: ", t.substr (0,idx), " Value: ", t.substr (idx+2)); - hm[i].name = t.substr(0,idx); - hm[i].value = t.substr(idx+2); - i++; } -} -// TODO: Support other requests than GET. -std::pair HTTPConnection::ExtractRequest () -{ - char * get = strstr (m_Buffer, "GET"); - if (get) + // TODO: Support other requests than GET. + std::pair HTTPConnection::ExtractRequest () { - char * http = strstr (get, "HTTP"); - if (http) + char * get = strstr (m_Buffer, "GET"); + if (get) { - std::string url (get + 4, http - get - 5); - size_t sp = url.find_first_of( '/', 7 /* skip http:// part */ ); - if ( sp != std::string::npos ) + char * http = strstr (get, "HTTP"); + if (http) { - std::string base_url( url.begin()+7, url.begin()+sp ); - LogPrint("Base URL is: ", base_url); - if ( sp != std::string::npos ) + std::string url (get + 4, http - get - 5); + size_t sp = url.find_first_of ('/', 7 /* skip http:// part */ ); + if (sp != std::string::npos) { - std::string query( url.begin()+sp+1, url.end() ); - LogPrint("Query is: ", "/" + query); + std::string base_url (url.begin()+7, url.begin()+sp); + LogPrint ("Base URL is: ", base_url); + if ( sp != std::string::npos ) + { + std::string query (url.begin ()+sp+1, url.end ()); + LogPrint ("Query is: ", "/" + query); - return std::make_pair(base_url, "/" + query); + return std::make_pair (base_url, "/" + query); + } + return std::make_pair (base_url, "/"); } - return std::make_pair(base_url, "/"); } } + return std::make_pair ("",""); } - return std::make_pair("",""); -} void HTTPConnection::HandleWrite (const boost::system::error_code& ecode) { @@ -139,8 +140,8 @@ std::pair HTTPConnection::ExtractRequest () std::string fullAddress; if (address.find (".b32.i2p") != std::string::npos) { - int li = address.find_first_of("."); - std::string newaddress = address.substr(0, li); + int li = address.find_first_of ("."); + std::string newaddress = address.substr (0, li); if (i2p::data::Base32ToByteStream (newaddress.c_str (), newaddress.length (), (uint8_t *)destination, 32) != 32) { LogPrint ("Invalid Base32 address ", newaddress); @@ -150,7 +151,7 @@ std::pair HTTPConnection::ExtractRequest () } else { - auto addr = i2p::data::netdb.FindAddress(address); + auto addr = i2p::data::netdb.FindAddress (address); if (!addr) { LogPrint ("Unknown address ", address); @@ -237,11 +238,11 @@ std::pair HTTPConnection::ExtractRequest () m_Acceptor.close(); m_Service.stop (); if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } } void HTTPProxy::Run () From 35e1442037f302b2f3ebf21d5b1f261eb0016bb8 Mon Sep 17 00:00:00 2001 From: Meeh Date: Tue, 18 Mar 2014 00:08:04 +0100 Subject: [PATCH 3/3] Adding itoopie icon on error messages. --- HTTPProxy.cpp | 6 +++--- HTTPProxy.h | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index 5cdf70b4..810e5a18 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -114,7 +114,7 @@ namespace proxy if (sp != std::string::npos) { std::string base_url (url.begin()+7, url.begin()+sp); - LogPrint ("Base URL is: ", base_url); + LogPrint ("Base URL is: ", base_url, "\n"); if ( sp != std::string::npos ) { std::string query (url.begin ()+sp+1, url.end ()); @@ -169,7 +169,7 @@ namespace proxy leaseSet = i2p::data::netdb.FindLeaseSet (destination); if (!leaseSet || !leaseSet->HasNonExpiredLeases ()) // still no LeaseSet { - m_Reply.content = leaseSet ? "Leases expired" : "LeaseSet not found"; + m_Reply.content = leaseSet ? ""+ i2p::proxy::itoopieImage +"
Leases expired" : ""+ i2p::proxy::itoopieImage +"LeaseSet not found"; m_Reply.headers.resize(2); m_Reply.headers[0].name = "Content-Length"; m_Reply.headers[0].value = boost::lexical_cast(m_Reply.content.size()); @@ -199,7 +199,7 @@ namespace proxy return; } else // nothing received - ss << "Not responding"; + ss << ""+ i2p::proxy::itoopieImage +"
Not responding"; s->Close (); DeleteStream (s); diff --git a/HTTPProxy.h b/HTTPProxy.h index 1c2a7168..58ccc9c4 100644 --- a/HTTPProxy.h +++ b/HTTPProxy.h @@ -10,6 +10,8 @@ namespace i2p { namespace proxy { + std::string itoopieImage = "\"\""; + class HTTPConnection { struct header