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#[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 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 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}