UniEvent
view release on metacpan or search on metacpan
clib/src/panda/unievent/ssl/SslFilter.cc view on Meta::CPAN
#include "../Stream.h"
#include <openssl/err.h>
#include <openssl/dh.h>
#include <openssl/conf.h>
#include <openssl/engine.h>
#include <openssl/ssl.h>
#ifdef SSL_OP_NO_RENEGOTIATION
#define RENEGOTIATION_DISABLED 1
#endif
#define PROFILE_STR profile == Profile::CLIENT ? "client" : "server"
namespace panda { namespace unievent { namespace ssl {
#define _ESSL(fmt, ...) do { \
char _log_buf_[1000]; \
int _log_size_ = snprintf(_log_buf_, 1000, "%s(): [%s] {%p} " fmt "\n", __func__, profile == Profile::CLIENT ? "client" : (profile == Profile::SERVER ? "server" : "no profile"), this->handle, ##__VA_ARGS__); \
panda_log_debug(string_view(_log_buf_, _log_size_)); \
} while(0)
const void* SslFilter::TYPE = &typeid(SslFilter);
static bool init_openssl_lib () {
SSL_library_init();
SSL_load_error_strings();
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
/* ERR_load_*(), ERR_func_error_string(), ERR_get_error_line(), ERR_get_error_line_data(), ERR_get_state()
* OpenSSL now loads error strings automatically so these functions are not needed.
* SEE FOR MORE: https://www.openssl.org/docs/manmaster/man7/migration_guide.html
*/
#else
ERR_load_BIO_strings();
ERR_load_crypto_strings();
#endif
OpenSSL_add_all_algorithms();
return true;
}
static const bool _init = init_openssl_lib();
static inline SslContext ssl_ctx_from_method (const SSL_METHOD* method) {
if (!method) method = SSLv23_client_method();
auto context = SSL_CTX_new(method);
if (!context) throw Error(make_ssl_error_code(SSL_ERROR_SSL));
return SslContext::attach(context);
}
static inline ErrorCode nest_ssl_error (const ErrorCode& err) { return err ? nest_error(errc::ssl_error, err) : err; }
struct SslWriteRequest : WriteRequest {
WriteRequest* orig;
bool final;
SslWriteRequest (WriteRequest* req = nullptr) : WriteRequest(), orig(req), final() {}
};
using SslWriteRequestSP = iptr<SslWriteRequest>;
SslFilter::SslFilter (Stream* stream, const SslContext &context, const SslFilterSP& server_filter)
: StreamFilter(stream, TYPE, PRIORITY), state(State::initial), profile(Profile::UNKNOWN), server_filter(server_filter)
{
panda_log_ctor();
if (stream->listening() && !SSL_CTX_check_private_key(context)) throw Error("SSL certificate&key needed to listen()");
#ifdef RENEGOTIATION_DISABLED
SSL_CTX_set_options(context, SSL_OP_NO_RENEGOTIATION);
#endif
init(context);
}
SslFilter::SslFilter (Stream* stream, const SSL_METHOD* method) : SslFilter(stream, ssl_ctx_from_method(method)) {
}
SslFilter::~SslFilter () {
panda_log_dtor();
SSL_free(ssl);
}
void SslFilter::init (const SslContext& context) {
auto raw_ssl = SSL_new(context);
if (!raw_ssl) throw Error(make_ssl_error_code(SSL_ERROR_SSL));
read_bio = BIO_new(SslBio::method());
if (!read_bio) throw Error(make_ssl_error_code(SSL_ERROR_SSL));
write_bio = BIO_new(SslBio::method());
if (!write_bio) throw Error(make_ssl_error_code(SSL_ERROR_SSL));
SslBio::set_handle(read_bio, handle);
SslBio::set_handle(write_bio, handle);
SSL_set_bio(raw_ssl, read_bio, write_bio);
ssl = raw_ssl;
}
void SslFilter::listen () {
if (!SSL_check_private_key(ssl)) throw Error("SSL certificate&key needed to listen()");
NextFilter::listen();
}
void SslFilter::reset () {
if (state == State::initial) return;
_ESSL("reset, state: %d, connecting: %d", (int)state, handle->connecting());
StreamFilterSP hold = this;
(void)hold;
source_request = nullptr;
ERR_clear_error();
state = State::initial;
// hard reset
auto oldssl = ssl;
init(SSL_get_SSL_CTX(oldssl));
SSL_free(oldssl);
//// soft reset - openssl docs say it should work, but IT DOES NOT WORK!
//if (!SSL_clear(ssl)) throw SSLError(SSL_ERROR_SSL);
NextFilter::reset();
}
void SslFilter::handle_connect (const ErrorCode& err, const ConnectRequestSP& req) {
_ESSL("ERR=%s", err.what().c_str());
//if (state == State::terminal) {
// we need this for ssl filters chaining
//NextFilter::on_connect(err, req);
//return;
//}
reset();
if (err) return NextFilter::handle_connect(err, req);
source_request = req;
start_ssl_connection(Profile::CLIENT);
}
void SslFilter::handle_connection (const StreamSP& client, const ErrorCode& err, const AcceptRequestSP& req) {
_ESSL("client: %p, err: %s", client.get(), err.what().c_str());
if (err) return NextFilter::handle_connection(client, err, req);
SslFilter* filter = new SslFilter(client, SSL_get_SSL_CTX(ssl), this);
client->add_filter(filter, true);
assert(req);
filter->source_request = req;
filter->start_ssl_connection(Profile::SERVER);
}
void SslFilter::start_ssl_connection (Profile profile) {
_ESSL();
this->profile = profile;
if (profile == Profile::CLIENT) SSL_set_connect_state(ssl);
else SSL_set_accept_state(ssl);
negotiate();
}
( run in 0.724 second using v1.01-cache-2.11-cpan-39bf76dae61 )