diff options
| author | Steven Fackler <[email protected]> | 2017-12-27 09:52:03 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2017-12-27 09:52:03 -0700 |
| commit | dcfe1dfa8bc106b6e196c701df71b495f978c8b2 (patch) | |
| tree | ebabd95bcc370b04370f7315dcedda5dd31a15ab /openssl/src/ssl/error.rs | |
| parent | Merge pull request #801 from sfackler/verify-error (diff) | |
| parent | Overhaul ssl error (diff) | |
| download | rust-openssl-dcfe1dfa8bc106b6e196c701df71b495f978c8b2.tar.xz rust-openssl-dcfe1dfa8bc106b6e196c701df71b495f978c8b2.zip | |
Merge pull request #802 from sfackler/ssl-error
Overhaul ssl error
Diffstat (limited to 'openssl/src/ssl/error.rs')
| -rw-r--r-- | openssl/src/ssl/error.rs | 154 |
1 files changed, 90 insertions, 64 deletions
diff --git a/openssl/src/ssl/error.rs b/openssl/src/ssl/error.rs index b0641dfd..c0bc80ae 100644 --- a/openssl/src/ssl/error.rs +++ b/openssl/src/ssl/error.rs @@ -1,3 +1,5 @@ +use ffi; +use libc::c_int; use std::error; use std::error::Error as StdError; use std::fmt; @@ -7,90 +9,114 @@ use error::ErrorStack; use ssl::MidHandshakeSslStream; use x509::X509VerifyResult; -/// An SSL error. -// FIXME this is missing variants +/// An error code returned from SSL functions. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ErrorCode(c_int); + +impl ErrorCode { + pub fn from_raw(raw: c_int) -> ErrorCode { + ErrorCode(raw) + } + + pub fn as_raw(&self) -> c_int { + self.0 + } + + /// The SSL session has been closed. + pub const ZERO_RETURN: ErrorCode = ErrorCode(ffi::SSL_ERROR_ZERO_RETURN); + + /// An attempt to read data from the underlying socket returned `WouldBlock`. + /// + /// Wait for read readiness and retry the operation. + pub const WANT_READ: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_READ); + + /// An attempt to write data to the underlying socket returned `WouldBlock`. + /// + /// Wait for write readiness and retry the operation. + pub const WANT_WRITE: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_WRITE); + + /// A non-recoverable IO error occurred. + pub const SYSCALL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SYSCALL); + + /// An error occurred in the SSL library. + pub const SSL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SSL); +} + #[derive(Debug)] -pub enum Error { - /// The SSL session has been closed by the other end - ZeroReturn, - /// An attempt to read data from the underlying socket returned - /// `WouldBlock`. Wait for read readiness and reattempt the operation. - WantRead(io::Error), - /// An attempt to write data from the underlying socket returned - /// `WouldBlock`. Wait for write readiness and reattempt the operation. - WantWrite(io::Error), - /// The client certificate callback requested to be called again. - WantX509Lookup, - /// An error reported by the underlying stream. - Stream(io::Error), - /// An error in the OpenSSL library. +pub(crate) enum InnerError { + Io(io::Error), Ssl(ErrorStack), } -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(self.description())?; - if let Some(err) = self.cause() { - write!(fmt, ": {}", err) - } else { - Ok(()) - } - } +/// An SSL error. +#[derive(Debug)] +pub struct Error { + pub(crate) code: ErrorCode, + pub(crate) cause: Option<InnerError>, } -impl error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::ZeroReturn => "The SSL session was closed by the other end", - Error::WantRead(_) => "A read attempt returned a `WouldBlock` error", - Error::WantWrite(_) => "A write attempt returned a `WouldBlock` error", - Error::WantX509Lookup => "The client certificate callback requested to be called again", - Error::Stream(_) => "The underlying stream reported an error", - Error::Ssl(_) => "The OpenSSL library reported an error", - } +impl Error { + pub fn code(&self) -> ErrorCode { + self.code } - fn cause(&self) -> Option<&error::Error> { - match *self { - Error::WantRead(ref err) => Some(err), - Error::WantWrite(ref err) => Some(err), - Error::Stream(ref err) => Some(err), - Error::Ssl(ref err) => Some(err), + pub fn io_error(&self) -> Option<&io::Error> { + match self.cause { + Some(InnerError::Io(ref e)) => Some(e), _ => None, } } -} -impl From<ErrorStack> for Error { - fn from(e: ErrorStack) -> Error { - Error::Ssl(e) + pub fn into_io_error(self) -> Result<io::Error, Error> { + match self.cause { + Some(InnerError::Io(e)) => Ok(e), + _ => Err(self), + } } -} -/// An error indicating that the operation can be immediately retried. -/// -/// OpenSSL's [`SSL_read`] and [`SSL_write`] functions can return `SSL_ERROR_WANT_READ` even when -/// the underlying socket is performing blocking IO in certain cases. When this happens, the -/// the operation can be immediately retried. -/// -/// To signal this event, the `io::Error` inside of [`Error::WantRead`] will be constructed around -/// a `RetryError`. -/// -/// [`SSL_read`]: https://www.openssl.org/docs/manmaster/man3/SSL_read.html -/// [`SSL_write`]: https://www.openssl.org/docs/manmaster/man3/SSL_write.html -/// [`Error::WantRead`]: enum.Error.html#variant.WantRead -#[derive(Debug)] -pub struct RetryError; + pub fn ssl_error(&self) -> Option<&ErrorStack> { + match self.cause { + Some(InnerError::Ssl(ref e)) => Some(e), + _ => None, + } + } +} -impl fmt::Display for RetryError { +impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(error::Error::description(self)) + match self.code { + ErrorCode::ZERO_RETURN => fmt.write_str("the SSL session has been shut down"), + ErrorCode::WANT_READ => match self.io_error() { + Some(_) => fmt.write_str("a nonblocking read call would have blocked"), + None => fmt.write_str("the operation should be retried"), + }, + ErrorCode::SYSCALL => match self.io_error() { + Some(err) => write!(fmt, "the inner stream returned an error: {}", err), + None => fmt.write_str("unexpected EOF"), + }, + ErrorCode::SSL => { + fmt.write_str("OpenSSL error")?; + if let Some(ref err) = self.ssl_error() { + write!(fmt, ": {}", err)? + } + Ok(()) + } + ErrorCode(code) => write!(fmt, "unknown error code {}", code), + } } } -impl error::Error for RetryError { +impl error::Error for Error { fn description(&self) -> &str { - "operation must be retried" + "an OpenSSL error" + } + + fn cause(&self) -> Option<&error::Error> { + match self.cause { + Some(InnerError::Io(ref e)) => Some(e), + Some(InnerError::Ssl(ref e)) => Some(e), + None => None, + } } } |