Error descriptions and docs

This commit is contained in:
Kornel 2025-06-13 12:21:41 +01:00 committed by Kornel
parent 8d77a5d40e
commit a91bfdc67d
2 changed files with 93 additions and 36 deletions

View File

@ -15,7 +15,8 @@
//! Err(e) => println!("Parsing Error: {:?}", e), //! Err(e) => println!("Parsing Error: {:?}", e),
//! } //! }
//! ``` //! ```
use libc::{c_char, c_uint}; use libc::{c_char, c_int, c_uint};
use openssl_macros::corresponds;
use std::borrow::Cow; use std::borrow::Cow;
use std::error; use std::error;
use std::ffi::CStr; use std::ffi::CStr;
@ -80,7 +81,9 @@ impl fmt::Display for ErrorStack {
write!( write!(
fmt, fmt,
"[{}]", "[{}]",
err.reason_internal().unwrap_or("unknown reason") err.reason_internal()
.or_else(|| err.library())
.unwrap_or("unknown reason")
)?; )?;
} }
Ok(()) Ok(())
@ -101,7 +104,7 @@ impl From<ErrorStack> for fmt::Error {
} }
} }
/// An error reported from OpenSSL. /// A detailed error reported as part of an [`ErrorStack`].
#[derive(Clone)] #[derive(Clone)]
pub struct Error { pub struct Error {
code: c_uint, code: c_uint,
@ -179,7 +182,10 @@ impl Error {
} }
} }
/// Returns the raw OpenSSL error code for this error. /// Returns a raw OpenSSL **packed** error code for this error, which **can't be reliably compared to any error constant**.
///
/// Use [`Error::library_code()`] and [`Error::reason_code()`] instead.
/// Packed error codes are different than [SSL error codes](crate::ssl::ErrorCode).
#[must_use] #[must_use]
pub fn code(&self) -> c_uint { pub fn code(&self) -> c_uint {
self.code self.code
@ -201,10 +207,11 @@ impl Error {
} }
} }
/// Returns the raw OpenSSL error constant for the library reporting the /// Returns the raw OpenSSL error constant for the library reporting the error (`ERR_LIB_{name}`).
/// error. ///
/// Error [reason codes](Error::reason_code) are not globally unique, but scoped to each library.
#[must_use] #[must_use]
pub fn library_code(&self) -> libc::c_int { pub fn library_code(&self) -> c_int {
ffi::ERR_GET_LIB(self.code) ffi::ERR_GET_LIB(self.code)
} }
@ -226,9 +233,14 @@ impl Error {
} }
} }
/// Returns the raw OpenSSL error constant for the reason for the error. /// Returns [library-specific](Error::library_code) reason code corresponding to some of the `{lib}_R_{reason}` constants.
///
/// Reason codes are ambiguous, and different libraries reuse the same numeric values for different errors.
///
/// For `ERR_LIB_SYS` the reason code is `errno`. `ERR_LIB_USER` can use any values.
/// Other libraries may use [`ERR_R_*`](ffi::ERR_R_FATAL) or their own codes.
#[must_use] #[must_use]
pub fn reason_code(&self) -> libc::c_int { pub fn reason_code(&self) -> c_int {
ffi::ERR_GET_REASON(self.code) ffi::ERR_GET_REASON(self.code)
} }
@ -285,17 +297,19 @@ impl Error {
impl fmt::Debug for Error { impl fmt::Debug for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut builder = fmt.debug_struct("Error"); let mut builder = fmt.debug_struct("Error");
builder.field("code", &self.code()); builder.field("code", &self.code);
if let Some(library) = self.library() { if !self.is_internal() {
builder.field("library", &library); if let Some(library) = self.library() {
builder.field("library", &library);
}
builder.field("library_code", &self.library_code());
if let Some(reason) = self.reason() {
builder.field("reason", &reason);
}
builder.field("reason_code", &self.reason_code());
builder.field("file", &self.file());
builder.field("line", &self.line());
} }
builder.field("library_code", &self.library_code());
if let Some(reason) = self.reason() {
builder.field("reason", &reason);
}
builder.field("reason_code", &self.reason_code());
builder.field("file", &self.file());
builder.field("line", &self.line());
if let Some(data) = self.data() { if let Some(data) = self.data() {
builder.field("data", &data); builder.field("data", &data);
} }
@ -309,7 +323,7 @@ impl fmt::Display for Error {
fmt, fmt,
"{}\n\nCode: {:08X}\nLoc: {}:{}", "{}\n\nCode: {:08X}\nLoc: {}:{}",
self.reason_internal().unwrap_or("unknown TLS error"), self.reason_internal().unwrap_or("unknown TLS error"),
self.code(), &self.code,
self.file(), self.file(),
self.line() self.line()
) )

View File

@ -1,16 +1,20 @@
use crate::ffi; use crate::ffi;
use crate::x509::X509VerifyError; use crate::x509::X509VerifyError;
use libc::c_int; use libc::c_int;
use openssl_macros::corresponds;
use std::error; use std::error;
use std::error::Error as StdError; use std::error::Error as StdError;
use std::ffi::CStr;
use std::fmt; use std::fmt;
use std::io; use std::io;
use crate::error::ErrorStack; use crate::error::ErrorStack;
use crate::ssl::MidHandshakeSslStream; use crate::ssl::MidHandshakeSslStream;
/// An error code returned from SSL functions. /// `SSL_ERROR_*` error code returned from SSL functions.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] ///
/// This is different than [packed error codes](crate::error::Error).
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ErrorCode(c_int); pub struct ErrorCode(c_int);
impl ErrorCode { impl ErrorCode {
@ -50,16 +54,52 @@ impl ErrorCode {
/// An error occurred in the SSL library. /// An error occurred in the SSL library.
pub const SSL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SSL); pub const SSL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SSL);
/// Wrap an `SSL_ERROR_*` error code.
///
/// This is different than [packed error codes](crate::error::Error).
#[must_use] #[must_use]
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
pub fn from_raw(raw: c_int) -> ErrorCode { pub fn from_raw(raw: c_int) -> ErrorCode {
ErrorCode(raw) let code = ErrorCode(raw);
debug_assert!(
raw < 64 || code.description().is_some(),
"{raw} is not an SSL_ERROR_* code"
);
code
} }
/// An `SSL_ERROR_*` error code.
///
/// This is different than [packed error codes](crate::error::Error).
#[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::trivially_copy_pass_by_ref)]
#[must_use] #[must_use]
pub fn as_raw(&self) -> c_int { pub fn as_raw(&self) -> c_int {
self.0 self.0
} }
#[corresponds(SSL_error_description)]
pub fn description(self) -> Option<&'static str> {
unsafe {
let msg = ffi::SSL_error_description(self.0);
if msg.is_null() {
return None;
}
CStr::from_ptr(msg).to_str().ok()
}
}
}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.description().unwrap_or("error"), self.0)
}
}
impl fmt::Debug for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -68,7 +108,7 @@ pub(crate) enum InnerError {
Ssl(ErrorStack), Ssl(ErrorStack),
} }
/// An SSL error. /// A general SSL error, based on [`SSL_ERROR_*` error codes](ErrorCode).
#[derive(Debug)] #[derive(Debug)]
pub struct Error { pub struct Error {
pub(crate) code: ErrorCode, pub(crate) code: ErrorCode,
@ -76,6 +116,7 @@ pub struct Error {
} }
impl Error { impl Error {
/// An `SSL_ERROR_*` error code.
#[must_use] #[must_use]
pub fn code(&self) -> ErrorCode { pub fn code(&self) -> ErrorCode {
self.code self.code
@ -96,6 +137,7 @@ impl Error {
} }
} }
/// Stack of [library-specific errors](crate::error::Error), if available.
#[must_use] #[must_use]
pub fn ssl_error(&self) -> Option<&ErrorStack> { pub fn ssl_error(&self) -> Option<&ErrorStack> {
match self.cause { match self.cause {
@ -131,26 +173,27 @@ impl From<ErrorStack> for Error {
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self.code { let msg = match self.code {
ErrorCode::ZERO_RETURN => fmt.write_str("the SSL session has been shut down"), ErrorCode::ZERO_RETURN => "the SSL session has been shut down",
ErrorCode::WANT_READ => match self.io_error() { ErrorCode::WANT_READ => match self.io_error() {
Some(_) => fmt.write_str("a nonblocking read call would have blocked"), Some(_) => "a nonblocking read call would have blocked",
None => fmt.write_str("the operation should be retried"), None => "the operation should be retried",
}, },
ErrorCode::WANT_WRITE => match self.io_error() { ErrorCode::WANT_WRITE => match self.io_error() {
Some(_) => fmt.write_str("a nonblocking write call would have blocked"), Some(_) => "a nonblocking write call would have blocked",
None => fmt.write_str("the operation should be retried"), None => "the operation should be retried",
}, },
ErrorCode::SYSCALL => match self.io_error() { ErrorCode::SYSCALL => match self.io_error() {
Some(err) => write!(fmt, "{err}"), Some(err) => return err.fmt(fmt),
None => fmt.write_str("unexpected EOF"), None => "unexpected EOF",
}, },
ErrorCode::SSL => match self.ssl_error() { ErrorCode::SSL => match self.ssl_error() {
Some(e) => write!(fmt, "{e}"), Some(err) => return err.fmt(fmt),
None => fmt.write_str("unknown BoringSSL error"), None => "unknown BoringSSL error",
}, },
ErrorCode(code) => write!(fmt, "unknown error code {code}"), ErrorCode(code) => return code.fmt(fmt),
} };
fmt.write_str(msg)
} }
} }