diff --git a/src/linux_tcp/sslconnected.h b/src/linux_tcp/sslconnected.h index 707443e..78fc8e8 100644 --- a/src/linux_tcp/sslconnected.h +++ b/src/linux_tcp/sslconnected.h @@ -169,10 +169,7 @@ private: { // get a human-readable error string - const SslErrorPrinter printer{error}; - - // ensure it is null-terminated - const std::string message{printer.data(), printer.size()}; + const SslErrorPrinter message{error}; // report an error to user-space _parent->onError(this, message.data()); diff --git a/src/linux_tcp/sslerrorprinter.cpp b/src/linux_tcp/sslerrorprinter.cpp index 21e1493..45eb8dd 100644 --- a/src/linux_tcp/sslerrorprinter.cpp +++ b/src/linux_tcp/sslerrorprinter.cpp @@ -17,75 +17,86 @@ */ namespace AMQP { +/** + * Callback used for ERR_print_errors_cb + * @param str The string + * @param len The length + * @param ctx The context (this ptr) + * @return always 1 to signal to OpenSSL to continue + */ +int sslerrorprintercallback(const char *str, size_t len, void *ctx) +{ + // cast to ourselves + auto *self = static_cast(ctx); + + // if this is not the first line, add a newline character + if (!self->_message.empty()) self->_message.push_back('\n'); + + // store the message + self->_message.append(str, len); + + // continue with the next message + return 1; +} + /** * Constructor * @param retval return value of SSL_get_error (must be a proper error) * @throw std::bad_alloc if the BIO couldn't be allocated */ -SslErrorPrinter::SslErrorPrinter(int retval) : _bio(nullptr, &::BIO_free) +SslErrorPrinter::SslErrorPrinter(int retval) { // check the return value of the SSL_get_error function, which has a very unfortunate name. switch (retval) { - // It can be a syscall error. - case SSL_ERROR_SYSCALL: - { - // The SSL_ERROR_SYSCALL with errno value of 0 indicates unexpected - // EOF from the peer. This will be properly reported as SSL_ERROR_SSL - // with reason code SSL_R_UNEXPECTED_EOF_WHILE_READING in the - // OpenSSL 3.0 release because it is truly a TLS protocol error to - // terminate the connection without a SSL_shutdown(). - if (errno == 0) _strerror = "SSL_R_UNEXPECTED_EOF_WHILE_READING"; + // It can be a syscall error. + case SSL_ERROR_SYSCALL: - // Otherwise we ask the OS for a description of the error. - else _strerror = ::strerror(errno); + // The SSL_ERROR_SYSCALL with errno value of 0 indicates unexpected + // EOF from the peer. This will be properly reported as SSL_ERROR_SSL + // with reason code SSL_R_UNEXPECTED_EOF_WHILE_READING in the + // OpenSSL 3.0 release because it is truly a TLS protocol error to + // terminate the connection without a SSL_shutdown(). + if (errno == 0) _message = "SSL_R_UNEXPECTED_EOF_WHILE_READING"; - // done - break; - } - // It can be an error in OpenSSL. In that case the error stack contains - // more information. The documentation notes: if this error occurs then - // no further I/O operations should be performed on the connection and - // SSL_shutdown() must not be called. - case SSL_ERROR_SSL: - { - // create a new bio - _bio = decltype(_bio)(::BIO_new(::BIO_s_mem()), &::BIO_free); + // Otherwise we ask the OS for a description of the error. + else _message = ::strerror(errno); - // check if it was allocated - if (!_bio) throw std::bad_alloc(); + // done + break; - // invoke the convenience function to extract the whole error stack - ::ERR_print_errors(_bio.get()); + // It can be an error in OpenSSL. In that case the error stack contains + // more information. The documentation notes: if this error occurs then + // no further I/O operations should be performed on the connection and + // SSL_shutdown() must not be called. + case SSL_ERROR_SSL: - // get it from the bio - ::BIO_get_mem_ptr(_bio.get(), &_bufmem); + // collect all error lines + ::ERR_print_errors_cb(&sslerrorprintercallback, this); - // done - break; - } - default: - { - // we don't know what kind of error this is - _strerror = "unknown ssl error"; + // done + break; - // done - break; - } + default: + // we don't know what kind of error this is + _message = "unknown ssl error"; + + // done + break; } } /** - * data ptr + * data ptr (guaranteed null-terminated) * @return const char * */ -const char *SslErrorPrinter::data() const noexcept { return _strerror ? _strerror : _bufmem->data; } +const char *SslErrorPrinter::data() const noexcept { return _message.data(); } /** * length of the string * @return size_t */ -std::size_t SslErrorPrinter::size() const noexcept { return _strerror ? std::strlen(_strerror) : _bufmem->length; } +std::size_t SslErrorPrinter::size() const noexcept { return _message.size(); } /** * End of namespace AMQP diff --git a/src/linux_tcp/sslerrorprinter.h b/src/linux_tcp/sslerrorprinter.h index d361827..231d7dd 100644 --- a/src/linux_tcp/sslerrorprinter.h +++ b/src/linux_tcp/sslerrorprinter.h @@ -30,24 +30,6 @@ namespace AMQP { */ class SslErrorPrinter final { - /** - * pointer to the BIO - * @var unique_ptr - */ - std::unique_ptr<::BIO, decltype(&::BIO_free)> _bio; - - /** - * pointer to the (data, size) tuple - * @var ::BUF_MEM* - */ - ::BUF_MEM *_bufmem = nullptr; - - /** - * In case of a syscall error, the static string pointing to the error - * @var const char* - */ - const char *_strerror = nullptr; - public: /** * Constructor @@ -57,7 +39,7 @@ public: SslErrorPrinter(int retval); /** - * data ptr + * data ptr (guaranteed null-terminated) * @return const char * */ const char *data() const noexcept; @@ -67,6 +49,22 @@ public: * @return size_t */ std::size_t size() const noexcept; + +private: + /** + * The error message + * @var std::string + */ + std::string _message; + + /** + * Callback used for ERR_print_errors_cb + * @param str The string + * @param len The length + * @param ctx The context (this ptr) + * @return always 1 to signal to OpenSSL to continue + */ + friend int sslerrorprintercallback(const char *str, size_t len, void *ctx); }; /** diff --git a/src/linux_tcp/sslhandshake.h b/src/linux_tcp/sslhandshake.h index bd24d0e..d04e3b1 100644 --- a/src/linux_tcp/sslhandshake.h +++ b/src/linux_tcp/sslhandshake.h @@ -87,10 +87,7 @@ private: TcpState *reportError(const Monitor &monitor, int retval) { // extract a human-readable error string - const SslErrorPrinter printer{retval}; - - // ensure it's null-terminated - const std::string message{printer.data(), printer.size()}; + const SslErrorPrinter message{retval}; // we have an error - report this to the user _parent->onError(this, message.data());