openssl_ktls/
error.rs

1use std::{error, ffi::c_int, fmt, io};
2
3use openssl::{error::ErrorStack, ssl::ErrorCode};
4
5#[derive(Debug)]
6pub(crate) enum InnerError {
7    Io(io::Error),
8    Ssl(ErrorStack),
9}
10
11/// An SSL error.
12#[derive(Debug)]
13pub struct Error {
14    pub(crate) code: ErrorCode,
15    pub(crate) cause: Option<InnerError>,
16}
17
18impl Error {
19    pub fn code(&self) -> ErrorCode {
20        self.code
21    }
22
23    pub fn io_error(&self) -> Option<&io::Error> {
24        match self.cause {
25            Some(InnerError::Io(ref e)) => Some(e),
26            _ => None,
27        }
28    }
29
30    pub fn into_io_error(self) -> Result<io::Error, Error> {
31        match self.cause {
32            Some(InnerError::Io(e)) => Ok(e),
33            _ => Err(self),
34        }
35    }
36
37    pub fn ssl_error(&self) -> Option<&ErrorStack> {
38        match self.cause {
39            Some(InnerError::Ssl(ref e)) => Some(e),
40            _ => None,
41        }
42    }
43
44    pub(crate) fn make(ret: c_int, ssl: &openssl::ssl::SslRef) -> Self {
45        use foreign_types_shared::ForeignTypeRef;
46        let code = unsafe { ErrorCode::from_raw(openssl_sys::SSL_get_error(ssl.as_ptr(), ret)) };
47
48        let cause = match code {
49            ErrorCode::SSL => Some(InnerError::Ssl(ErrorStack::get())),
50            ErrorCode::SYSCALL => {
51                let errs = ErrorStack::get();
52                if errs.errors().is_empty() {
53                    // get last error from io
54                    let e = std::io::Error::last_os_error();
55                    Some(InnerError::Io(e))
56                } else {
57                    Some(InnerError::Ssl(errs))
58                }
59            }
60            ErrorCode::ZERO_RETURN => None,
61            ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => {
62                // get last error from io
63                let e = std::io::Error::last_os_error();
64                Some(InnerError::Io(e))
65            }
66            _ => None,
67        };
68
69        Error { code, cause }
70    }
71
72    pub(crate) fn make_from_io(e: io::Error) -> Self {
73        Error {
74            code: ErrorCode::SYSCALL,
75            cause: Some(InnerError::Io(e)),
76        }
77    }
78}
79
80impl From<ErrorStack> for Error {
81    fn from(e: ErrorStack) -> Error {
82        Error {
83            code: ErrorCode::SSL,
84            cause: Some(InnerError::Ssl(e)),
85        }
86    }
87}
88
89impl fmt::Display for Error {
90    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
91        match self.code {
92            ErrorCode::ZERO_RETURN => fmt.write_str("the SSL session has been shut down"),
93            ErrorCode::WANT_READ => match self.io_error() {
94                Some(_) => fmt.write_str("a nonblocking read call would have blocked"),
95                None => fmt.write_str("the operation should be retried"),
96            },
97            ErrorCode::WANT_WRITE => match self.io_error() {
98                Some(_) => fmt.write_str("a nonblocking write call would have blocked"),
99                None => fmt.write_str("the operation should be retried"),
100            },
101            ErrorCode::SYSCALL => match self.io_error() {
102                Some(err) => write!(fmt, "{err}"),
103                None => fmt.write_str("unexpected EOF"),
104            },
105            ErrorCode::SSL => match self.ssl_error() {
106                Some(e) => write!(fmt, "{e}"),
107                None => fmt.write_str("OpenSSL error"),
108            },
109            _ => write!(fmt, "unknown error code {}", self.code.as_raw()),
110        }
111    }
112}
113
114impl error::Error for Error {
115    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
116        match self.cause {
117            Some(InnerError::Io(ref e)) => Some(e),
118            Some(InnerError::Ssl(ref e)) => Some(e),
119            None => None,
120        }
121    }
122}