mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-01-19 04:52:08 +00:00
Phase 1
This commit is contained in:
@@ -20,14 +20,14 @@
|
||||
|
||||
### フェーズ 1: TLS 抽象化 API の追加
|
||||
|
||||
- [ ] `namespace detail::tls` を宣言エリアに追加
|
||||
- [ ] `ErrorCode` enum を定義
|
||||
- [ ] `TlsError` 構造体を定義
|
||||
- [ ] `tls_ctx_t`, `tls_session_t` 型を定義
|
||||
- [ ] 関数宣言を追加
|
||||
- [ ] 実装エリアに OpenSSL バックエンド実装を追加
|
||||
- [ ] 既存コードは変更していない(新 API 追加のみ)
|
||||
- [ ] `make test_split` 通過
|
||||
- [x] `namespace detail::tls` を宣言エリアに追加
|
||||
- [x] `ErrorCode` enum を定義
|
||||
- [x] `TlsError` 構造体を定義
|
||||
- [x] `tls_ctx_t`, `tls_session_t` 型を定義
|
||||
- [x] 関数宣言を追加
|
||||
- [x] 実装エリアに OpenSSL バックエンド実装を追加
|
||||
- [x] 既存コードは変更していない(新 API 追加のみ)
|
||||
- [x] `make test_split` 通過
|
||||
|
||||
### フェーズ 2: SSLClient と SSLSocketStream の移行
|
||||
|
||||
|
||||
535
httplib.h
535
httplib.h
@@ -2760,6 +2760,94 @@ bool is_field_value(const std::string &s);
|
||||
|
||||
} // namespace fields
|
||||
|
||||
// TLS abstraction layer
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
namespace tls {
|
||||
|
||||
// Error codes for TLS operations (backend-independent)
|
||||
enum class ErrorCode : int {
|
||||
Success = 0,
|
||||
WantRead, // Non-blocking: need to wait for read
|
||||
WantWrite, // Non-blocking: need to wait for write
|
||||
PeerClosed, // Peer closed the connection
|
||||
Fatal, // Unrecoverable error
|
||||
SyscallError, // System call error (check sys_errno)
|
||||
CertVerifyFailed, // Certificate verification failed
|
||||
HostnameMismatch, // Hostname verification failed
|
||||
};
|
||||
|
||||
// TLS error information
|
||||
struct TlsError {
|
||||
ErrorCode code = ErrorCode::Fatal;
|
||||
uint64_t backend_code = 0; // OpenSSL: ERR_get_error(), mbedTLS: return value
|
||||
int sys_errno = 0; // errno when SyscallError
|
||||
};
|
||||
|
||||
// Opaque handles (defined as void* for abstraction)
|
||||
using tls_ctx_t = void *;
|
||||
using tls_session_t = void *;
|
||||
using tls_cert_t = void *;
|
||||
|
||||
// Global initialization
|
||||
bool tls_global_init();
|
||||
void tls_global_cleanup();
|
||||
|
||||
// Client context
|
||||
tls_ctx_t tls_create_client_context();
|
||||
void tls_free_context(tls_ctx_t ctx);
|
||||
bool tls_set_min_version(tls_ctx_t ctx, int version);
|
||||
bool tls_load_ca_pem(tls_ctx_t ctx, const char *pem, size_t len);
|
||||
bool tls_load_ca_file(tls_ctx_t ctx, const char *file_path);
|
||||
bool tls_load_ca_dir(tls_ctx_t ctx, const char *dir_path);
|
||||
bool tls_load_system_certs(tls_ctx_t ctx);
|
||||
bool tls_set_client_cert_pem(tls_ctx_t ctx, const char *cert, const char *key,
|
||||
const char *password);
|
||||
bool tls_set_client_cert_file(tls_ctx_t ctx, const char *cert_path,
|
||||
const char *key_path, const char *password);
|
||||
|
||||
// Server context
|
||||
tls_ctx_t tls_create_server_context();
|
||||
bool tls_set_server_cert_pem(tls_ctx_t ctx, const char *cert, const char *key,
|
||||
const char *password);
|
||||
bool tls_set_server_cert_file(tls_ctx_t ctx, const char *cert_path,
|
||||
const char *key_path, const char *password);
|
||||
bool tls_set_client_ca_file(tls_ctx_t ctx, const char *ca_file,
|
||||
const char *ca_dir);
|
||||
void tls_set_verify_client(tls_ctx_t ctx, bool require);
|
||||
|
||||
// Session management
|
||||
tls_session_t tls_create_session(tls_ctx_t ctx, socket_t sock);
|
||||
void tls_free_session(tls_session_t session);
|
||||
bool tls_set_hostname(tls_session_t session, const char *hostname);
|
||||
|
||||
// Handshake (non-blocking capable)
|
||||
TlsError tls_connect(tls_session_t session);
|
||||
TlsError tls_accept(tls_session_t session);
|
||||
|
||||
// I/O (non-blocking capable)
|
||||
ssize_t tls_read(tls_session_t session, void *buf, size_t len, TlsError &err);
|
||||
ssize_t tls_write(tls_session_t session, const void *buf, size_t len,
|
||||
TlsError &err);
|
||||
int tls_pending(tls_session_t session);
|
||||
void tls_shutdown(tls_session_t session, bool graceful);
|
||||
|
||||
// Connection state
|
||||
bool tls_is_peer_closed(tls_session_t session);
|
||||
|
||||
// Certificate verification
|
||||
tls_cert_t tls_get_peer_cert(tls_session_t session);
|
||||
void tls_free_cert(tls_cert_t cert);
|
||||
bool tls_verify_hostname(tls_cert_t cert, const char *hostname);
|
||||
long tls_get_verify_result(tls_session_t session);
|
||||
|
||||
// Error information
|
||||
uint64_t tls_peek_error();
|
||||
uint64_t tls_get_error();
|
||||
std::string tls_error_string(uint64_t code);
|
||||
|
||||
} // namespace tls
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace stream {
|
||||
@@ -12314,6 +12402,453 @@ inline void ClientImpl::set_error_logger(ErrorLogger error_logger) {
|
||||
error_logger_ = std::move(error_logger);
|
||||
}
|
||||
|
||||
/*
|
||||
* TLS Abstraction Layer Implementation
|
||||
*/
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
namespace detail {
|
||||
namespace tls {
|
||||
|
||||
// Helper to map OpenSSL SSL_get_error to ErrorCode
|
||||
inline ErrorCode map_ssl_error(int ssl_error, int &out_errno) {
|
||||
switch (ssl_error) {
|
||||
case SSL_ERROR_NONE: return ErrorCode::Success;
|
||||
case SSL_ERROR_WANT_READ: return ErrorCode::WantRead;
|
||||
case SSL_ERROR_WANT_WRITE: return ErrorCode::WantWrite;
|
||||
case SSL_ERROR_ZERO_RETURN: return ErrorCode::PeerClosed;
|
||||
case SSL_ERROR_SYSCALL: out_errno = errno; return ErrorCode::SyscallError;
|
||||
case SSL_ERROR_SSL:
|
||||
default: return ErrorCode::Fatal;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool tls_global_init() {
|
||||
// OpenSSL 3.0+: OPENSSL_init_ssl() is called automatically
|
||||
return OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS |
|
||||
OPENSSL_INIT_LOAD_CRYPTO_STRINGS,
|
||||
nullptr) == 1;
|
||||
}
|
||||
|
||||
inline void tls_global_cleanup() {
|
||||
// OpenSSL 3.0+: cleanup is automatic
|
||||
}
|
||||
|
||||
inline tls_ctx_t tls_create_client_context() {
|
||||
SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
|
||||
if (ctx) {
|
||||
// Disable auto-retry to properly handle non-blocking I/O
|
||||
SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY);
|
||||
// Set minimum TLS version
|
||||
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
|
||||
}
|
||||
return static_cast<tls_ctx_t>(ctx);
|
||||
}
|
||||
|
||||
inline void tls_free_context(tls_ctx_t ctx) {
|
||||
if (ctx) { SSL_CTX_free(static_cast<SSL_CTX *>(ctx)); }
|
||||
}
|
||||
|
||||
inline bool tls_set_min_version(tls_ctx_t ctx, int version) {
|
||||
if (!ctx) return false;
|
||||
return SSL_CTX_set_min_proto_version(static_cast<SSL_CTX *>(ctx), version) ==
|
||||
1;
|
||||
}
|
||||
|
||||
inline bool tls_load_ca_pem(tls_ctx_t ctx, const char *pem, size_t len) {
|
||||
if (!ctx || !pem || len == 0) return false;
|
||||
|
||||
auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
|
||||
auto store = SSL_CTX_get_cert_store(ssl_ctx);
|
||||
if (!store) return false;
|
||||
|
||||
auto bio = BIO_new_mem_buf(pem, static_cast<int>(len));
|
||||
if (!bio) return false;
|
||||
|
||||
bool ok = true;
|
||||
X509 *cert = nullptr;
|
||||
while ((cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr)) !=
|
||||
nullptr) {
|
||||
if (X509_STORE_add_cert(store, cert) != 1) {
|
||||
// Ignore duplicate errors
|
||||
auto err = ERR_peek_last_error();
|
||||
if (ERR_GET_REASON(err) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
X509_free(cert);
|
||||
if (!ok) break;
|
||||
}
|
||||
BIO_free(bio);
|
||||
|
||||
// Clear any "no more certificates" errors
|
||||
ERR_clear_error();
|
||||
return ok;
|
||||
}
|
||||
|
||||
inline bool tls_load_ca_file(tls_ctx_t ctx, const char *file_path) {
|
||||
if (!ctx || !file_path) return false;
|
||||
return SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(ctx), file_path,
|
||||
nullptr) == 1;
|
||||
}
|
||||
|
||||
inline bool tls_load_ca_dir(tls_ctx_t ctx, const char *dir_path) {
|
||||
if (!ctx || !dir_path) return false;
|
||||
return SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(ctx), nullptr,
|
||||
dir_path) == 1;
|
||||
}
|
||||
|
||||
inline bool tls_load_system_certs(tls_ctx_t ctx) {
|
||||
if (!ctx) return false;
|
||||
auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows: Load from system certificate store
|
||||
auto store = SSL_CTX_get_cert_store(ssl_ctx);
|
||||
if (!store) return false;
|
||||
|
||||
auto hStore = CertOpenSystemStoreW(NULL, L"ROOT");
|
||||
if (!hStore) return false;
|
||||
|
||||
bool loaded_any = false;
|
||||
PCCERT_CONTEXT pContext = nullptr;
|
||||
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
|
||||
nullptr) {
|
||||
const unsigned char *data = pContext->pbCertEncoded;
|
||||
auto x509 = d2i_X509(nullptr, &data, pContext->cbCertEncoded);
|
||||
if (x509) {
|
||||
if (X509_STORE_add_cert(store, x509) == 1) { loaded_any = true; }
|
||||
X509_free(x509);
|
||||
}
|
||||
}
|
||||
CertCloseStore(hStore, 0);
|
||||
return loaded_any;
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
||||
// macOS: Load from Keychain
|
||||
auto store = SSL_CTX_get_cert_store(ssl_ctx);
|
||||
if (!store) return false;
|
||||
|
||||
CFArrayRef certs = nullptr;
|
||||
if (SecTrustCopyAnchorCertificates(&certs) != errSecSuccess || !certs) {
|
||||
return SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
|
||||
}
|
||||
|
||||
bool loaded_any = false;
|
||||
auto count = CFArrayGetCount(certs);
|
||||
for (CFIndex i = 0; i < count; i++) {
|
||||
auto cert = reinterpret_cast<SecCertificateRef>(
|
||||
const_cast<void *>(CFArrayGetValueAtIndex(certs, i)));
|
||||
CFDataRef der = SecCertificateCopyData(cert);
|
||||
if (der) {
|
||||
const unsigned char *data = CFDataGetBytePtr(der);
|
||||
auto x509 = d2i_X509(nullptr, &data, CFDataGetLength(der));
|
||||
if (x509) {
|
||||
if (X509_STORE_add_cert(store, x509) == 1) { loaded_any = true; }
|
||||
X509_free(x509);
|
||||
}
|
||||
CFRelease(der);
|
||||
}
|
||||
}
|
||||
CFRelease(certs);
|
||||
return loaded_any || SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
|
||||
#else
|
||||
return SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
|
||||
#endif
|
||||
|
||||
#else
|
||||
// Other Unix: use default verify paths
|
||||
return SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool tls_set_client_cert_pem(tls_ctx_t ctx, const char *cert,
|
||||
const char *key, const char *password) {
|
||||
if (!ctx || !cert || !key) return false;
|
||||
|
||||
auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
|
||||
|
||||
// Load certificate
|
||||
auto cert_bio = BIO_new_mem_buf(cert, -1);
|
||||
if (!cert_bio) return false;
|
||||
|
||||
auto x509 = PEM_read_bio_X509(cert_bio, nullptr, nullptr, nullptr);
|
||||
BIO_free(cert_bio);
|
||||
if (!x509) return false;
|
||||
|
||||
auto cert_ok = SSL_CTX_use_certificate(ssl_ctx, x509) == 1;
|
||||
X509_free(x509);
|
||||
if (!cert_ok) return false;
|
||||
|
||||
// Load private key
|
||||
auto key_bio = BIO_new_mem_buf(key, -1);
|
||||
if (!key_bio) return false;
|
||||
|
||||
auto pkey = PEM_read_bio_PrivateKey(key_bio, nullptr, nullptr,
|
||||
password ? const_cast<char *>(password)
|
||||
: nullptr);
|
||||
BIO_free(key_bio);
|
||||
if (!pkey) return false;
|
||||
|
||||
auto key_ok = SSL_CTX_use_PrivateKey(ssl_ctx, pkey) == 1;
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
return key_ok && SSL_CTX_check_private_key(ssl_ctx) == 1;
|
||||
}
|
||||
|
||||
inline bool tls_set_client_cert_file(tls_ctx_t ctx, const char *cert_path,
|
||||
const char *key_path,
|
||||
const char *password) {
|
||||
if (!ctx || !cert_path || !key_path) return false;
|
||||
|
||||
auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
|
||||
|
||||
if (password && password[0] != '\0') {
|
||||
SSL_CTX_set_default_passwd_cb_userdata(
|
||||
ssl_ctx, reinterpret_cast<void *>(const_cast<char *>(password)));
|
||||
}
|
||||
|
||||
return SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_path) == 1 &&
|
||||
SSL_CTX_use_PrivateKey_file(ssl_ctx, key_path, SSL_FILETYPE_PEM) == 1;
|
||||
}
|
||||
|
||||
inline tls_ctx_t tls_create_server_context() {
|
||||
SSL_CTX *ctx = SSL_CTX_new(TLS_server_method());
|
||||
if (ctx) {
|
||||
SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
|
||||
}
|
||||
return static_cast<tls_ctx_t>(ctx);
|
||||
}
|
||||
|
||||
inline bool tls_set_server_cert_pem(tls_ctx_t ctx, const char *cert,
|
||||
const char *key, const char *password) {
|
||||
// Same implementation as client cert
|
||||
return tls_set_client_cert_pem(ctx, cert, key, password);
|
||||
}
|
||||
|
||||
inline bool tls_set_server_cert_file(tls_ctx_t ctx, const char *cert_path,
|
||||
const char *key_path,
|
||||
const char *password) {
|
||||
// Same implementation as client cert file
|
||||
return tls_set_client_cert_file(ctx, cert_path, key_path, password);
|
||||
}
|
||||
|
||||
inline bool tls_set_client_ca_file(tls_ctx_t ctx, const char *ca_file,
|
||||
const char *ca_dir) {
|
||||
if (!ctx) return false;
|
||||
auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
|
||||
|
||||
if (ca_file || ca_dir) {
|
||||
if (SSL_CTX_load_verify_locations(ssl_ctx, ca_file, ca_dir) != 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Set CA list for client certificate request
|
||||
if (ca_file) {
|
||||
auto list = SSL_load_client_CA_file(ca_file);
|
||||
if (list) { SSL_CTX_set_client_CA_list(ssl_ctx, list); }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void tls_set_verify_client(tls_ctx_t ctx, bool require) {
|
||||
if (!ctx) return;
|
||||
SSL_CTX_set_verify(static_cast<SSL_CTX *>(ctx),
|
||||
require
|
||||
? (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
|
||||
: SSL_VERIFY_NONE,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
inline tls_session_t tls_create_session(tls_ctx_t ctx, socket_t sock) {
|
||||
if (!ctx || sock == INVALID_SOCKET) return nullptr;
|
||||
|
||||
auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
|
||||
SSL *ssl = SSL_new(ssl_ctx);
|
||||
if (!ssl) return nullptr;
|
||||
|
||||
// Disable auto-retry for proper non-blocking I/O handling
|
||||
SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
|
||||
|
||||
auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
|
||||
if (!bio) {
|
||||
SSL_free(ssl);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SSL_set_bio(ssl, bio, bio);
|
||||
return static_cast<tls_session_t>(ssl);
|
||||
}
|
||||
|
||||
inline void tls_free_session(tls_session_t session) {
|
||||
if (session) { SSL_free(static_cast<SSL *>(session)); }
|
||||
}
|
||||
|
||||
inline bool tls_set_hostname(tls_session_t session, const char *hostname) {
|
||||
if (!session || !hostname) return false;
|
||||
|
||||
auto ssl = static_cast<SSL *>(session);
|
||||
|
||||
// Set SNI (Server Name Indication)
|
||||
if (SSL_set_tlsext_host_name(ssl, hostname) != 1) { return false; }
|
||||
|
||||
// Enable hostname verification
|
||||
auto param = SSL_get0_param(ssl);
|
||||
if (!param) return false;
|
||||
|
||||
X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
|
||||
if (X509_VERIFY_PARAM_set1_host(param, hostname, 0) != 1) { return false; }
|
||||
|
||||
SSL_set_verify(ssl, SSL_VERIFY_PEER, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline TlsError tls_connect(tls_session_t session) {
|
||||
if (!session) { return TlsError(); }
|
||||
|
||||
auto ssl = static_cast<SSL *>(session);
|
||||
auto ret = SSL_connect(ssl);
|
||||
|
||||
TlsError err;
|
||||
if (ret == 1) {
|
||||
err.code = ErrorCode::Success;
|
||||
} else {
|
||||
auto ssl_err = SSL_get_error(ssl, ret);
|
||||
err.code = map_ssl_error(ssl_err, err.sys_errno);
|
||||
if (err.code == ErrorCode::Fatal) { err.backend_code = ERR_get_error(); }
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
inline TlsError tls_accept(tls_session_t session) {
|
||||
if (!session) { return TlsError(); }
|
||||
|
||||
auto ssl = static_cast<SSL *>(session);
|
||||
auto ret = SSL_accept(ssl);
|
||||
|
||||
TlsError err;
|
||||
if (ret == 1) {
|
||||
err.code = ErrorCode::Success;
|
||||
} else {
|
||||
auto ssl_err = SSL_get_error(ssl, ret);
|
||||
err.code = map_ssl_error(ssl_err, err.sys_errno);
|
||||
if (err.code == ErrorCode::Fatal) { err.backend_code = ERR_get_error(); }
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
inline ssize_t tls_read(tls_session_t session, void *buf, size_t len,
|
||||
TlsError &err) {
|
||||
if (!session || !buf) {
|
||||
err.code = ErrorCode::Fatal;
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto ssl = static_cast<SSL *>(session);
|
||||
auto ret = SSL_read(ssl, buf, static_cast<int>(len));
|
||||
|
||||
if (ret > 0) {
|
||||
err.code = ErrorCode::Success;
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto ssl_err = SSL_get_error(ssl, ret);
|
||||
err.code = map_ssl_error(ssl_err, err.sys_errno);
|
||||
if (err.code == ErrorCode::Fatal) { err.backend_code = ERR_get_error(); }
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline ssize_t tls_write(tls_session_t session, const void *buf, size_t len,
|
||||
TlsError &err) {
|
||||
if (!session || !buf) {
|
||||
err.code = ErrorCode::Fatal;
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto ssl = static_cast<SSL *>(session);
|
||||
auto ret = SSL_write(ssl, buf, static_cast<int>(len));
|
||||
|
||||
if (ret > 0) {
|
||||
err.code = ErrorCode::Success;
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto ssl_err = SSL_get_error(ssl, ret);
|
||||
err.code = map_ssl_error(ssl_err, err.sys_errno);
|
||||
if (err.code == ErrorCode::Fatal) { err.backend_code = ERR_get_error(); }
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline int tls_pending(tls_session_t session) {
|
||||
if (!session) return 0;
|
||||
return SSL_pending(static_cast<SSL *>(session));
|
||||
}
|
||||
|
||||
inline void tls_shutdown(tls_session_t session, bool graceful) {
|
||||
if (!session) return;
|
||||
|
||||
auto ssl = static_cast<SSL *>(session);
|
||||
if (graceful) {
|
||||
// First call sends close_notify
|
||||
if (SSL_shutdown(ssl) == 0) {
|
||||
// Second call waits for peer's close_notify
|
||||
SSL_shutdown(ssl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool tls_is_peer_closed(tls_session_t session) {
|
||||
if (!session) return true;
|
||||
|
||||
auto ssl = static_cast<SSL *>(session);
|
||||
char buf;
|
||||
auto ret = SSL_peek(ssl, &buf, 1);
|
||||
if (ret > 0) return false;
|
||||
|
||||
auto err = SSL_get_error(ssl, ret);
|
||||
return err == SSL_ERROR_ZERO_RETURN || err == SSL_ERROR_SYSCALL;
|
||||
}
|
||||
|
||||
inline tls_cert_t tls_get_peer_cert(tls_session_t session) {
|
||||
if (!session) return nullptr;
|
||||
return static_cast<tls_cert_t>(
|
||||
SSL_get1_peer_certificate(static_cast<SSL *>(session)));
|
||||
}
|
||||
|
||||
inline void tls_free_cert(tls_cert_t cert) {
|
||||
if (cert) { X509_free(static_cast<X509 *>(cert)); }
|
||||
}
|
||||
|
||||
inline bool tls_verify_hostname(tls_cert_t cert, const char *hostname) {
|
||||
if (!cert || !hostname) return false;
|
||||
|
||||
auto x509 = static_cast<X509 *>(cert);
|
||||
return X509_check_host(x509, hostname, strlen(hostname), 0, nullptr) == 1;
|
||||
}
|
||||
|
||||
inline long tls_get_verify_result(tls_session_t session) {
|
||||
if (!session) return X509_V_ERR_UNSPECIFIED;
|
||||
return SSL_get_verify_result(static_cast<SSL *>(session));
|
||||
}
|
||||
|
||||
inline uint64_t tls_peek_error() { return ERR_peek_last_error(); }
|
||||
|
||||
inline uint64_t tls_get_error() { return ERR_get_error(); }
|
||||
|
||||
inline std::string tls_error_string(uint64_t code) {
|
||||
char buf[256];
|
||||
ERR_error_string_n(static_cast<unsigned long>(code), buf, sizeof(buf));
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
} // namespace tls
|
||||
} // namespace detail
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SSL Implementation
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user