From f1370189b6ef6b4d29deb015365e6668ac31aad8 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 20 Nov 2016 09:25:56 -0500 Subject: [PATCH] initial outproxy support for http proxy --- Config.cpp | 3 +- Destination.cpp | 14 ++-- Destination.h | 2 +- HTTPProxy.cpp | 193 +++++++++++++++++++++++++++++++++++++++++++----- SOCKS.h | 1 + 5 files changed, 185 insertions(+), 28 deletions(-) diff --git a/Config.cpp b/Config.cpp index fb587e61..b994496b 100644 --- a/Config.cpp +++ b/Config.cpp @@ -88,7 +88,8 @@ namespace config { ("httpproxy.inbound.quantity", value()->default_value("5"), "HTTP proxy inbound tunnels quantity") ("httpproxy.outbound.quantity", value()->default_value("5"), "HTTP proxy outbound tunnels quantity") ("httpproxy.latency.min", value()->default_value("0"), "HTTP proxy min latency for tunnels") - ("httpproxy.latency.max", value()->default_value("0"), "HTTP proxy max latency for tunnels") + ("httpproxy.latency.max", value()->default_value("0"), "HTTP proxy max latency for tunnels") + ("httpproxy.outproxy", value()->default_value(""), "HTTP proxy upstream out proxy url") ; options_description socksproxy("SOCKS Proxy options"); diff --git a/Destination.cpp b/Destination.cpp index 9cf49336..634e59f6 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -97,14 +97,14 @@ namespace client while (m_IsRunning) { try - { + { m_Service.run (); } catch (std::exception& ex) { - LogPrint (eLogError, "Destination: runtime exception: ", ex.what ()); - } - } + LogPrint (eLogError, "Destination: Runtime Exception", ex.what ()); + } + } } bool LeaseSetDestination::Start () @@ -113,7 +113,7 @@ namespace client { m_IsRunning = true; m_Pool->SetLocalDestination (shared_from_this ()); - m_Pool->SetActive (true); + m_Pool->SetActive (true); m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ())); m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); @@ -137,7 +137,7 @@ namespace client { m_Pool->SetLocalDestination (nullptr); i2p::tunnel::tunnels.StopTunnelPool (m_Pool); - } + } m_Service.stop (); if (m_Thread) { @@ -622,8 +622,8 @@ namespace client if (done) { auto requestComplete = it->second; - m_LeaseSetRequests.erase (it); if (requestComplete) requestComplete->Complete (nullptr); + m_LeaseSetRequests.erase (it); } } } diff --git a/Destination.h b/Destination.h index 5b3ee655..bf622b85 100644 --- a/Destination.h +++ b/Destination.h @@ -137,7 +137,7 @@ namespace client volatile bool m_IsRunning; std::thread * m_Thread; boost::asio::io_service m_Service; - boost::asio::io_service::work m_Work; + boost::asio::io_service::work m_Work; mutable std::mutex m_RemoteLeaseSetsMutex; std::map > m_RemoteLeaseSets; std::map > m_LeaseSetRequests; diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index c40989d1..1750e582 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -64,15 +64,37 @@ namespace proxy { void HostNotFound(std::string & host); void SendProxyError(std::string & content); + void ForwardToUpstreamProxy(); + void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec); + void HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec); + + void HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transfered); + void HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transfered); + + typedef std::function ProxyResolvedHandler; + + void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler); + + void SocksProxySuccess(); + void HandoverToUpstreamProxy(); + uint8_t m_recv_chunk[8192]; std::string m_recv_buf; // from client std::string m_send_buf; // to upstream std::shared_ptr m_sock; - + std::shared_ptr m_proxysock; + boost::asio::ip::tcp::resolver m_proxy_resolver; + i2p::http::URL m_RequestURL; + i2p::http::URL m_ProxyURL; + std::string m_HTTPMethod; + uint8_t m_socks_buf[255+8]; // for socks request/response + public: HTTPReqHandler(HTTPProxy * parent, std::shared_ptr sock) : - I2PServiceHandler(parent), m_sock(sock) {} + I2PServiceHandler(parent), m_sock(sock), + m_proxysock(std::make_shared(parent->GetService())), + m_proxy_resolver(parent->GetService()) {} ~HTTPReqHandler() { Terminate(); } void Handle () { AsyncSockRead(); } /* overload */ }; @@ -97,6 +119,12 @@ namespace proxy { m_sock->close(); m_sock = nullptr; } + if(m_proxysock) + { + if(m_proxysock->is_open()) + m_proxysock->close(); + m_proxysock = nullptr; + } Done(shared_from_this()); } @@ -142,7 +170,7 @@ namespace proxy { << "\r\n"; res.body = ss.str(); std::string response = res.to_string(); - boost::asio::async_write(*m_sock, boost::asio::buffer(response), + boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); } @@ -199,7 +227,6 @@ namespace proxy { bool HTTPReqHandler::HandleRequest() { i2p::http::HTTPReq req; - i2p::http::URL url; std::string b64; int req_len = 0; @@ -216,14 +243,14 @@ namespace proxy { /* parsing success, now let's look inside request */ LogPrint(eLogDebug, "HTTPProxy: requested: ", req.uri); - url.parse(req.uri); + m_RequestURL.parse(req.uri); - if (ExtractAddressHelper(url, b64)) { - i2p::client::context.GetAddressBook ().InsertAddress (url.host, b64); - LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", url.host); - std::string full_url = url.to_string(); + if (ExtractAddressHelper(m_RequestURL, b64)) { + i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, b64); + LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", m_RequestURL.host); + std::string full_url = m_RequestURL.to_string(); std::stringstream ss; - ss << "Host " << url.host << " added to router's addressbook from helper. " + ss << "Host " << m_RequestURL.host << " added to router's addressbook from helper. " << "Click here to proceed."; GenericProxyInfo("Addresshelper found", ss.str().c_str()); return true; /* request processed */ @@ -231,11 +258,11 @@ namespace proxy { SanitizeHTTPRequest(req); - std::string dest_host = url.host; - uint16_t dest_port = url.port; + std::string dest_host = m_RequestURL.host; + uint16_t dest_port = m_RequestURL.port; /* always set port, even if missing in request */ if (!dest_port) { - dest_port = (url.schema == "https") ? 443 : 80; + dest_port = (m_RequestURL.schema == "https") ? 443 : 80; } /* detect dest_host, set proper 'Host' header in upstream request */ auto h = req.headers.find("Host"); @@ -267,16 +294,26 @@ namespace proxy { } /* TODO: outproxy handler here */ } else { - LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": not implemented yet"); - std::string message = "Host" + dest_host + "not inside I2P network, but outproxy support not implemented yet"; - GenericProxyError("Outproxy failure", message.c_str()); + std::string outproxyUrl; i2p::config::GetOption("httpproxy.outproxy", outproxyUrl); + if(outproxyUrl.size()) { + m_HTTPMethod = req.method; + LogPrint (eLogDebug, "HTTPProxy: use outproxy ", outproxyUrl); + if(m_ProxyURL.parse(outproxyUrl)) + ForwardToUpstreamProxy(); + else + GenericProxyError("Outproxy failure", "bad outproxy settings"); + } else { + LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": not implemented yet"); + std::string message = "Host" + dest_host + "not inside I2P network, but outproxy support not implemented yet"; + GenericProxyError("Outproxy failure", message.c_str()); + } return true; } /* make relative url */ - url.schema = ""; - url.host = ""; - req.uri = url.to_string(); + m_RequestURL.schema = ""; + m_RequestURL.host = ""; + req.uri = m_RequestURL.to_string(); /* drop original request from recv buffer */ m_recv_buf.erase(0, req_len); @@ -290,6 +327,124 @@ namespace proxy { return true; } + void HTTPReqHandler::ForwardToUpstreamProxy() + { + LogPrint(eLogDebug, "HTTPProxy: forward to upstream"); + // assume http if empty schema + if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") { + // handle upstream http proxy + if (!m_ProxyURL.port) m_ProxyURL.port = 80; + boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); + m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { + m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1)); + })); + } else if (m_ProxyURL.schema == "socks") { + // handle upstream socks proxy + if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified + boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); + m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { + m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1)); + })); + } else { + // unknown type, complain + GenericProxyError("unknown outproxy url", m_ProxyURL.to_string().c_str()); + } + } + + void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler) + { + if(ec) GenericProxyError("cannot resolve upstream proxy", ec.message().c_str()); + else handler(*it); + } + + void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec) + { + if(!ec) { + if(m_RequestURL.host.size() > 255) { + GenericProxyError("hostname too long", m_RequestURL.host.c_str()); + return; + } + uint16_t port = m_RequestURL.port; + LogPrint(eLogDebug, "HTTPProxy: connected to socks upstream"); + if(m_HTTPMethod == "CONNECT") { + std::string host = m_RequestURL.host; + std::size_t reqsize = 0; + m_socks_buf[0] = '\x04'; + m_socks_buf[1] = 1; + htobe16buf(m_socks_buf+2, port); + m_socks_buf[4] = 0; + m_socks_buf[5] = 0; + m_socks_buf[6] = 0; + m_socks_buf[7] = 1; + // user id + m_socks_buf[8] = 'i'; + m_socks_buf[9] = '2'; + m_socks_buf[10] = 'p'; + m_socks_buf[11] = 'd'; + m_socks_buf[12] = 0; + reqsize += 13; + memcpy(m_socks_buf+ reqsize, host.c_str(), host.size()); + reqsize += host.size(); + m_socks_buf[++reqsize] = 0; + m_proxysock->async_write_some(boost::asio::buffer(m_socks_buf, reqsize), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2)); + } else { + GenericProxyError("unsupported http method", m_HTTPMethod.c_str()); + } + } else GenericProxyError("cannot connect to upstream socks proxy", ec.message().c_str()); + } + + void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred) + { + if(ec) GenericProxyError("Cannot negotiate with socks proxy", ec.message().c_str()); + else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2)); + } + + void HTTPReqHandler::HandoverToUpstreamProxy() + { + auto connection = std::make_shared(GetOwner(), m_proxysock, m_sock); + m_sock = nullptr; + m_proxysock = nullptr; + GetOwner()->AddHandler(connection); + connection->Start(); + Terminate(); + } + + void HTTPReqHandler::SocksProxySuccess() + { + i2p::http::HTTPRes res; + res.code = 200; + std::string response = res.to_string(); + boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) { + if(ec) GenericProxyError("socks proxy error", ec.message().c_str()); + else HandoverToUpstreamProxy(); + }); + } + + void HTTPReqHandler::HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transferred) + { + if(!ec) + { + if(m_socks_buf[1] == 90) { + // success + SocksProxySuccess(); + } else { + std::stringstream ss; + ss << (int) m_socks_buf[1]; + std::string msg = ss.str(); + GenericProxyError("Socks Proxy error", msg.c_str()); + } + } + else GenericProxyError("No Reply From socks proxy", ec.message().c_str()); + } + + void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec) + { + if(!ec) { + LogPrint(eLogDebug, "HTTPProxy: connected to http upstream"); + GenericProxyError("cannot connect", "http out proxy not implemented"); + } else GenericProxyError("cannot connect to upstream http proxy", ec.message().c_str()); + } + /* will be called after some data received from client */ void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) { diff --git a/SOCKS.h b/SOCKS.h index d417c4a0..05106958 100644 --- a/SOCKS.h +++ b/SOCKS.h @@ -33,6 +33,7 @@ namespace proxy }; typedef SOCKSServer SOCKSProxy; + } }