c++ - 如何获取 OpenSSL BIO_do_connect() 失败原因?
问题描述
我正在使用 Ubuntu 18.04/gcc 7.3/OpenSSL 1.1.0g 使 C++ 应用程序使用非阻塞 BIO API 执行 TLS/SSL 连接。
当BIO_do_connect()
连接失败时,例如使用错误的主机名或端口,OpenSSL 不会报告错误。ERR_get_error()
返回零并且ERR_print_errors_xx()
不打印任何内容。
所以问题是 - 如何获得实际的连接失败原因,例如“连接被拒绝”或“主机未解析”等?
使用的代码片段如下:
#include <cstdio>
#include <cstring>
#include <iostream>
#include "openssl/bio.h"
#include "openssl/err.h"
#include "openssl/ssl.h"
int main(int argc, char *argv[])
{
OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, nullptr);
std::cout << OpenSSL_version(OPENSSL_VERSION) << std::endl;
SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
if (!ctx)
{
std::cerr << "Error creating SSL context:" << std::endl;
ERR_print_errors_fp(stderr);
return 1;
}
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
if(!SSL_CTX_load_verify_locations(ctx,
"/etc/ssl/certs/ca-certificates.crt",
nullptr))
{
std::cerr << "Error loading trust store into SSL context" << std::endl;
ERR_print_errors_fp(stderr);
return 1;
}
BIO* cbio = BIO_new_ssl_connect(ctx);
SSL* ssl = nullptr;
BIO_get_ssl(cbio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
BIO_set_conn_hostname(cbio, "not_actually_existing_host.com:https");
BIO_set_nbio(cbio, 1);
std::cout << "Start connecting" << std::endl;
next:
if (BIO_do_connect(cbio) <= 0)
{
if (!BIO_should_retry(cbio))
{
std::cerr << "Error attempting to connect:" << std::endl;
ERR_print_errors_fp(stderr); // <---- PRINTS NOTHING!!!
BIO_free_all(cbio);
SSL_CTX_free(ctx);
return 1;
}
else goto next;
}
std::cout << "Connected OK" << std::endl;
BIO_free_all(cbio);
SSL_CTX_free(ctx);
return 0;
}
解决方案
这种方法最终对我有用:
const auto sysErrorCode = errno;
const auto sslErrorCode = ERR_get_error();
std::string errorDescription;
if (sslErrorCode != 0) errorDescription = ERR_error_string(sslErrorCode, nullptr);
if (sysErrorCode != 0)
{
if (!errorDescription.empty()) errorDescription += '\n';
errorDescription += "System error, code=" + std::to_string(sysErrorCode);
errorDescription += ", ";
errorDescription += strerror(sysErrorCode);
}
推荐阅读
- python - 从“兄弟”类继承?
- salesforce - SOQL 查询 - 如何通过将字段设为小写和比较来编写 SOQL 查询?
- windows - 从 findstr 输出中排除前 20 个字符
- javascript - 为什么 'async1 end' 会在以下脚本中的 'promise2' 和 'promise3' 之后记录?
- amazon-web-services - 启动 Go 服务器后 CodeDeploy AfterInstall 钩子不退出
- php - 如何使用php一次验证两个表单
- python - 如何将图像和注释拆分为目标检测任务的训练集、测试集和验证集?
- linux-kernel - 在内核模块中使用 memcpy 更改作为参数传递给导出函数的缓冲区内容时出现内核错误
- python - 在 pyenv 中使用时 pip 不响应
- html - CSS flexbox 的基本对齐问题