diff options
| author | Steven Fackler <[email protected]> | 2016-01-30 12:55:22 -0800 |
|---|---|---|
| committer | Steven Fackler <[email protected]> | 2016-05-03 20:24:07 -0700 |
| commit | fa622326490e1dd27df4d42b4097ca574deedb3f (patch) | |
| tree | f6b60233b5f71847a3fbd87aa13a74f16fe79ddf /openssl/src/ssl | |
| parent | Remove deprecated methods (diff) | |
| download | rust-openssl-fa622326490e1dd27df4d42b4097ca574deedb3f.tar.xz rust-openssl-fa622326490e1dd27df4d42b4097ca574deedb3f.zip | |
Error reform
Diffstat (limited to 'openssl/src/ssl')
| -rw-r--r-- | openssl/src/ssl/bio.rs | 4 | ||||
| -rw-r--r-- | openssl/src/ssl/error.rs | 228 | ||||
| -rw-r--r-- | openssl/src/ssl/mod.rs | 123 | ||||
| -rw-r--r-- | openssl/src/ssl/tests/mod.rs | 2 |
4 files changed, 59 insertions, 298 deletions
diff --git a/openssl/src/ssl/bio.rs b/openssl/src/ssl/bio.rs index e53545d7..44893b88 100644 --- a/openssl/src/ssl/bio.rs +++ b/openssl/src/ssl/bio.rs @@ -9,7 +9,7 @@ use std::ptr; use std::slice; use std::sync::Arc; -use ssl::error::SslError; +use error::ErrorStack; pub struct StreamState<S> { pub stream: S, @@ -39,7 +39,7 @@ impl BioMethod { unsafe impl Send for BioMethod {} -pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, Arc<BioMethod>), SslError> { +pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, Arc<BioMethod>), ErrorStack> { let method = Arc::new(BioMethod::new::<S>()); let state = Box::new(StreamState { diff --git a/openssl/src/ssl/error.rs b/openssl/src/ssl/error.rs index ba0c7458..95213361 100644 --- a/openssl/src/ssl/error.rs +++ b/openssl/src/ssl/error.rs @@ -1,15 +1,8 @@ -pub use self::SslError::*; -pub use self::OpensslError::*; - -use libc::c_ulong; use std::error; use std::error::Error as StdError; use std::fmt; -use std::ffi::CStr; use std::io; -use std::str; - -use ffi; +use error::ErrorStack; /// An SSL error. #[derive(Debug)] @@ -27,30 +20,16 @@ pub enum Error { /// An error reported by the underlying stream. Stream(io::Error), /// An error in the OpenSSL library. - Ssl(Vec<OpenSslError>), + Ssl(ErrorStack), } impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { try!(fmt.write_str(self.description())); - match *self { - Error::Stream(ref err) => write!(fmt, ": {}", err), - Error::WantRead(ref err) => write!(fmt, ": {}", err), - Error::WantWrite(ref err) => write!(fmt, ": {}", err), - Error::Ssl(ref errs) => { - let mut first = true; - for err in errs { - if first { - try!(fmt.write_str(": ")); - first = false; - } else { - try!(fmt.write_str(", ")); - } - try!(fmt.write_str(&err.reason())) - } - Ok(()) - } - _ => Ok(()), + if let Some(err) = self.cause() { + write!(fmt, ": {}", err) + } else { + Ok(()) } } } @@ -72,201 +51,14 @@ impl error::Error for Error { Error::WantRead(ref err) => Some(err), Error::WantWrite(ref err) => Some(err), Error::Stream(ref err) => Some(err), + Error::Ssl(ref err) => Some(err), _ => None, } } } -/// An error reported from OpenSSL. -pub struct OpenSslError(c_ulong); - -impl OpenSslError { - /// Returns the contents of the OpenSSL error stack. - pub fn get_stack() -> Vec<OpenSslError> { - ffi::init(); - - let mut errs = vec![]; - loop { - match unsafe { ffi::ERR_get_error() } { - 0 => break, - err => errs.push(OpenSslError(err)), - } - } - errs - } - - /// Returns the raw OpenSSL error code for this error. - pub fn error_code(&self) -> c_ulong { - self.0 - } - - /// Returns the name of the library reporting the error. - pub fn library(&self) -> &'static str { - get_lib(self.0) +impl From<ErrorStack> for Error { + fn from(e: ErrorStack) -> Error { + Error::Ssl(e) } - - /// Returns the name of the function reporting the error. - pub fn function(&self) -> &'static str { - get_func(self.0) - } - - /// Returns the reason for the error. - pub fn reason(&self) -> &'static str { - get_reason(self.0) - } -} - -impl fmt::Debug for OpenSslError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("OpenSslError") - .field("library", &self.library()) - .field("function", &self.function()) - .field("reason", &self.reason()) - .finish() - } -} - -impl fmt::Display for OpenSslError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(&self.reason()) - } -} - -impl error::Error for OpenSslError { - fn description(&self) -> &str { - "An OpenSSL error" - } -} - -/// An SSL error -#[derive(Debug)] -pub enum SslError { - /// The underlying stream reported an error - StreamError(io::Error), - /// The SSL session has been closed by the other end - SslSessionClosed, - /// An error in the OpenSSL library - OpenSslErrors(Vec<OpensslError>), -} - -impl fmt::Display for SslError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - try!(fmt.write_str(error::Error::description(self))); - if let OpenSslErrors(ref errs) = *self { - let mut first = true; - for err in errs { - if first { - try!(fmt.write_str(": ")); - first = false; - } else { - try!(fmt.write_str(", ")); - } - match *err { - UnknownError { ref reason, .. } => try!(fmt.write_str(reason)), - } - } - } - - Ok(()) - } -} - -impl error::Error for SslError { - fn description(&self) -> &str { - match *self { - StreamError(_) => "The underlying stream reported an error", - SslSessionClosed => "The SSL session has been closed by the other end", - OpenSslErrors(_) => "An error in the OpenSSL library", - } - } - - fn cause(&self) -> Option<&error::Error> { - match *self { - StreamError(ref err) => Some(err as &error::Error), - _ => None, - } - } -} - -/// An error from the OpenSSL library -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum OpensslError { - /// An unknown error - UnknownError { - /// The library reporting the error - library: String, - /// The function reporting the error - function: String, - /// The reason for the error - reason: String, - }, -} - -impl OpensslError { - pub fn from_error_code(err: c_ulong) -> OpensslError { - ffi::init(); - UnknownError { - library: get_lib(err).to_owned(), - function: get_func(err).to_owned(), - reason: get_reason(err).to_owned(), - } - } -} - -fn get_lib(err: c_ulong) -> &'static str { - unsafe { - let cstr = ffi::ERR_lib_error_string(err); - let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); - str::from_utf8(bytes).unwrap() - } -} - -fn get_func(err: c_ulong) -> &'static str { - unsafe { - let cstr = ffi::ERR_func_error_string(err); - let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); - str::from_utf8(bytes).unwrap() - } -} - -fn get_reason(err: c_ulong) -> &'static str { - unsafe { - let cstr = ffi::ERR_reason_error_string(err); - let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); - str::from_utf8(bytes).unwrap() - } -} - -impl SslError { - /// Creates a new `OpenSslErrors` with the current contents of the error - /// stack. - pub fn get() -> SslError { - let mut errs = vec![]; - loop { - match unsafe { ffi::ERR_get_error() } { - 0 => break, - err => errs.push(OpensslError::from_error_code(err)), - } - } - OpenSslErrors(errs) - } - - /// Creates an `SslError` from the raw numeric error code. - pub fn from_error(err: c_ulong) -> SslError { - OpenSslErrors(vec![OpensslError::from_error_code(err)]) - } -} - -#[test] -fn test_uknown_error_should_have_correct_messages() { - let errs = match SslError::from_error(336032784) { - OpenSslErrors(errs) => errs, - _ => panic!("This should always be an `OpenSslErrors` variant."), - }; - - let UnknownError { ref library, ref function, ref reason } = errs[0]; - - assert_eq!(&library[..], "SSL routines"); - assert_eq!(&function[..], "SSL23_GET_SERVER_HELLO"); - assert_eq!(&reason[..], "sslv3 alert handshake failure"); } diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index c713aeb2..f9534bc2 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -25,9 +25,9 @@ use std::os::windows::io::{AsRawSocket, RawSocket}; use ffi; use ffi_extras; use dh::DH; -use ssl::error::{SslError, OpenSslError}; use x509::{X509StoreContext, X509FileType, X509}; use crypto::pkey::PKey; +use error::ErrorStack; pub mod error; mod bio; @@ -513,9 +513,9 @@ pub type ServerNameCallbackData<T> = fn(ssl: &mut Ssl, ad: &mut i32, data: &T) - // FIXME: macro may be instead of inlining? #[inline] -fn wrap_ssl_result(res: c_int) -> Result<(), SslError> { +fn wrap_ssl_result(res: c_int) -> Result<(), ErrorStack> { if res == 0 { - Err(SslError::get()) + Err(ErrorStack::get()) } else { Ok(()) } @@ -558,7 +558,7 @@ impl SslContext { } /// Creates a new SSL context. - pub fn new(method: SslMethod) -> Result<SslContext, SslError> { + pub fn new(method: SslMethod) -> Result<SslContext, ErrorStack> { init(); let ctx = try_ssl_null!(unsafe { ffi::SSL_CTX_new(method.to_raw()) }); @@ -647,7 +647,7 @@ impl SslContext { } } - pub fn set_tmp_dh(&self, dh: DH) -> Result<(), SslError> { + pub fn set_tmp_dh(&self, dh: DH) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi_extras::SSL_CTX_set_tmp_dh(self.ctx, dh.raw()) as i32 }) } @@ -656,13 +656,13 @@ impl SslContext { /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` /// environment variables if present, or defaults specified at OpenSSL /// build time otherwise. - pub fn set_default_verify_paths(&mut self) -> Result<(), SslError> { + pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi::SSL_CTX_set_default_verify_paths(self.ctx) }) } #[allow(non_snake_case)] /// Specifies the file that contains trusted CA certificates. - pub fn set_CA_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), SslError> { + pub fn set_CA_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), ErrorStack> { let file = CString::new(file.as_ref().as_os_str().to_str().expect("invalid utf8")).unwrap(); wrap_ssl_result(unsafe { ffi::SSL_CTX_load_verify_locations(self.ctx, file.as_ptr() as *const _, ptr::null()) @@ -677,7 +677,7 @@ impl SslContext { /// /// This value should be set when using client certificates, or each request will fail /// handshake and need to be restarted. - pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), SslError> { + pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi::SSL_CTX_set_session_id_context(self.ctx, sid_ctx.as_ptr(), sid_ctx.len() as u32) }) @@ -687,7 +687,7 @@ impl SslContext { pub fn set_certificate_file<P: AsRef<Path>>(&mut self, file: P, file_type: X509FileType) - -> Result<(), SslError> { + -> Result<(), ErrorStack> { let file = CString::new(file.as_ref().as_os_str().to_str().expect("invalid utf8")).unwrap(); wrap_ssl_result(unsafe { ffi::SSL_CTX_use_certificate_file(self.ctx, @@ -700,7 +700,7 @@ impl SslContext { pub fn set_certificate_chain_file<P: AsRef<Path>>(&mut self, file: P, file_type: X509FileType) - -> Result<(), SslError> { + -> Result<(), ErrorStack> { let file = CString::new(file.as_ref().as_os_str().to_str().expect("invalid utf8")).unwrap(); wrap_ssl_result(unsafe { ffi::SSL_CTX_use_certificate_chain_file(self.ctx, @@ -710,13 +710,13 @@ impl SslContext { } /// Specifies the certificate - pub fn set_certificate(&mut self, cert: &X509) -> Result<(), SslError> { + pub fn set_certificate(&mut self, cert: &X509) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi::SSL_CTX_use_certificate(self.ctx, cert.get_handle()) }) } /// Adds a certificate to the certificate chain presented together with the /// certificate specified using set_certificate() - pub fn add_extra_chain_cert(&mut self, cert: &X509) -> Result<(), SslError> { + pub fn add_extra_chain_cert(&mut self, cert: &X509) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi_extras::SSL_CTX_add_extra_chain_cert(self.ctx, cert.get_handle()) as c_int }) @@ -726,7 +726,7 @@ impl SslContext { pub fn set_private_key_file<P: AsRef<Path>>(&mut self, file: P, file_type: X509FileType) - -> Result<(), SslError> { + -> Result<(), ErrorStack> { let file = CString::new(file.as_ref().as_os_str().to_str().expect("invalid utf8")).unwrap(); wrap_ssl_result(unsafe { ffi::SSL_CTX_use_PrivateKey_file(self.ctx, @@ -736,16 +736,16 @@ impl SslContext { } /// Specifies the private key - pub fn set_private_key(&mut self, key: &PKey) -> Result<(), SslError> { + pub fn set_private_key(&mut self, key: &PKey) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi::SSL_CTX_use_PrivateKey(self.ctx, key.get_handle()) }) } /// Check consistency of private key and certificate - pub fn check_private_key(&mut self) -> Result<(), SslError> { + pub fn check_private_key(&mut self) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi::SSL_CTX_check_private_key(self.ctx) }) } - pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), SslError> { + pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { let cipher_list = CString::new(cipher_list).unwrap(); ffi::SSL_CTX_set_cipher_list(self.ctx, cipher_list.as_ptr() as *const _) @@ -757,7 +757,7 @@ impl SslContext { /// /// This method requires OpenSSL >= 1.0.2 or LibreSSL and the `ecdh_auto` feature. #[cfg(feature = "ecdh_auto")] - pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), SslError> { + pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi_extras::SSL_CTX_set_ecdh_auto(self.ctx, onoff as c_int) }) } @@ -922,7 +922,7 @@ impl Drop for Ssl { } impl Ssl { - pub fn new(ctx: &SslContext) -> Result<Ssl, SslError> { + pub fn new(ctx: &SslContext) -> Result<Ssl, ErrorStack> { let ssl = try_ssl_null!(unsafe { ffi::SSL_new(ctx.ctx) }); let ssl = Ssl { ssl: ssl }; Ok(ssl) @@ -950,9 +950,9 @@ impl Ssl { unsafe { ffi::SSL_write(self.ssl, buf.as_ptr() as *const c_void, len) } } - fn get_error(&self, ret: c_int) -> LibSslError { + fn get_error(&self, ret: c_int) -> LibErrorStack { let err = unsafe { ffi::SSL_get_error(self.ssl, ret) }; - match LibSslError::from_i32(err as i32) { + match LibErrorStack::from_i32(err as i32) { Some(err) => err, None => unreachable!(), } @@ -1013,7 +1013,7 @@ impl Ssl { } /// Sets the host name to be used with SNI (Server Name Indication). - pub fn set_hostname(&self, hostname: &str) -> Result<(), SslError> { + pub fn set_hostname(&self, hostname: &str) -> Result<(), ErrorStack> { let cstr = CString::new(hostname).unwrap(); let ret = unsafe { ffi_extras::SSL_set_tlsext_host_name(self.ssl, cstr.as_ptr() as *const _) @@ -1021,7 +1021,7 @@ impl Ssl { // For this case, 0 indicates failure. if ret == 0 { - Err(SslError::get()) + Err(ErrorStack::get()) } else { Ok(()) } @@ -1162,18 +1162,18 @@ impl Ssl { } } -macro_rules! make_LibSslError { +macro_rules! make_LibErrorStack { ($($variant:ident = $value:ident),+) => { #[derive(Debug)] #[repr(i32)] - enum LibSslError { + enum LibErrorStack { $($variant = ffi::$value),+ } - impl LibSslError { - fn from_i32(val: i32) -> Option<LibSslError> { + impl LibErrorStack { + fn from_i32(val: i32) -> Option<LibErrorStack> { match val { - $(ffi::$value => Some(LibSslError::$variant),)+ + $(ffi::$value => Some(LibErrorStack::$variant),)+ _ => None } } @@ -1181,7 +1181,7 @@ macro_rules! make_LibSslError { } } -make_LibSslError! { +make_LibErrorStack! { ErrorNone = SSL_ERROR_NONE, ErrorSsl = SSL_ERROR_SSL, ErrorWantRead = SSL_ERROR_WANT_READ, @@ -1241,31 +1241,31 @@ impl<S: Read + Write> SslStream<S> { } /// Creates an SSL/TLS client operating over the provided stream. - pub fn connect<T: IntoSsl>(ssl: T, stream: S) -> Result<Self, SslError> { + pub fn connect<T: IntoSsl>(ssl: T, stream: S) -> Result<Self, Error> { let ssl = try!(ssl.into_ssl()); let mut stream = Self::new_base(ssl, stream); let ret = stream.ssl.connect(); if ret > 0 { Ok(stream) } else { - match stream.make_old_error(ret) { - Some(err) => Err(err), - None => Ok(stream), + match stream.make_error(ret) { + Error::WantRead(..) | Error::WantWrite(..) => Ok(stream), + err => Err(err) } } } /// Creates an SSL/TLS server operating over the provided stream. - pub fn accept<T: IntoSsl>(ssl: T, stream: S) -> Result<Self, SslError> { + pub fn accept<T: IntoSsl>(ssl: T, stream: S) -> Result<Self, Error> { let ssl = try!(ssl.into_ssl()); let mut stream = Self::new_base(ssl, stream); let ret = stream.ssl.accept(); if ret > 0 { Ok(stream) } else { - match stream.make_old_error(ret) { - Some(err) => Err(err), - None => Ok(stream), + match stream.make_error(ret) { + Error::WantRead(..) | Error::WantWrite(..) => Ok(stream), + err => Err(err) } } } @@ -1302,10 +1302,10 @@ impl<S> SslStream<S> { self.check_panic(); match self.ssl.get_error(ret) { - LibSslError::ErrorSsl => Error::Ssl(OpenSslError::get_stack()), - LibSslError::ErrorSyscall => { - let errs = OpenSslError::get_stack(); - if errs.is_empty() { + LibErrorStack::ErrorSsl => Error::Ssl(ErrorStack::get()), + LibErrorStack::ErrorSyscall => { + let errs = ErrorStack::get(); + if errs.errors().is_empty() { if ret == 0 { Error::Stream(io::Error::new(io::ErrorKind::ConnectionAborted, "unexpected EOF observed")) @@ -1316,9 +1316,9 @@ impl<S> SslStream<S> { Error::Ssl(errs) } } - LibSslError::ErrorZeroReturn => Error::ZeroReturn, - LibSslError::ErrorWantWrite => Error::WantWrite(self.get_bio_error()), - LibSslError::ErrorWantRead => Error::WantRead(self.get_bio_error()), + LibErrorStack::ErrorZeroReturn => Error::ZeroReturn, + LibErrorStack::ErrorWantWrite => Error::WantWrite(self.get_bio_error()), + LibErrorStack::ErrorWantRead => Error::WantRead(self.get_bio_error()), err => { Error::Stream(io::Error::new(io::ErrorKind::Other, format!("unexpected error {:?}", err))) @@ -1326,37 +1326,6 @@ impl<S> SslStream<S> { } } - fn make_old_error(&mut self, ret: c_int) -> Option<SslError> { - self.check_panic(); - - match self.ssl.get_error(ret) { - LibSslError::ErrorSsl => Some(SslError::get()), - LibSslError::ErrorSyscall => { - let err = SslError::get(); - let count = match err { - SslError::OpenSslErrors(ref v) => v.len(), - _ => unreachable!(), - }; - if count == 0 { - if ret == 0 { - Some(SslError::StreamError(io::Error::new(io::ErrorKind::ConnectionAborted, - "unexpected EOF observed"))) - } else { - Some(SslError::StreamError(self.get_bio_error())) - } - } else { - Some(err) - } - } - LibSslError::ErrorZeroReturn => Some(SslError::SslSessionClosed), - LibSslError::ErrorWantWrite | LibSslError::ErrorWantRead => None, - err => { - Some(SslError::StreamError(io::Error::new(io::ErrorKind::Other, - format!("unexpected error {:?}", err)))) - } - } - } - #[cfg(feature = "nightly")] fn check_panic(&mut self) { if let Some(err) = unsafe { bio::take_panic::<S>(self.ssl.get_raw_rbio()) } { @@ -1437,17 +1406,17 @@ impl<S: Read + Write> Write for SslStream<S> { } pub trait IntoSsl { - fn into_ssl(self) -> Result<Ssl, SslError>; + fn into_ssl(self) -> Result<Ssl, ErrorStack>; } impl IntoSsl for Ssl { - fn into_ssl(self) -> Result<Ssl, SslError> { + fn into_ssl(self) -> Result<Ssl, ErrorStack> { Ok(self) } } impl<'a> IntoSsl for &'a SslContext { - fn into_ssl(self) -> Result<Ssl, SslError> { + fn into_ssl(self) -> Result<Ssl, ErrorStack> { Ssl::new(self) } } diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs index 2ada0065..7f0f3415 100644 --- a/openssl/src/ssl/tests/mod.rs +++ b/openssl/src/ssl/tests/mod.rs @@ -403,7 +403,7 @@ run_test!(ssl_verify_callback, |method, stream| { } }); - match SslStream::connect_generic(ssl, stream) { + match SslStream::connect(ssl, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err) } |