forked from PurpleI2P/i2pd
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
366 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,269 @@ | ||
#include <boost/bind.hpp> | ||
#include <boost/lexical_cast.hpp> | ||
|
||
#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<boost::asio::const_buffer> HTTPConnection::reply::to_buffers() | ||
{ | ||
std::vector<boost::asio::const_buffer> 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<std::string,std::string> 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<header>& 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<std::string, std::string> 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 ? "<html>Leases expired</html>" : "<html>LeaseSet not found</html>"; | ||
m_Reply.headers.resize(2); | ||
m_Reply.headers[0].name = "Content-Length"; | ||
m_Reply.headers[0].value = boost::lexical_cast<std::string>(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 << "<html>Not responding</html>"; | ||
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<std::string>(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 (); | ||
} | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
#ifndef HTTP_PROXY_H__ | ||
#define HTTP_PROXY_H__ | ||
|
||
#include <sstream> | ||
#include <thread> | ||
#include <boost/asio.hpp> | ||
#include <boost/array.hpp> | ||
|
||
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<header> headers; | ||
}; | ||
|
||
struct reply | ||
{ | ||
std::vector<header> headers; | ||
std::string content; | ||
|
||
std::vector<boost::asio::const_buffer> 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<std::string, std::string> ExtractRequest (); | ||
void parseHeaders(const std::string& h, std::vector<header>& 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 | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters