使用 boost::asio(同步调用)通过 HTTP 代理在 HTTPS TLS 握手中出现流截断错误
stream truncated error on HTTPS TLS handshake via a HTTP proxy using boost::asio (synchronous calls)
我正在研究一些 HTTP 代码以连接到 REST 端点。我在公司网络中工作,与外界的连接必须通过 HTTP 代理,而端点在 HTTPS 上。
现在,我已经成功地使用 Python requests
和 curl
进行了这些调用,但是在 C++ 中进行相同的调用一直很痛苦。
我正在尝试使用以下代码通过此代理执行握手(连接到示例 Postmen 端点),该代码改编自以下示例:
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/http/empty_body.hpp>
#include <boost/beast/http/fields.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/string_body.hpp>
#include<boost/certify/https_verification.hpp>
#include <iostream>
using tcp = boost::asio::ip::tcp;
namespace http = boost::beast::http;
// https://postman-echo.com/post see https://docs.postman-echo.com/?version=latest
static const std::string
server_endpoint = "/post",
hostname = "postman-echo.com",
target = "https://postman-echo.com:443",
port_no = "443",
authorization_token =
"Auth: "
"c3RhdGljIGNvbnN0IHN0ZDo6c3RyaW5nIGF1dGhvcml6YXRpb"
"25fdG9rZW4gPSAiQXV0aDogIj"
"sK",
client_name = "User-Agent: demo program 0.01",
req_str = R"(name=blabla&password=bloblo)";
int main() {
boost::asio::io_service io_service;
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
// Establish TCP base socket to Proxy
tcp::socket base_sock{io_service};
tcp::resolver resolver(io_service);
{
// Connect socket via proxy
const boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp> proxy_hosts = resolver.resolve("PROXY_ADDR", "PROXY_PORT");
if (proxy_hosts.empty()) {
throw std::range_error("Proxy cannot be resolved.");
}
std::cout << "Proxy IP address: " << proxy_hosts->endpoint().address() << std::endl;
//boost::asio::ip::tcp::socket::lowest_layer_type& socket = base_sock.lowest_layer();
base_sock.connect(proxy_hosts->endpoint());
}
{
// Converts request to transparent TCP/IP tunnel. To facilitate TLS communications
// through HTTP proxy.
http::request<http::string_body> req1{http::verb::connect, target, 11};
req1.set(http::field::host, target);
http::write(base_sock, req1);
boost::beast::flat_buffer buffer;
http::response<http::empty_body> res;
http::parser</* isRequest */ false, http::empty_body> http_parser(res);
/** Set the skip parse option. */
http_parser.skip(true); // see
http::read_header(base_sock, buffer, http_parser);
// Write the message to standard out
std::cout << "target_host response: " << res << std::endl;
}
ctx.set_verify_mode(boost::asio::ssl::verify_peer);
//ctx.load_verify_file("/** ssl/cacert from python requests library */");
//ctx.add_verify_path("/** CA certificates from Curl via strace https://serverfault.com/questions/485597/default-ca-cert-bundle-location */");
//ctx.load_verify_file("/** CA certs from curl via calling endpoints with --verbose */");
//ctx.set_default_verify_paths();
boost::certify::enable_native_https_server_verification(ctx); // from lib https://github.com/djarek/certify
ctx.set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> ssl_socket(base_sock,
ctx);
{
//ssl_socket.handshake(boost::asio::ssl::stream_base::client);
ssl_socket.handshake(boost::asio::ssl::stream<tcp::socket&>::client);
/** Make REST calls here */
}
}
运行 上面的代码在握手步骤失败,出现“流被截断”错误。
到目前为止我的尝试是:
- 验证使用的证书是否正确,其中我已经输入了来自 Python、Curl 和 certify 的证书。这并没有解决问题。
- 正在检查 Python 和 Curl 跟踪它们的协议步骤,但是由于来自 boost::asio 的错误消息有限,我无法在 C++ 中对此进行验证;而
gdb
痕迹很难追踪。
- 大量的 Whosebug 挖掘,不幸的是我无法拼凑出一个工作原型。
虽然我在编写网络代码方面的经验有限,但错误很可能是我的逻辑某处而不是不正确的环境。
想知道是否有人可以指出代码可能在哪里不正确?
经过更多试验,我注意到问题出在 target
变量中。设置 target = "postmen-echo.com:443";
- 删除 https
前缀,并传入适当的 CA 证书(例如来自 Python 或 Curl)可解决此问题。
我正在研究一些 HTTP 代码以连接到 REST 端点。我在公司网络中工作,与外界的连接必须通过 HTTP 代理,而端点在 HTTPS 上。
现在,我已经成功地使用 Python requests
和 curl
进行了这些调用,但是在 C++ 中进行相同的调用一直很痛苦。
我正在尝试使用以下代码通过此代理执行握手(连接到示例 Postmen 端点),该代码改编自以下示例:
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/http/empty_body.hpp>
#include <boost/beast/http/fields.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/string_body.hpp>
#include<boost/certify/https_verification.hpp>
#include <iostream>
using tcp = boost::asio::ip::tcp;
namespace http = boost::beast::http;
// https://postman-echo.com/post see https://docs.postman-echo.com/?version=latest
static const std::string
server_endpoint = "/post",
hostname = "postman-echo.com",
target = "https://postman-echo.com:443",
port_no = "443",
authorization_token =
"Auth: "
"c3RhdGljIGNvbnN0IHN0ZDo6c3RyaW5nIGF1dGhvcml6YXRpb"
"25fdG9rZW4gPSAiQXV0aDogIj"
"sK",
client_name = "User-Agent: demo program 0.01",
req_str = R"(name=blabla&password=bloblo)";
int main() {
boost::asio::io_service io_service;
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
// Establish TCP base socket to Proxy
tcp::socket base_sock{io_service};
tcp::resolver resolver(io_service);
{
// Connect socket via proxy
const boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp> proxy_hosts = resolver.resolve("PROXY_ADDR", "PROXY_PORT");
if (proxy_hosts.empty()) {
throw std::range_error("Proxy cannot be resolved.");
}
std::cout << "Proxy IP address: " << proxy_hosts->endpoint().address() << std::endl;
//boost::asio::ip::tcp::socket::lowest_layer_type& socket = base_sock.lowest_layer();
base_sock.connect(proxy_hosts->endpoint());
}
{
// Converts request to transparent TCP/IP tunnel. To facilitate TLS communications
// through HTTP proxy.
http::request<http::string_body> req1{http::verb::connect, target, 11};
req1.set(http::field::host, target);
http::write(base_sock, req1);
boost::beast::flat_buffer buffer;
http::response<http::empty_body> res;
http::parser</* isRequest */ false, http::empty_body> http_parser(res);
/** Set the skip parse option. */
http_parser.skip(true); // see
http::read_header(base_sock, buffer, http_parser);
// Write the message to standard out
std::cout << "target_host response: " << res << std::endl;
}
ctx.set_verify_mode(boost::asio::ssl::verify_peer);
//ctx.load_verify_file("/** ssl/cacert from python requests library */");
//ctx.add_verify_path("/** CA certificates from Curl via strace https://serverfault.com/questions/485597/default-ca-cert-bundle-location */");
//ctx.load_verify_file("/** CA certs from curl via calling endpoints with --verbose */");
//ctx.set_default_verify_paths();
boost::certify::enable_native_https_server_verification(ctx); // from lib https://github.com/djarek/certify
ctx.set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> ssl_socket(base_sock,
ctx);
{
//ssl_socket.handshake(boost::asio::ssl::stream_base::client);
ssl_socket.handshake(boost::asio::ssl::stream<tcp::socket&>::client);
/** Make REST calls here */
}
}
运行 上面的代码在握手步骤失败,出现“流被截断”错误。
到目前为止我的尝试是:
- 验证使用的证书是否正确,其中我已经输入了来自 Python、Curl 和 certify 的证书。这并没有解决问题。
- 正在检查 Python 和 Curl 跟踪它们的协议步骤,但是由于来自 boost::asio 的错误消息有限,我无法在 C++ 中对此进行验证;而
gdb
痕迹很难追踪。 - 大量的 Whosebug 挖掘,不幸的是我无法拼凑出一个工作原型。
虽然我在编写网络代码方面的经验有限,但错误很可能是我的逻辑某处而不是不正确的环境。
想知道是否有人可以指出代码可能在哪里不正确?
经过更多试验,我注意到问题出在 target
变量中。设置 target = "postmen-echo.com:443";
- 删除 https
前缀,并传入适当的 CA 证书(例如来自 Python 或 Curl)可解决此问题。