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 )