add http use default certificates api.

This commit is contained in:
amass 2024-01-14 17:54:26 +08:00
parent 20750d1a80
commit df14b67695
4 changed files with 4194 additions and 174 deletions

View File

@ -1,5 +1,6 @@
project(HttpProxy project(HttpProxy
DESCRIPTION "router api is copy of boost_1_84_0/libs/url/example/router" DESCRIPTION "router api is copy of boost_1_84_0/libs/url/example/router
root_certificates.hpp is copy of boost_1_84_0/libs/beast/example/common/root_certificates.hpp"
) )
find_package(Boost COMPONENTS url REQUIRED) find_package(Boost COMPONENTS url REQUIRED)
@ -9,18 +10,23 @@ add_library(HttpProxy
ProxyHttpSession.h ProxyHttpSession.cpp ProxyHttpSession.h ProxyHttpSession.cpp
ProxyListener.h ProxyListener.cpp ProxyListener.h ProxyListener.cpp
ProxyTcpSession.h ProxyTcpSession.cpp ProxyTcpSession.h ProxyTcpSession.cpp
root_certificates.hpp
router.hpp
detail/impl/router.cpp detail/impl/router.cpp
impl/matches.cpp impl/matches.cpp
) )
target_include_directories(HttpProxy target_include_directories(HttpProxy
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${OpenSSL_INCLUDE_DIR} PRIVATE ${OPENSSL_INCLUDE_DIR}
)
target_link_directories(HttpProxy
PUBLIC ${OPENSSL_LIBRARY_DIRS}
) )
target_link_libraries(HttpProxy target_link_libraries(HttpProxy
PUBLIC ${Boost_LIBRARIES} PUBLIC ${Boost_LIBRARIES}
PUBLIC Universal PUBLIC Universal
PRIVATE ${OpenSSL_LIBRARY} PRIVATE ${OPENSSL_LIBRARIES}
) )

View File

@ -1,166 +1,32 @@
#include "NetworkUtility.h" #include "NetworkUtility.h"
#include "BoostLog.h" #include "BoostLog.h"
#include "root_certificates.hpp"
#include <boost/beast/core.hpp> #include <boost/beast/core.hpp>
#include <boost/beast/http.hpp> #include <boost/beast/http/dynamic_body.hpp>
#include <boost/beast/ssl.hpp> #include <boost/beast/http/read.hpp>
#include <boost/beast/http/write.hpp>
#include <boost/beast/ssl/ssl_stream.hpp>
#include <boost/beast/version.hpp> #include <boost/beast/version.hpp>
#include <optional>
static std::optional<boost::beast::ssl_stream<boost::beast::tcp_stream>> std::string Https::get(boost::asio::io_context &ioContext, const std::string &host, const std::string &port,
makeSslStream(boost::asio::io_context &ioContext, const std::string_view &host, const std::string_view &port, const std::string &url, boost::system::error_code &error, Http::Version version) {
boost::system::error_code &error); Http::Client client(ioContext, Http::SSL, version);
return client.get(host, port, url, error);
std::string Https::get(boost::asio::io_context &ioContext, const std::string_view &host, const std::string_view &port,
const std::string_view &url, boost::system::error_code &error, Http::Version version) {
namespace beast = boost::beast;
namespace http = boost::beast::http;
auto stream = makeSslStream(ioContext, host, port, error);
if (!stream) return std::string();
http::request<http::string_body> req{http::verb::get, url, static_cast<unsigned>(version)};
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
http::write(*stream, req, error);
if (error) {
// LOG(error) << error.message();
return std::string();
}
beast::flat_buffer buffer;
http::response_parser<http::dynamic_body> parser;
parser.body_limit(std::numeric_limits<std::uint64_t>::max());
http::read(*stream, buffer, parser, error);
if (error) {
LOG(error) << error.message();
return std::string();
}
beast::error_code ec;
stream->shutdown(ec);
return boost::beast::buffers_to_string(parser.get().body().data());
} }
std::string Https::post(boost::asio::io_context &ioContext, const std::string_view &host, const std::string_view &port, std::string Https::post(boost::asio::io_context &ioContext, const std::string &host, const std::string &port,
const std::string_view &url, const std::string_view &body, boost::system::error_code &error, const std::string &url, const std::string &body, boost::system::error_code &error,
Http::Version version) { Http::Version version) {
namespace beast = boost::beast; Http::Client client(ioContext, Http::SSL, version);
namespace http = boost::beast::http; return client.post(host, port, url, body, error);
auto stream = makeSslStream(ioContext, host, port, error);
if (!stream) return std::string();
http::request<http::string_body> req{http::verb::post, url.data(), static_cast<unsigned>(version)};
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
req.body() = body;
req.prepare_payload();
http::write(*stream, req, error);
if (error) {
LOG(error) << error.message();
return std::string();
}
beast::flat_buffer buffer;
http::response<boost::beast::http::dynamic_body> response;
http::read(*stream, buffer, response, error);
if (error) {
LOG(error) << error.message();
return std::string();
}
beast::error_code ec;
stream->shutdown(ec);
return boost::beast::buffers_to_string(response.body().data());
} }
std::string Https::put(boost::asio::io_context &ioContext, const std::string &host, const std::string &port, std::string Https::put(boost::asio::io_context &ioContext, const std::string &host, const std::string &port,
const std::string &url, const std::string &body, boost::system::error_code &error, const std::string &url, const std::string &body, boost::system::error_code &error,
Http::Version version) { Http::Version version) {
namespace beast = boost::beast; Http::Client client(ioContext, Http::SSL, version);
namespace http = boost::beast::http; return client.put(host, port, url, body, error);
auto stream = makeSslStream(ioContext, host, port, error);
if (!stream) return std::string();
http::request<http::string_body> req{http::verb::put, url.data(), static_cast<unsigned>(version)};
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
req.body() = body;
req.prepare_payload();
http::write(*stream, req, error);
if (error) {
LOG(error) << error.message();
return std::string();
}
beast::flat_buffer buffer;
http::response<boost::beast::http::dynamic_body> response;
http::read(*stream, buffer, response, error);
if (error) {
LOG(error) << error.message();
return std::string();
}
beast::error_code ec;
stream->shutdown(ec);
return boost::beast::buffers_to_string(response.body().data());
}
static std::optional<boost::beast::ssl_stream<boost::beast::tcp_stream>>
makeSslStream(boost::asio::io_context &ioContext, const std::string_view &host, const std::string_view &port,
boost::system::error_code &error) {
namespace beast = boost::beast;
namespace http = boost::beast::http;
namespace asio = boost::asio; // from <boost/asio.hpp>
using tcp = asio::ip::tcp;
namespace ssl = asio::ssl;
ssl::context sslContext(ssl::context::sslv23_client);
sslContext.set_default_verify_paths(error);
if (error) {
LOG(error) << error.message();
return std::nullopt;
}
boost::asio::ip::tcp::resolver resolver(ioContext);
beast::ssl_stream<beast::tcp_stream> stream(ioContext, sslContext);
auto const results = resolver.resolve(host, port, error);
if (error) {
LOG(error) << error.message();
return std::nullopt;
}
beast::get_lowest_layer(stream).connect(results);
stream.handshake(ssl::stream_base::client);
return std::make_optional(std::move(stream));
}
std::vector<Http::ParseItem> Http::parseFormData(const std::string_view &buffer) {
std::vector<ParseItem> ret;
auto pos = buffer.find("\r\n");
std::string_view m_spliter = buffer.substr(0, pos);
size_t m_end = buffer.rfind(m_spliter);
pos = 0;
while (pos != m_end) {
pos = buffer.find(m_spliter, pos);
if (pos == m_end) break;
ParseItem item;
auto dataSpliterPos = buffer.find("\r\n\r\n", pos);
item.begin = dataSpliterPos + 4;
item.end = buffer.find(m_spliter, pos + 1) - 2;
auto meta = buffer.substr(pos, dataSpliterPos - pos);
auto beginPos = meta.find("filename=\"") + 10;
auto endPos = meta.find("\"", beginPos);
item.filename = meta.substr(beginPos, endPos - beginPos);
pos++;
ret.push_back(item);
}
return ret;
} }
uint64_t Network::htonll(uint64_t val) { uint64_t Network::htonll(uint64_t val) {
@ -200,3 +66,144 @@ Network::~Network() {
::WSACleanup(); ::WSACleanup();
#endif #endif
} }
namespace Http {
std::vector<ParseItem> parseFormData(const std::string_view &buffer) {
std::vector<ParseItem> ret;
auto pos = buffer.find("\r\n");
std::string_view m_spliter = buffer.substr(0, pos);
size_t m_end = buffer.rfind(m_spliter);
pos = 0;
while (pos != m_end) {
pos = buffer.find(m_spliter, pos);
if (pos == m_end) break;
ParseItem item;
auto dataSpliterPos = buffer.find("\r\n\r\n", pos);
item.begin = dataSpliterPos + 4;
item.end = buffer.find(m_spliter, pos + 1) - 2;
auto meta = buffer.substr(pos, dataSpliterPos - pos);
auto beginPos = meta.find("filename=\"") + 10;
auto endPos = meta.find("\"", beginPos);
item.filename = meta.substr(beginPos, endPos - beginPos);
pos++;
ret.push_back(item);
}
return ret;
}
Client::Client(boost::asio::io_context &ioContext, Type type, Version version) : m_ioContext(ioContext) {
m_request.version(static_cast<unsigned>(version));
using namespace boost::asio;
using namespace boost::beast;
m_request.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
if (type == SSL) {
m_sslContext = std::make_shared<ssl::context>(ssl::context::sslv23_client);
}
}
void Client::loadRootCertificates(boost::system::error_code &error) {
if (m_sslContext) {
load_root_certificates(*m_sslContext, error);
if (!error) {
m_certificateLocation = CertificateLocation::Default;
}
}
}
std::string Client::get(const std::string &host, const std::string &port, const std::string &url,
boost::system::error_code &error) {
using namespace boost::beast;
m_request.method(http::verb::put);
m_request.target(url);
m_request.set(http::field::host, host);
return execute(host, port, m_request, error);
}
std::string Client::post(const std::string &host, const std::string &port, const std::string &url,
const std::string &body, boost::system::error_code &error) {
using namespace boost::beast;
m_request.method(http::verb::put);
m_request.target(url);
m_request.set(http::field::host, host);
m_request.body() = body;
m_request.prepare_payload();
return execute(host, port, m_request, error);
}
std::string Client::put(const std::string &host, const std::string &port, const std::string &url,
const std::string &body, boost::system::error_code &error) {
using namespace boost::beast;
m_request.method(http::verb::put);
m_request.target(url);
m_request.set(http::field::host, host);
m_request.body() = body;
m_request.prepare_payload();
return execute(host, port, m_request, error);
}
void Client::addRequestField(boost::beast::http::field name, const std::string &value) {
m_request.set(name, value);
}
std::string Client::execute(const std::string &host, const std::string &port, const Request &request,
boost::system::error_code &error) {
using namespace boost::asio::ip;
using namespace boost::beast;
tcp::resolver resolver(m_ioContext);
auto const results = resolver.resolve(host, port, error);
if (error) {
return std::string();
}
std::unique_ptr<ssl_stream<tcp_stream>> sslStream;
std::unique_ptr<tcp_stream> tcpStream;
if (m_sslContext) {
if (m_certificateLocation == CertificateLocation::None) {
m_sslContext->set_default_verify_paths(error);
if (!error) m_certificateLocation = CertificateLocation::SystemDependent;
}
sslStream = std::make_unique<ssl_stream<tcp_stream>>(m_ioContext, *m_sslContext);
get_lowest_layer(*sslStream).connect(results, error);
if (error) {
return std::string();
}
sslStream->handshake(ssl::stream_base::client, error);
} else {
tcpStream = std::make_unique<tcp_stream>(m_ioContext);
tcpStream->connect(results, error);
}
if (error) {
return std::string();
}
if (sslStream) {
http::write(*sslStream, request, error);
} else if (tcpStream) {
http::write(*tcpStream, request, error);
}
http::response<http::dynamic_body> response;
if (!error) {
flat_buffer buffer;
if (sslStream) {
http::read(*sslStream, buffer, response, error);
error_code ec;
sslStream->shutdown(ec);
} else if (tcpStream) {
http::read(*tcpStream, buffer, response, error);
error_code ec;
tcpStream->socket().shutdown(tcp::socket::shutdown_both, ec);
}
}
return boost::beast::buffers_to_string(response.body().data());
}
} // namespace Http

View File

@ -1,12 +1,21 @@
#ifndef __NETWORKUTILITY_H__ #ifndef __NETWORKUTILITY_H__
#define __NETWORKUTILITY_H__ #define __NETWORKUTILITY_H__
#include "Singleton.h" #include "Singleton.h"
#include <boost/asio/io_context.hpp> #include <boost/asio/io_context.hpp>
#include <boost/beast/http/string_body.hpp>
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
#include <string> #include <string>
#include <string_view> #include <string_view>
namespace boost {
namespace asio {
namespace ssl {
class context;
}
} // namespace asio
} // namespace boost
class Network { class Network {
friend class Amass::Singleton<Network, Amass::GlobalInstance>; friend class Amass::Singleton<Network, Amass::GlobalInstance>;
@ -20,18 +29,28 @@ protected:
~Network(); ~Network();
}; };
class Http { namespace Http {
public: using Request = boost::beast::http::request<boost::beast::http::string_body>;
enum Version : unsigned { enum Type {
Transparent,
SSL,
};
enum CertificateLocation {
None,
Default,
SystemDependent,
};
enum Version : unsigned {
Version_1_1 = 11, Version_1_1 = 11,
}; };
struct ParseItem { struct ParseItem {
std::string filename; std::string filename;
// std::string::const_iterator begin; // std::string::const_iterator begin;
// std::string::const_iterator end; // std::string::const_iterator end;
size_t begin; size_t begin;
size_t end; size_t end;
}; };
/** /**
* @brief * @brief
@ -48,17 +67,42 @@ public:
* } * }
*/ */
static std::vector<ParseItem> parseFormData(const std::string_view &buffer); static std::vector<ParseItem> parseFormData(const std::string_view &buffer);
};
class Client {
public:
Client(boost::asio::io_context &ioContext, Type type, Version version = Version_1_1);
void loadRootCertificates(boost::system::error_code &error);
std::string get(const std::string &host, const std::string &port, const std::string &url,
boost::system::error_code &error);
std::string post(const std::string &host, const std::string &port, const std::string &url,
const std::string &body, boost::system::error_code &error);
std::string put(const std::string &host, const std::string &port, const std::string &url,
const std::string &body, boost::system::error_code &error);
void addRequestField(boost::beast::http::field name, const std::string &value);
protected:
std::string execute(const std::string &host, const std::string &port, const Request &request,
boost::system::error_code &error);
private:
boost::asio::io_context &m_ioContext;
std::shared_ptr<boost::asio::ssl::context> m_sslContext;
CertificateLocation m_certificateLocation = CertificateLocation::None;
Request m_request;
};
} // namespace Http
class Https { class Https {
public: public:
static std::string get(boost::asio::io_context &ioContext, const std::string_view &host, static std::string get(boost::asio::io_context &ioContext, const std::string &host, const std::string &port,
const std::string_view &port, const std::string_view &url, boost::system::error_code &error, const std::string &url, boost::system::error_code &error,
Http::Version version = Http::Version_1_1); Http::Version version = Http::Version_1_1);
static std::string post(boost::asio::io_context &ioContext, const std::string_view &host, static std::string post(boost::asio::io_context &ioContext, const std::string &host, const std::string &port,
const std::string_view &port, const std::string_view &url, const std::string_view &body, const std::string &url, const std::string &body, boost::system::error_code &error,
boost::system::error_code &error, Http::Version version = Http::Version_1_1); Http::Version version = Http::Version_1_1);
static std::string put(boost::asio::io_context &ioContext, const std::string &host, const std::string &port, static std::string put(boost::asio::io_context &ioContext, const std::string &host, const std::string &port,
const std::string &url, const std::string &body, boost::system::error_code &error, const std::string &url, const std::string &body, boost::system::error_code &error,

File diff suppressed because it is too large Load Diff