variant_ssl/ssl/
error.rs

1use libc::c_int;
2use std::error;
3use std::error::Error as StdError;
4use std::fmt;
5use std::io;
6
7use crate::error::ErrorStack;
8use crate::ssl::MidHandshakeSslStream;
9use crate::x509::X509VerifyResult;
10
11/// An error code returned from SSL functions.
12#[derive(Debug, Copy, Clone, PartialEq, Eq)]
13pub struct ErrorCode(c_int);
14
15impl ErrorCode {
16    /// The SSL session has been closed.
17    pub const ZERO_RETURN: ErrorCode = ErrorCode(ffi::SSL_ERROR_ZERO_RETURN);
18
19    /// The operation did not complete because an asynchronous engine is still
20    /// processing data.
21    ///
22    /// This will only occur if the mode has been set to SSL_MODE_ASYNC.
23    ///
24    /// Wait for async engine by using async job APIs and retry the operation.
25    #[cfg(ossl110)]
26    pub const WANT_ASYNC: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_ASYNC);
27
28    /// The asynchronous job could not be started because there were no async jobs
29    /// available in the pool.
30    ///
31    /// This will only occur if the mode has been set to SSL_MODE_ASYNC.
32    ///
33    /// Retry the operation after a currently executing asynchronous operation
34    /// for the current thread has completed
35    #[cfg(ossl110)]
36    pub const WANT_ASYNC_JOB: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_ASYNC_JOB);
37
38    /// An attempt to read data from the underlying socket returned `WouldBlock`.
39    ///
40    /// Wait for read readiness and retry the operation.
41    pub const WANT_READ: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_READ);
42
43    /// An attempt to write data to the underlying socket returned `WouldBlock`.
44    ///
45    /// Wait for write readiness and retry the operation.
46    pub const WANT_WRITE: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_WRITE);
47
48    /// A non-recoverable IO error occurred.
49    pub const SYSCALL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SYSCALL);
50
51    /// An error occurred in the SSL library.
52    pub const SSL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SSL);
53
54    /// The client hello callback indicated that it needed to be retried.
55    ///
56    /// Requires OpenSSL 1.1.1 or newer.
57    #[cfg(ossl111)]
58    pub const WANT_CLIENT_HELLO_CB: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_CLIENT_HELLO_CB);
59
60    /// The operation failed because the early callback indicated certificate lookup was
61    /// incomplete.
62    ///
63    /// The caller may retry the operation when lookup has completed.
64    ///
65    /// Requires BoringSSL.
66    #[cfg(any(boringssl, awslc))]
67    pub const PENDING_CERTIFICATE: ErrorCode = ErrorCode(ffi::SSL_ERROR_PENDING_CERTIFICATE);
68
69    /// The operation failed because a private key operation was unfinished.
70    ///
71    /// The caller may retry the operation when the private key operation is complete.
72    ///
73    /// Requires BoringSSL.
74    #[cfg(any(boringssl, awslc))]
75    pub const WANT_PRIVATE_KEY_OPERATION: ErrorCode =
76        ErrorCode(ffi::SSL_ERROR_WANT_PRIVATE_KEY_OPERATION);
77
78    pub fn from_raw(raw: c_int) -> ErrorCode {
79        ErrorCode(raw)
80    }
81
82    #[allow(clippy::trivially_copy_pass_by_ref)]
83    pub fn as_raw(&self) -> c_int {
84        self.0
85    }
86}
87
88#[derive(Debug)]
89pub(crate) enum InnerError {
90    Io(io::Error),
91    Ssl(ErrorStack),
92}
93
94/// An SSL error.
95#[derive(Debug)]
96pub struct Error {
97    pub(crate) code: ErrorCode,
98    pub(crate) cause: Option<InnerError>,
99}
100
101impl Error {
102    pub fn code(&self) -> ErrorCode {
103        self.code
104    }
105
106    pub fn io_error(&self) -> Option<&io::Error> {
107        match self.cause {
108            Some(InnerError::Io(ref e)) => Some(e),
109            _ => None,
110        }
111    }
112
113    pub fn into_io_error(self) -> Result<io::Error, Error> {
114        match self.cause {
115            Some(InnerError::Io(e)) => Ok(e),
116            _ => Err(self),
117        }
118    }
119
120    pub fn ssl_error(&self) -> Option<&ErrorStack> {
121        match self.cause {
122            Some(InnerError::Ssl(ref e)) => Some(e),
123            _ => None,
124        }
125    }
126}
127
128impl From<ErrorStack> for Error {
129    fn from(e: ErrorStack) -> Error {
130        Error {
131            code: ErrorCode::SSL,
132            cause: Some(InnerError::Ssl(e)),
133        }
134    }
135}
136
137impl fmt::Display for Error {
138    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
139        match self.code {
140            ErrorCode::ZERO_RETURN => fmt.write_str("the SSL session has been shut down"),
141            ErrorCode::WANT_READ => match self.io_error() {
142                Some(_) => fmt.write_str("a nonblocking read call would have blocked"),
143                None => fmt.write_str("the operation should be retried"),
144            },
145            ErrorCode::WANT_WRITE => match self.io_error() {
146                Some(_) => fmt.write_str("a nonblocking write call would have blocked"),
147                None => fmt.write_str("the operation should be retried"),
148            },
149            ErrorCode::SYSCALL => match self.io_error() {
150                Some(err) => write!(fmt, "{}", err),
151                None => fmt.write_str("unexpected EOF"),
152            },
153            ErrorCode::SSL => match self.ssl_error() {
154                Some(e) => write!(fmt, "{}", e),
155                None => fmt.write_str("OpenSSL error"),
156            },
157            ErrorCode(code) => write!(fmt, "unknown error code {}", code),
158        }
159    }
160}
161
162impl error::Error for Error {
163    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
164        match self.cause {
165            Some(InnerError::Io(ref e)) => Some(e),
166            Some(InnerError::Ssl(ref e)) => Some(e),
167            None => None,
168        }
169    }
170}
171
172/// An error or intermediate state after a TLS handshake attempt.
173// FIXME overhaul
174#[derive(Debug)]
175pub enum HandshakeError<S> {
176    /// Setup failed.
177    SetupFailure(ErrorStack),
178    /// The handshake failed.
179    Failure(MidHandshakeSslStream<S>),
180    /// The handshake encountered a `WouldBlock` error midway through.
181    ///
182    /// This error will never be returned for blocking streams.
183    WouldBlock(MidHandshakeSslStream<S>),
184}
185
186impl<S: fmt::Debug> StdError for HandshakeError<S> {
187    fn source(&self) -> Option<&(dyn StdError + 'static)> {
188        match *self {
189            HandshakeError::SetupFailure(ref e) => Some(e),
190            HandshakeError::Failure(ref s) | HandshakeError::WouldBlock(ref s) => Some(s.error()),
191        }
192    }
193}
194
195impl<S: fmt::Debug> fmt::Display for HandshakeError<S> {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        match *self {
198            HandshakeError::SetupFailure(ref e) => write!(f, "stream setup failed: {}", e)?,
199            HandshakeError::Failure(ref s) => {
200                write!(f, "the handshake failed: {}", s.error())?;
201                let verify = s.ssl().verify_result();
202                if verify != X509VerifyResult::OK {
203                    write!(f, ": {}", verify)?;
204                }
205            }
206            HandshakeError::WouldBlock(ref s) => {
207                write!(f, "the handshake was interrupted: {}", s.error())?;
208                let verify = s.ssl().verify_result();
209                if verify != X509VerifyResult::OK {
210                    write!(f, ": {}", verify)?;
211                }
212            }
213        }
214        Ok(())
215    }
216}
217
218impl<S> From<ErrorStack> for HandshakeError<S> {
219    fn from(e: ErrorStack) -> HandshakeError<S> {
220        HandshakeError::SetupFailure(e)
221    }
222}