diff options
Diffstat (limited to 'openssl/src/ssl')
| -rw-r--r-- | openssl/src/ssl/bio.rs | 8 | ||||
| -rw-r--r-- | openssl/src/ssl/error.rs | 274 | ||||
| -rw-r--r-- | openssl/src/ssl/mod.rs | 1256 | ||||
| -rw-r--r-- | openssl/src/ssl/tests/mod.rs | 251 |
4 files changed, 596 insertions, 1193 deletions
diff --git a/openssl/src/ssl/bio.rs b/openssl/src/ssl/bio.rs index b6f20cf2..c5663eb1 100644 --- a/openssl/src/ssl/bio.rs +++ b/openssl/src/ssl/bio.rs @@ -1,6 +1,6 @@ use libc::{c_char, c_int, c_long, c_void, strlen}; -use ffi::{self, BIO, BIO_CTRL_FLUSH, BIO_TYPE_NONE, BIO_new}; -use ffi_extras::{BIO_clear_retry_flags, BIO_set_retry_read, BIO_set_retry_write}; +use ffi::{self, BIO, BIO_CTRL_FLUSH, BIO_TYPE_NONE, BIO_new, BIO_clear_retry_flags, + BIO_set_retry_read, BIO_set_retry_write}; use std::any::Any; use std::io; use std::io::prelude::*; @@ -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 2459a473..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,247 +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) - } - - /// 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>), -} - -/// An error on a nonblocking stream. -#[derive(Debug)] -pub enum NonblockingSslError { - /// A standard SSL error occurred. - SslError(SslError), - /// The OpenSSL library wants data from the remote socket; - /// the caller should wait for read readiness. - WantRead, - /// The OpenSSL library wants to send data to the remote socket; - /// the caller should wait for write readiness. - WantWrite, -} - -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", - } +impl From<ErrorStack> for Error { + fn from(e: ErrorStack) -> Error { + Error::Ssl(e) } - - fn cause(&self) -> Option<&error::Error> { - match *self { - StreamError(ref err) => Some(err as &error::Error), - _ => None, - } - } -} - -impl fmt::Display for NonblockingSslError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(error::Error::description(self)) - } -} - -impl error::Error for NonblockingSslError { - fn description(&self) -> &str { - match *self { - NonblockingSslError::SslError(ref e) => e.description(), - NonblockingSslError::WantRead => { - "The OpenSSL library wants data from the remote socket" - } - NonblockingSslError::WantWrite => { - "The OpenSSL library want to send data to the remote socket" - } - } - } - - fn cause(&self) -> Option<&error::Error> { - match *self { - NonblockingSslError::SslError(ref e) => e.cause(), - _ => None, - } - } -} - -impl From<SslError> for NonblockingSslError { - fn from(e: SslError) -> NonblockingSslError { - NonblockingSslError::SslError(e) - } -} - -/// 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 d0954bc7..82ee3127 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -1,34 +1,31 @@ use libc::{c_int, c_void, c_long}; +use std::any::Any; use std::any::TypeId; +use std::cmp; use std::collections::HashMap; +use std::error as stderror; use std::ffi::{CStr, CString}; use std::fmt; use std::io; use std::io::prelude::*; use std::mem; -use std::str; -use std::net; +use std::ops::{Deref, DerefMut}; use std::path::Path; use std::ptr; -use std::sync::{Once, ONCE_INIT, Mutex, Arc}; -use std::cmp; -use std::any::Any; +use std::str; +use std::sync::{Mutex, Arc}; #[cfg(any(feature = "npn", feature = "alpn"))] use libc::{c_uchar, c_uint}; #[cfg(any(feature = "npn", feature = "alpn"))] use std::slice; use std::marker::PhantomData; -#[cfg(unix)] -use std::os::unix::io::{AsRawFd, RawFd}; -#[cfg(windows)] -use std::os::windows::io::{AsRawSocket, RawSocket}; - use ffi; -use ffi_extras; + +use init; use dh::DH; -use ssl::error::{NonblockingSslError, SslError, OpenSslError, OpensslError}; -use x509::{X509StoreContext, X509FileType, X509}; +use x509::{X509StoreContext, X509FileType, X509, X509Ref}; use crypto::pkey::PKey; +use error::ErrorStack; pub mod error; mod bio; @@ -40,81 +37,35 @@ use self::bio::BioMethod; #[doc(inline)] pub use ssl::error::Error; -extern "C" { - fn rust_SSL_clone(ssl: *mut ffi::SSL); - fn rust_SSL_CTX_clone(cxt: *mut ffi::SSL_CTX); -} - -static mut VERIFY_IDX: c_int = -1; -static mut SNI_IDX: c_int = -1; - -/// Manually initialize SSL. -/// It is optional to call this function and safe to do so more than once. -pub fn init() { - static mut INIT: Once = ONCE_INIT; - - unsafe { - INIT.call_once(|| { - ffi::init(); - - let verify_idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None, None, None); - assert!(verify_idx >= 0); - VERIFY_IDX = verify_idx; - - let sni_idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None, None, None); - assert!(sni_idx >= 0); - SNI_IDX = sni_idx; - }); - } -} - bitflags! { - pub flags SslContextOptions: u64 { - const SSL_OP_MICROSOFT_SESS_ID_BUG = ::ffi_extras::SSL_OP_MICROSOFT_SESS_ID_BUG, - const SSL_OP_NETSCAPE_CHALLENGE_BUG = ::ffi_extras::SSL_OP_NETSCAPE_CHALLENGE_BUG, - const SSL_OP_LEGACY_SERVER_CONNECT = ::ffi_extras::SSL_OP_LEGACY_SERVER_CONNECT, - const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = ::ffi_extras::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG, - const SSL_OP_TLSEXT_PADDING = ::ffi_extras::SSL_OP_TLSEXT_PADDING, - const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = ::ffi_extras::SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER, - const SSL_OP_SAFARI_ECDHE_ECDSA_BUG = ::ffi_extras::SSL_OP_SAFARI_ECDHE_ECDSA_BUG, - const SSL_OP_SSLEAY_080_CLIENT_DH_BUG = ::ffi_extras::SSL_OP_SSLEAY_080_CLIENT_DH_BUG, - const SSL_OP_TLS_D5_BUG = ::ffi_extras::SSL_OP_TLS_D5_BUG, - const SSL_OP_TLS_BLOCK_PADDING_BUG = ::ffi_extras::SSL_OP_TLS_BLOCK_PADDING_BUG, - const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = ::ffi_extras::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS, - const SSL_OP_NO_QUERY_MTU = ::ffi_extras::SSL_OP_NO_QUERY_MTU, - const SSL_OP_COOKIE_EXCHANGE = ::ffi_extras::SSL_OP_COOKIE_EXCHANGE, - const SSL_OP_NO_TICKET = ::ffi_extras::SSL_OP_NO_TICKET, - const SSL_OP_CISCO_ANYCONNECT = ::ffi_extras::SSL_OP_CISCO_ANYCONNECT, - const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = ::ffi_extras::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION, - const SSL_OP_NO_COMPRESSION = ::ffi_extras::SSL_OP_NO_COMPRESSION, - const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = ::ffi_extras::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, - const SSL_OP_SINGLE_ECDH_USE = ::ffi_extras::SSL_OP_SINGLE_ECDH_USE, - const SSL_OP_SINGLE_DH_USE = ::ffi_extras::SSL_OP_SINGLE_DH_USE, - const SSL_OP_CIPHER_SERVER_PREFERENCE = ::ffi_extras::SSL_OP_CIPHER_SERVER_PREFERENCE, - const SSL_OP_TLS_ROLLBACK_BUG = ::ffi_extras::SSL_OP_TLS_ROLLBACK_BUG, - const SSL_OP_NO_SSLV2 = ::ffi_extras::SSL_OP_NO_SSLv2, - const SSL_OP_NO_SSLV3 = ::ffi_extras::SSL_OP_NO_SSLv3, - const SSL_OP_NO_DTLSV1 = ::ffi_extras::SSL_OP_NO_DTLSv1, - const SSL_OP_NO_TLSV1 = ::ffi_extras::SSL_OP_NO_TLSv1, - const SSL_OP_NO_DTLSV1_2 = ::ffi_extras::SSL_OP_NO_DTLSv1_2, - const SSL_OP_NO_TLSV1_2 = ::ffi_extras::SSL_OP_NO_TLSv1_2, - const SSL_OP_NO_TLSV1_1 = ::ffi_extras::SSL_OP_NO_TLSv1_1, - const SSL_OP_NETSCAPE_CA_DN_BUG = ::ffi_extras::SSL_OP_NETSCAPE_CA_DN_BUG, - const SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = ::ffi_extras::SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG, - const SSL_OP_CRYPTOPRO_TLSEXT_BUG = ::ffi_extras::SSL_OP_CRYPTOPRO_TLSEXT_BUG, - const SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG = ::ffi_extras::SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG, - const SSL_OP_MSIE_SSLV2_RSA_PADDING = ::ffi_extras::SSL_OP_MSIE_SSLV2_RSA_PADDING, - const SSL_OP_PKCS1_CHECK_1 = ::ffi_extras::SSL_OP_PKCS1_CHECK_1, - const SSL_OP_PKCS1_CHECK_2 = ::ffi_extras::SSL_OP_PKCS1_CHECK_2, - const SSL_OP_EPHEMERAL_RSA = ::ffi_extras::SSL_OP_EPHEMERAL_RSA, - const SSL_OP_ALL = SSL_OP_MICROSOFT_SESS_ID_BUG.bits|SSL_OP_NETSCAPE_CHALLENGE_BUG.bits - |SSL_OP_LEGACY_SERVER_CONNECT.bits|SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG.bits - |SSL_OP_TLSEXT_PADDING.bits|SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER.bits - |SSL_OP_SAFARI_ECDHE_ECDSA_BUG.bits|SSL_OP_SSLEAY_080_CLIENT_DH_BUG.bits - |SSL_OP_TLS_D5_BUG.bits|SSL_OP_TLS_BLOCK_PADDING_BUG.bits - |SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS.bits|SSL_OP_CRYPTOPRO_TLSEXT_BUG.bits, - const SSL_OP_NO_SSL_MASK = SSL_OP_NO_SSLV2.bits|SSL_OP_NO_SSLV3.bits|SSL_OP_NO_TLSV1.bits - |SSL_OP_NO_TLSV1_1.bits|SSL_OP_NO_TLSV1_2.bits, + pub flags SslContextOptions: c_long { + const SSL_OP_MICROSOFT_SESS_ID_BUG = ffi::SSL_OP_MICROSOFT_SESS_ID_BUG, + const SSL_OP_NETSCAPE_CHALLENGE_BUG = ffi::SSL_OP_NETSCAPE_CHALLENGE_BUG, + const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = + ffi::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG, + const SSL_OP_TLSEXT_PADDING = ffi::SSL_OP_TLSEXT_PADDING, + const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = ffi::SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER, + const SSL_OP_SSLEAY_080_CLIENT_DH_BUG = ffi::SSL_OP_SSLEAY_080_CLIENT_DH_BUG, + const SSL_OP_TLS_D5_BUG = ffi::SSL_OP_TLS_D5_BUG, + const SSL_OP_TLS_BLOCK_PADDING_BUG = ffi::SSL_OP_TLS_BLOCK_PADDING_BUG, + const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = ffi::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS, + const SSL_OP_ALL = ffi::SSL_OP_ALL, + const SSL_OP_NO_QUERY_MTU = ffi::SSL_OP_NO_QUERY_MTU, + const SSL_OP_COOKIE_EXCHANGE = ffi::SSL_OP_COOKIE_EXCHANGE, + const SSL_OP_NO_TICKET = ffi::SSL_OP_NO_TICKET, + const SSL_OP_CISCO_ANYCONNECT = ffi::SSL_OP_CISCO_ANYCONNECT, + const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = + ffi::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION, + const SSL_OP_NO_COMPRESSION = ffi::SSL_OP_NO_COMPRESSION, + const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = + ffi::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, + const SSL_OP_SINGLE_ECDH_USE = ffi::SSL_OP_SINGLE_ECDH_USE, + const SSL_OP_SINGLE_DH_USE = ffi::SSL_OP_SINGLE_DH_USE, + const SSL_OP_CIPHER_SERVER_PREFERENCE = ffi::SSL_OP_CIPHER_SERVER_PREFERENCE, + const SSL_OP_TLS_ROLLBACK_BUG = ffi::SSL_OP_TLS_ROLLBACK_BUG, + const SSL_OP_NO_SSLV2 = ffi::SSL_OP_NO_SSLv2, + const SSL_OP_NO_SSLV3 = ffi::SSL_OP_NO_SSLv3, + const SSL_OP_NO_TLSV1 = ffi::SSL_OP_NO_TLSv1, } } @@ -148,68 +99,48 @@ pub enum SslMethod { } impl SslMethod { - unsafe fn to_raw(&self) -> *const ffi::SSL_METHOD { - match *self { - #[cfg(feature = "sslv2")] - SslMethod::Sslv2 => ffi::SSLv2_method(), - #[cfg(feature = "sslv3")] - SslMethod::Sslv3 => ffi::SSLv3_method(), - SslMethod::Tlsv1 => ffi::TLSv1_method(), - SslMethod::Sslv23 => ffi::SSLv23_method(), - #[cfg(feature = "tlsv1_1")] - SslMethod::Tlsv1_1 => ffi::TLSv1_1_method(), - #[cfg(feature = "tlsv1_2")] - SslMethod::Tlsv1_2 => ffi::TLSv1_2_method(), - #[cfg(feature = "dtlsv1")] - SslMethod::Dtlsv1 => ffi::DTLSv1_method(), - #[cfg(feature = "dtlsv1_2")] - SslMethod::Dtlsv1_2 => ffi::DTLSv1_2_method(), + fn to_raw(&self) -> *const ffi::SSL_METHOD { + unsafe { + match *self { + #[cfg(feature = "sslv2")] + SslMethod::Sslv2 => ffi::SSLv2_method(), + #[cfg(feature = "sslv3")] + SslMethod::Sslv3 => ffi::SSLv3_method(), + SslMethod::Tlsv1 => ffi::TLSv1_method(), + SslMethod::Sslv23 => ffi::SSLv23_method(), + #[cfg(feature = "tlsv1_1")] + SslMethod::Tlsv1_1 => ffi::TLSv1_1_method(), + #[cfg(feature = "tlsv1_2")] + SslMethod::Tlsv1_2 => ffi::TLSv1_2_method(), + #[cfg(feature = "dtlsv1")] + SslMethod::Dtlsv1 => ffi::DTLSv1_method(), + #[cfg(feature = "dtlsv1_2")] + SslMethod::Dtlsv1_2 => ffi::DTLSv1_2_method(), + } } } - unsafe fn from_raw(method: *const ffi::SSL_METHOD) -> Option<SslMethod> { - match method { - #[cfg(feature = "sslv2")] - x if x == ffi::SSLv2_method() => Some(SslMethod::Sslv2), - #[cfg(feature = "sslv3")] - x if x == ffi::SSLv3_method() => Some(SslMethod::Sslv3), - x if x == ffi::TLSv1_method() => Some(SslMethod::Tlsv1), - x if x == ffi::SSLv23_method() => Some(SslMethod::Sslv23), - #[cfg(feature = "tlsv1_1")] - x if x == ffi::TLSv1_1_method() => Some(SslMethod::Tlsv1_1), - #[cfg(feature = "tlsv1_2")] - x if x == ffi::TLSv1_2_method() => Some(SslMethod::Tlsv1_2), - #[cfg(feature = "dtlsv1")] - x if x == ffi::DTLSv1_method() => Some(SslMethod::Dtlsv1), - #[cfg(feature = "dtlsv1_2")] - x if x == ffi::DTLSv1_2_method() => Some(SslMethod::Dtlsv1_2), - _ => None, + fn from_raw(method: *const ffi::SSL_METHOD) -> Option<SslMethod> { + unsafe { + match method { + #[cfg(feature = "sslv2")] + x if x == ffi::SSLv2_method() => Some(SslMethod::Sslv2), + #[cfg(feature = "sslv3")] + x if x == ffi::SSLv3_method() => Some(SslMethod::Sslv3), + x if x == ffi::TLSv1_method() => Some(SslMethod::Tlsv1), + x if x == ffi::SSLv23_method() => Some(SslMethod::Sslv23), + #[cfg(feature = "tlsv1_1")] + x if x == ffi::TLSv1_1_method() => Some(SslMethod::Tlsv1_1), + #[cfg(feature = "tlsv1_2")] + x if x == ffi::TLSv1_2_method() => Some(SslMethod::Tlsv1_2), + #[cfg(feature = "dtlsv1")] + x if x == ffi::DTLSv1_method() => Some(SslMethod::Dtlsv1), + #[cfg(feature = "dtlsv1_2")] + x if x == ffi::DTLSv1_2_method() => Some(SslMethod::Dtlsv1_2), + _ => None, + } } } - - #[cfg(feature = "dtlsv1")] - pub fn is_dtlsv1(&self) -> bool { - *self == SslMethod::Dtlsv1 - } - - #[cfg(feature = "dtlsv1_2")] - pub fn is_dtlsv1_2(&self) -> bool { - *self == SslMethod::Dtlsv1_2 - } - - pub fn is_dtls(&self) -> bool { - self.is_dtlsv1() || self.is_dtlsv1_2() - } - - #[cfg(not(feature = "dtlsv1"))] - pub fn is_dtlsv1(&self) -> bool { - false - } - - #[cfg(not(feature = "dtlsv1_2"))] - pub fn is_dtlsv1_2(&self) -> bool { - false - } } /// Determines the type of certificate verification used @@ -292,47 +223,19 @@ fn get_new_ssl_idx<T>() -> c_int { } } -extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int { - unsafe { - let idx = ffi::SSL_get_ex_data_X509_STORE_CTX_idx(); - let ssl = ffi::X509_STORE_CTX_get_ex_data(x509_ctx, idx); - let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); - let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, VERIFY_IDX); - let verify: Option<VerifyCallback> = mem::transmute(verify); - - let ctx = X509StoreContext::new(x509_ctx); - - match verify { - None => preverify_ok, - Some(verify) => verify(preverify_ok != 0, &ctx) as c_int, - } - } -} - -extern "C" fn raw_verify_with_data<T>(preverify_ok: c_int, - x509_ctx: *mut ffi::X509_STORE_CTX) - -> c_int - where T: Any + 'static +extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int + where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send { unsafe { let idx = ffi::SSL_get_ex_data_X509_STORE_CTX_idx(); let ssl = ffi::X509_STORE_CTX_get_ex_data(x509_ctx, idx); let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); - - let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, VERIFY_IDX); - let verify: Option<VerifyCallbackData<T>> = mem::transmute(verify); - - let data = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::<T>()); - let data: &T = mem::transmute(data); + let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::<F>()); + let verify: &F = mem::transmute(verify); let ctx = X509StoreContext::new(x509_ctx); - let res = match verify { - None => preverify_ok, - Some(verify) => verify(preverify_ok != 0, &ctx, data) as c_int, - }; - - res + verify(preverify_ok != 0, &ctx) as c_int } } @@ -351,50 +254,30 @@ extern "C" fn ssl_raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_ST } } -extern "C" fn raw_sni(ssl: *mut ffi::SSL, ad: &mut c_int, _arg: *mut c_void) -> c_int { - unsafe { - let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); - let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, SNI_IDX); - let callback: Option<ServerNameCallback> = mem::transmute(callback); - rust_SSL_clone(ssl); - let mut s = Ssl { ssl: ssl }; - - let res = match callback { - None => ffi::SSL_TLSEXT_ERR_ALERT_FATAL, - Some(callback) => callback(&mut s, ad), - }; - - res - } -} - -extern "C" fn raw_sni_with_data<T>(ssl: *mut ffi::SSL, ad: &mut c_int, arg: *mut c_void) -> c_int - where T: Any + 'static +extern "C" fn raw_sni<F>(ssl: *mut ffi::SSL, al: *mut c_int, _arg: *mut c_void) -> c_int + where F: Fn(&mut SslRef) -> Result<(), SniError> + Any + 'static + Sync + Send { unsafe { let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); - - let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, SNI_IDX); - let callback: Option<ServerNameCallbackData<T>> = mem::transmute(callback); - rust_SSL_clone(ssl); - let mut s = Ssl { ssl: ssl }; - - let data: &T = mem::transmute(arg); - - let res = match callback { - None => ffi::SSL_TLSEXT_ERR_ALERT_FATAL, - Some(callback) => callback(&mut s, ad, &*data), - }; - - // Since data might be required on the next verification - // it is time to forget about it and avoid dropping - // data will be freed once OpenSSL considers it is time - // to free all context data - res + let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::<F>()); + let callback: &F = mem::transmute(callback); + let mut ssl = SslRef::from_ptr(ssl); + + match callback(&mut ssl) { + Ok(()) => ffi::SSL_TLSEXT_ERR_OK, + Err(SniError::Fatal(e)) => { + *al = e; + ffi::SSL_TLSEXT_ERR_ALERT_FATAL + } + Err(SniError::Warning(e)) => { + *al = e; + ffi::SSL_TLSEXT_ERR_ALERT_WARNING + } + Err(SniError::NoAck) => ffi::SSL_TLSEXT_ERR_NOACK, + } } } - #[cfg(any(feature = "npn", feature = "alpn"))] unsafe fn select_proto_using(ssl: *mut ffi::SSL, out: *mut *mut c_uchar, @@ -499,164 +382,91 @@ fn ssl_encode_byte_strings(strings: &[&[u8]]) -> Vec<u8> { enc } -/// The signature of functions that can be used to manually verify certificates -pub type VerifyCallback = fn(preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool; - -/// The signature of functions that can be used to manually verify certificates -/// when user-data should be carried for all verification process -pub type VerifyCallbackData<T> = fn(preverify_ok: bool, x509_ctx: &X509StoreContext, data: &T) - -> bool; - -/// The signature of functions that can be used to choose the context depending on the server name -pub type ServerNameCallback = fn(ssl: &mut Ssl, ad: &mut i32) -> i32; - -pub type ServerNameCallbackData<T> = fn(ssl: &mut Ssl, ad: &mut i32, data: &T) -> i32; +/// An error returned from an SNI callback. +pub enum SniError { + Fatal(c_int), + Warning(c_int), + NoAck, +} // 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(()) } } -/// An SSL context object -/// -/// Internally ref-counted, use `.clone()` in the same way as Rc and Arc. -pub struct SslContext { - ctx: *mut ffi::SSL_CTX, -} +/// A borrowed SSL context object. +pub struct SslContextRef<'a>(*mut ffi::SSL_CTX, PhantomData<&'a ()>); -unsafe impl Send for SslContext {} -unsafe impl Sync for SslContext {} - -impl Clone for SslContext { - fn clone(&self) -> Self { - unsafe { SslContext::new_ref(self.ctx) } +impl<'a> SslContextRef<'a> { + pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextRef<'a> { + SslContextRef(ctx, PhantomData) } -} -// TODO: add useful info here -impl fmt::Debug for SslContext { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "SslContext") - } -} - -impl Drop for SslContext { - fn drop(&mut self) { - unsafe { ffi::SSL_CTX_free(self.ctx) } - } -} - -impl SslContext { - // Create a new SslContext given an existing ref, and incriment ref-count appropriately. - unsafe fn new_ref(ctx: *mut ffi::SSL_CTX) -> SslContext { - rust_SSL_CTX_clone(ctx); - SslContext { ctx: ctx } - } - - /// Creates a new SSL context. - pub fn new(method: SslMethod) -> Result<SslContext, SslError> { - init(); - - let ctx = try_ssl_null!(unsafe { ffi::SSL_CTX_new(method.to_raw()) }); - - let ctx = SslContext { ctx: ctx }; - - // this is a bit dubious (?) - try!(ctx.set_mode(ffi::SSL_MODE_AUTO_RETRY)); - - if method.is_dtls() { - ctx.set_read_ahead(1); - } - - Ok(ctx) + pub fn as_ptr(&self) -> *mut ffi::SSL_CTX { + self.0 } /// Configures the certificate verification method for new connections. - pub fn set_verify(&mut self, mode: SslVerifyMode, verify: Option<VerifyCallback>) { + pub fn set_verify(&mut self, mode: SslVerifyMode) { unsafe { - ffi::SSL_CTX_set_ex_data(self.ctx, VERIFY_IDX, mem::transmute(verify)); - let f: extern "C" fn(c_int, *mut ffi::X509_STORE_CTX) -> c_int = raw_verify; - - ffi::SSL_CTX_set_verify(self.ctx, mode.bits as c_int, Some(f)); + ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, None); } } - /// Configures the certificate verification method for new connections also - /// carrying supplied data. - // Note: no option because there is no point to set data without providing - // a function handling it - pub fn set_verify_with_data<T>(&mut self, - mode: SslVerifyMode, - verify: VerifyCallbackData<T>, - data: T) - where T: Any + 'static + /// Configures the certificate verification method for new connections and + /// registers a verification callback. + pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F) + where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send { - let data = Box::new(data); unsafe { - ffi::SSL_CTX_set_ex_data(self.ctx, VERIFY_IDX, mem::transmute(Some(verify))); - ffi::SSL_CTX_set_ex_data(self.ctx, get_verify_data_idx::<T>(), mem::transmute(data)); - let f: extern "C" fn(c_int, *mut ffi::X509_STORE_CTX) -> c_int = - raw_verify_with_data::<T>; - - ffi::SSL_CTX_set_verify(self.ctx, mode.bits as c_int, Some(f)); + let verify = Box::new(verify); + ffi::SSL_CTX_set_ex_data(self.as_ptr(), get_verify_data_idx::<F>(), mem::transmute(verify)); + ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, Some(raw_verify::<F>)); } } /// Configures the server name indication (SNI) callback for new connections /// - /// obtain the server name with `get_servername` then set the corresponding context + /// Obtain the server name with `servername` then set the corresponding context /// with `set_ssl_context` - pub fn set_servername_callback(&mut self, callback: Option<ServerNameCallback>) { - unsafe { - ffi::SSL_CTX_set_ex_data(self.ctx, SNI_IDX, mem::transmute(callback)); - let f: extern "C" fn(_, _, _) -> _ = raw_sni; - let f: extern "C" fn() = mem::transmute(f); - ffi_extras::SSL_CTX_set_tlsext_servername_callback(self.ctx, Some(f)); - } - } - - /// Configures the server name indication (SNI) callback for new connections - /// carrying supplied data - pub fn set_servername_callback_with_data<T>(&mut self, - callback: ServerNameCallbackData<T>, - data: T) - where T: Any + 'static + pub fn set_servername_callback<F>(&mut self, callback: F) + where F: Fn(&mut SslRef) -> Result<(), SniError> + Any + 'static + Sync + Send { - let data = Box::new(data); unsafe { - ffi::SSL_CTX_set_ex_data(self.ctx, SNI_IDX, mem::transmute(Some(callback))); - - ffi_extras::SSL_CTX_set_tlsext_servername_arg(self.ctx, mem::transmute(data)); - let f: extern "C" fn(_, _, _) -> _ = raw_sni_with_data::<T>; + let callback = Box::new(callback); + ffi::SSL_CTX_set_ex_data(self.as_ptr(), + get_verify_data_idx::<F>(), + mem::transmute(callback)); + let f: extern "C" fn(_, _, _) -> _ = raw_sni::<F>; let f: extern "C" fn() = mem::transmute(f); - ffi_extras::SSL_CTX_set_tlsext_servername_callback(self.ctx, Some(f)); + ffi::SSL_CTX_set_tlsext_servername_callback(self.as_ptr(), Some(f)); } } /// Sets verification depth pub fn set_verify_depth(&mut self, depth: u32) { unsafe { - ffi::SSL_CTX_set_verify_depth(self.ctx, depth as c_int); + ffi::SSL_CTX_set_verify_depth(self.as_ptr(), depth as c_int); } } - pub fn set_read_ahead(&self, m: u32) { + pub fn set_read_ahead(&mut self, m: u32) { unsafe { - ffi_extras::SSL_CTX_set_read_ahead(self.ctx, m as c_long); + ffi::SSL_CTX_set_read_ahead(self.as_ptr(), m as c_long); } } - fn set_mode(&self, mode: c_long) -> Result<(), SslError> { - wrap_ssl_result(unsafe { ffi_extras::SSL_CTX_set_mode(self.ctx, mode) as c_int }) + fn set_mode(&mut self, mode: c_long) -> Result<(), ErrorStack> { + wrap_ssl_result(unsafe { ffi::SSL_CTX_set_mode(self.as_ptr(), mode) as c_int }) } - pub fn set_tmp_dh(&self, dh: DH) -> Result<(), SslError> { - wrap_ssl_result(unsafe { ffi_extras::SSL_CTX_set_tmp_dh(self.ctx, dh.raw()) as c_int }) + pub fn set_tmp_dh(&mut self, dh: &DH) -> Result<(), ErrorStack> { + wrap_ssl_result(unsafe { ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as i32 }) } /// Use the default locations of trusted certificates for verification. @@ -664,16 +474,16 @@ 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> { - wrap_ssl_result(unsafe { ffi::SSL_CTX_set_default_verify_paths(self.ctx) }) + pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> { + wrap_ssl_result(unsafe { ffi::SSL_CTX_set_default_verify_paths(self.as_ptr()) }) } #[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()) + ffi::SSL_CTX_load_verify_locations(self.as_ptr(), file.as_ptr() as *const _, ptr::null()) }) } @@ -685,9 +495,9 @@ 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) + ffi::SSL_CTX_set_session_id_context(self.as_ptr(), sid_ctx.as_ptr(), sid_ctx.len() as u32) }) } @@ -695,10 +505,10 @@ 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, + ffi::SSL_CTX_use_certificate_file(self.as_ptr(), file.as_ptr() as *const _, file_type as c_int) }) @@ -708,25 +518,25 @@ 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, + ffi::SSL_CTX_use_certificate_chain_file(self.as_ptr(), file.as_ptr() as *const _, file_type as c_int) }) } /// Specifies the certificate - pub fn set_certificate(&mut self, cert: &X509) -> Result<(), SslError> { - wrap_ssl_result(unsafe { ffi::SSL_CTX_use_certificate(self.ctx, cert.get_handle()) }) + pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { + wrap_ssl_result(unsafe { ffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr()) }) } /// 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: &X509Ref) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { - ffi_extras::SSL_CTX_add_extra_chain_cert(self.ctx, cert.get_handle()) as c_int + ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int }) } @@ -734,29 +544,29 @@ 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, + ffi::SSL_CTX_use_PrivateKey_file(self.as_ptr(), file.as_ptr() as *const _, file_type as c_int) }) } /// Specifies the private key - pub fn set_private_key(&mut self, key: &PKey) -> Result<(), SslError> { - wrap_ssl_result(unsafe { ffi::SSL_CTX_use_PrivateKey(self.ctx, key.get_handle()) }) + pub fn set_private_key(&mut self, key: &PKey) -> Result<(), ErrorStack> { + wrap_ssl_result(unsafe { ffi::SSL_CTX_use_PrivateKey(self.as_ptr(), key.as_ptr()) }) } /// Check consistency of private key and certificate - pub fn check_private_key(&mut self) -> Result<(), SslError> { - wrap_ssl_result(unsafe { ffi::SSL_CTX_check_private_key(self.ctx) }) + pub fn check_private_key(&mut self) -> Result<(), ErrorStack> { + wrap_ssl_result(unsafe { ffi::SSL_CTX_check_private_key(self.as_ptr()) }) } - 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 _) + ffi::SSL_CTX_set_cipher_list(self.as_ptr(), cipher_list.as_ptr() as *const _) }) } @@ -765,24 +575,22 @@ 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> { - wrap_ssl_result(unsafe { ffi_extras::SSL_CTX_set_ecdh_auto(self.ctx, onoff as c_int) }) + pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> { + wrap_ssl_result(unsafe { ffi::SSL_CTX_set_ecdh_auto(self.as_ptr(), onoff as c_long) as c_int }) } pub fn set_options(&mut self, option: SslContextOptions) -> SslContextOptions { - let raw_bits = option.bits(); - let ret = unsafe { ffi_extras::SSL_CTX_set_options(self.ctx, raw_bits) }; + let ret = unsafe { ffi::SSL_CTX_set_options(self.as_ptr(), option.bits()) }; SslContextOptions::from_bits(ret).unwrap() } - pub fn get_options(&mut self) -> SslContextOptions { - let ret = unsafe { ffi_extras::SSL_CTX_get_options(self.ctx) }; + pub fn options(&self) -> SslContextOptions { + let ret = unsafe { ffi::SSL_CTX_get_options(self.as_ptr()) }; SslContextOptions::from_bits(ret).unwrap() } pub fn clear_options(&mut self, option: SslContextOptions) -> SslContextOptions { - let raw_bits = option.bits(); - let ret = unsafe { ffi_extras::SSL_CTX_clear_options(self.ctx, raw_bits) }; + let ret = unsafe { ffi::SSL_CTX_clear_options(self.as_ptr(), option.bits()) }; SslContextOptions::from_bits(ret).unwrap() } @@ -799,16 +607,16 @@ impl SslContext { unsafe { // Attach the protocol list to the OpenSSL context structure, // so that we can refer to it within the callback. - ffi::SSL_CTX_set_ex_data(self.ctx, *NPN_PROTOS_IDX, mem::transmute(protocols)); + ffi::SSL_CTX_set_ex_data(self.as_ptr(), *NPN_PROTOS_IDX, mem::transmute(protocols)); // Now register the callback that performs the default protocol // matching based on the client-supported list of protocols that // has been saved. - ffi::SSL_CTX_set_next_proto_select_cb(self.ctx, + ffi::SSL_CTX_set_next_proto_select_cb(self.as_ptr(), raw_next_proto_select_cb, ptr::null_mut()); // Also register the callback to advertise these protocols, if a server socket is // created with the context. - ffi::SSL_CTX_set_next_protos_advertised_cb(self.ctx, + ffi::SSL_CTX_set_next_protos_advertised_cb(self.as_ptr(), raw_next_protos_advertise_cb, ptr::null_mut()); } @@ -827,28 +635,105 @@ impl SslContext { let protocols: Box<Vec<u8>> = Box::new(ssl_encode_byte_strings(protocols)); unsafe { // Set the context's internal protocol list for use if we are a server - ffi::SSL_CTX_set_alpn_protos(self.ctx, protocols.as_ptr(), protocols.len() as c_uint); + ffi::SSL_CTX_set_alpn_protos(self.as_ptr(), protocols.as_ptr(), protocols.len() as c_uint); // Rather than use the argument to the callback to contain our data, store it in the // ssl ctx's ex_data so that we can configure a function to free it later. In the // future, it might make sense to pull this into our internal struct Ssl instead of // leaning on openssl and using function pointers. - ffi::SSL_CTX_set_ex_data(self.ctx, *ALPN_PROTOS_IDX, mem::transmute(protocols)); + ffi::SSL_CTX_set_ex_data(self.as_ptr(), *ALPN_PROTOS_IDX, mem::transmute(protocols)); // Now register the callback that performs the default protocol // matching based on the client-supported list of protocols that // has been saved. - ffi::SSL_CTX_set_alpn_select_cb(self.ctx, raw_alpn_select_cb, ptr::null_mut()); + ffi::SSL_CTX_set_alpn_select_cb(self.as_ptr(), raw_alpn_select_cb, ptr::null_mut()); + } + } +} + +/// An owned SSL context object. +pub struct SslContext(SslContextRef<'static>); + +unsafe impl Send for SslContext {} +unsafe impl Sync for SslContext {} + +#[cfg(feature = "ssl_context_clone")] +impl Clone for SslContext { + /// Requires the `ssl_context_clone` feature. + fn clone(&self) -> Self { + unsafe { + ::c_helpers::rust_SSL_CTX_clone(self.as_ptr()); + SslContext::from_ptr(self.as_ptr()) } } } +// TODO: add useful info here +impl fmt::Debug for SslContext { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "SslContext") + } +} + +impl Drop for SslContext { + fn drop(&mut self) { + unsafe { ffi::SSL_CTX_free(self.as_ptr()) } + } +} + +impl Deref for SslContext { + type Target = SslContextRef<'static>; + + fn deref(&self) -> &SslContextRef<'static> { + &self.0 + } +} + +impl DerefMut for SslContext { + fn deref_mut(&mut self) -> &mut SslContextRef<'static> { + &mut self.0 + } +} + +impl SslContext { + /// Creates a new SSL context. + pub fn new(method: SslMethod) -> Result<SslContext, ErrorStack> { + init(); + + let mut ctx = unsafe { + let ctx = try_ssl_null!(ffi::SSL_CTX_new(method.to_raw())); + SslContext::from_ptr(ctx) + }; + + match method { + #[cfg(feature = "dtlsv1")] + SslMethod::Dtlsv1 => ctx.set_read_ahead(1), + #[cfg(feature = "dtlsv1_2")] + SslMethod::Dtlsv1_2 => ctx.set_read_ahead(1), + _ => {} + } + // this is a bit dubious (?) + try!(ctx.set_mode(ffi::SSL_MODE_AUTO_RETRY | ffi::SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)); + + Ok(ctx) + } + + pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContext { + SslContext(SslContextRef::from_ptr(ctx)) + } + + pub fn as_ptr(&self) -> *mut ffi::SSL_CTX { + (**self).as_ptr() + } +} + pub struct CipherBits { /// The number of secret bits used for the cipher. pub secret: i32, /// The number of bits processed by the chosen algorithm, if not None. pub algorithm: Option<i32>, + _p: (), } @@ -887,11 +772,13 @@ impl<'a> SslCipher<'a> { CipherBits { secret: secret_bits, algorithm: Some(*algo_bits), + _p: (), } } else { CipherBits { secret: secret_bits, algorithm: None, + _p: (), } } } @@ -913,79 +800,63 @@ impl<'a> SslCipher<'a> { } } +pub struct SslRef<'a>(*mut ffi::SSL, PhantomData<&'a ()>); -pub struct Ssl { - ssl: *mut ffi::SSL, -} +unsafe impl<'a> Send for SslRef<'a> {} +unsafe impl<'a> Sync for SslRef<'a> {} -unsafe impl Send for Ssl {} -unsafe impl Sync for Ssl {} - -impl fmt::Debug for Ssl { +impl<'a> fmt::Debug for SslRef<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("Ssl") + fmt.debug_struct("SslRef") .field("state", &self.state_string_long()) .finish() } } -impl Drop for Ssl { - fn drop(&mut self) { - unsafe { ffi::SSL_free(self.ssl) } +impl<'a> SslRef<'a> { + pub unsafe fn from_ptr(ssl: *mut ffi::SSL) -> SslRef<'a> { + SslRef(ssl, PhantomData) } -} - -impl Clone for Ssl { - /// # Deprecated - fn clone(&self) -> Ssl { - unsafe { rust_SSL_clone(self.ssl) }; - Ssl { ssl: self.ssl } + pub fn as_ptr(&self) -> *mut ffi::SSL { + self.0 } -} -impl Ssl { - pub fn new(ctx: &SslContext) -> Result<Ssl, SslError> { - let ssl = try_ssl_null!(unsafe { ffi::SSL_new(ctx.ctx) }); - let ssl = Ssl { ssl: ssl }; - Ok(ssl) + fn get_raw_rbio(&self) -> *mut ffi::BIO { + unsafe { ffi::SSL_get_rbio(self.as_ptr()) } } - fn get_raw_rbio(&self) -> *mut ffi::BIO { - unsafe { ffi::SSL_get_rbio(self.ssl) } + fn connect(&mut self) -> c_int { + unsafe { ffi::SSL_connect(self.as_ptr()) } } - fn connect(&self) -> c_int { - unsafe { ffi::SSL_connect(self.ssl) } + fn accept(&mut self) -> c_int { + unsafe { ffi::SSL_accept(self.as_ptr()) } } - fn accept(&self) -> c_int { - unsafe { ffi::SSL_accept(self.ssl) } + fn handshake(&mut self) -> c_int { + unsafe { ffi::SSL_do_handshake(self.as_ptr()) } } - fn read(&self, buf: &mut [u8]) -> c_int { + fn read(&mut self, buf: &mut [u8]) -> c_int { let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int; - unsafe { ffi::SSL_read(self.ssl, buf.as_ptr() as *mut c_void, len) } + unsafe { ffi::SSL_read(self.as_ptr(), buf.as_ptr() as *mut c_void, len) } } - fn write(&self, buf: &[u8]) -> c_int { + fn write(&mut self, buf: &[u8]) -> c_int { let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int; - unsafe { ffi::SSL_write(self.ssl, buf.as_ptr() as *const c_void, len) } + unsafe { ffi::SSL_write(self.as_ptr(), buf.as_ptr() as *const c_void, len) } } - fn get_error(&self, ret: c_int) -> LibSslError { - let err = unsafe { ffi::SSL_get_error(self.ssl, ret) }; - match LibSslError::from_i32(err as i32) { - Some(err) => err, - None => unreachable!(), - } + fn get_error(&self, ret: c_int) -> c_int { + unsafe { ffi::SSL_get_error(self.as_ptr(), ret) } } /// Sets the verification mode to be used during the handshake process. /// /// Use `set_verify_callback` to additionally add a callback. pub fn set_verify(&mut self, mode: SslVerifyMode) { - unsafe { ffi::SSL_set_verify(self.ssl, mode.bits as c_int, None) } + unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, None) } } /// Sets the certificate verification callback to be used during the @@ -1000,16 +871,16 @@ impl Ssl { { unsafe { let verify = Box::new(verify); - ffi::SSL_set_ex_data(self.ssl, + ffi::SSL_set_ex_data(self.as_ptr(), get_ssl_verify_data_idx::<F>(), mem::transmute(verify)); - ffi::SSL_set_verify(self.ssl, mode.bits as c_int, Some(ssl_raw_verify::<F>)); + ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, Some(ssl_raw_verify::<F>)); } } - pub fn get_current_cipher<'a>(&'a self) -> Option<SslCipher<'a>> { + pub fn current_cipher(&self) -> Option<SslCipher<'a>> { unsafe { - let ptr = ffi::SSL_get_current_cipher(self.ssl); + let ptr = ffi::SSL_get_current_cipher(self.as_ptr()); if ptr.is_null() { None @@ -1024,7 +895,7 @@ impl Ssl { pub fn state_string(&self) -> &'static str { let state = unsafe { - let ptr = ffi::SSL_state_string(self.ssl); + let ptr = ffi::SSL_state_string(self.as_ptr()); CStr::from_ptr(ptr as *const _) }; @@ -1033,7 +904,7 @@ impl Ssl { pub fn state_string_long(&self) -> &'static str { let state = unsafe { - let ptr = ffi::SSL_state_string_long(self.ssl); + let ptr = ffi::SSL_state_string_long(self.as_ptr()); CStr::from_ptr(ptr as *const _) }; @@ -1041,15 +912,15 @@ 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(&mut 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 _) + ffi::SSL_set_tlsext_host_name(self.as_ptr(), cstr.as_ptr() as *mut _) }; // For this case, 0 indicates failure. if ret == 0 { - Err(SslError::get()) + Err(ErrorStack::get()) } else { Ok(()) } @@ -1058,11 +929,11 @@ impl Ssl { /// Returns the certificate of the peer, if present. pub fn peer_certificate(&self) -> Option<X509> { unsafe { - let ptr = ffi::SSL_get_peer_certificate(self.ssl); + let ptr = ffi::SSL_get_peer_certificate(self.as_ptr()); if ptr.is_null() { None } else { - Some(X509::new(ptr, true)) + Some(X509::new(ptr)) } } } @@ -1070,7 +941,7 @@ impl Ssl { /// Returns the name of the protocol used for the connection, e.g. "TLSv1.2", "SSLv3", etc. pub fn version(&self) -> &'static str { let version = unsafe { - let ptr = ffi::SSL_get_version(self.ssl); + let ptr = ffi::SSL_get_version(self.as_ptr()); CStr::from_ptr(ptr as *const _) }; @@ -1090,7 +961,7 @@ impl Ssl { let mut len: c_uint = 0; // Get the negotiated protocol from the SSL instance. // `data` will point at a `c_uchar` array; `len` will contain the length of this array. - ffi::SSL_get0_next_proto_negotiated(self.ssl, &mut data, &mut len); + ffi::SSL_get0_next_proto_negotiated(self.as_ptr(), &mut data, &mut len); if data.is_null() { None @@ -1113,7 +984,7 @@ impl Ssl { let mut len: c_uint = 0; // Get the negotiated protocol from the SSL instance. // `data` will point at a `c_uchar` array; `len` will contain the length of this array. - ffi::SSL_get0_alpn_selected(self.ssl, &mut data, &mut len); + ffi::SSL_get0_alpn_selected(self.as_ptr(), &mut data, &mut len); if data.is_null() { None @@ -1126,7 +997,7 @@ impl Ssl { /// Returns the number of bytes remaining in the currently processed TLS /// record. pub fn pending(&self) -> usize { - unsafe { ffi::SSL_pending(self.ssl) as usize } + unsafe { ffi::SSL_pending(self.as_ptr()) as usize } } /// Returns the compression currently in use. @@ -1134,7 +1005,7 @@ impl Ssl { /// The result will be either None, indicating no compression is in use, or /// a string with the compression name. pub fn compression(&self) -> Option<String> { - let ptr = unsafe { ffi::SSL_get_current_compression(self.ssl) }; + let ptr = unsafe { ffi::SSL_get_current_compression(self.as_ptr()) }; if ptr == ptr::null() { return None; } @@ -1147,16 +1018,16 @@ impl Ssl { Some(s) } - pub fn get_ssl_method(&self) -> Option<SslMethod> { + pub fn ssl_method(&self) -> SslMethod { unsafe { - let method = ffi::SSL_get_ssl_method(self.ssl); - SslMethod::from_raw(method) + let method = ffi::SSL_get_ssl_method(self.as_ptr()); + SslMethod::from_raw(method).unwrap() } } /// Returns the server's name for the current connection - pub fn get_servername(&self) -> Option<String> { - let name = unsafe { ffi::SSL_get_servername(self.ssl, ffi::TLSEXT_NAMETYPE_host_name) }; + pub fn servername(&self) -> Option<String> { + let name = unsafe { ffi::SSL_get_servername(self.as_ptr(), ffi::TLSEXT_NAMETYPE_host_name) }; if name == ptr::null() { return None; } @@ -1164,61 +1035,64 @@ impl Ssl { unsafe { String::from_utf8(CStr::from_ptr(name as *const _).to_bytes().to_vec()).ok() } } - /// change the context corresponding to the current connection - /// - /// Returns a clone of the SslContext @ctx (ie: the new context). The old context is freed. - pub fn set_ssl_context(&self, ctx: &SslContext) -> SslContext { - // If duplication of @ctx's cert fails, this returns NULL. This _appears_ to only occur on - // allocation failures (meaning panicing is probably appropriate), but it might be nice to - // propogate the error. - assert!(unsafe { ffi::SSL_set_SSL_CTX(self.ssl, ctx.ctx) } != ptr::null_mut()); - - // FIXME: we return this reference here for compatibility, but it isn't actually required. - // This should be removed when a api-incompatabile version is to be released. - // - // ffi:SSL_set_SSL_CTX() returns copy of the ctx pointer passed to it, so it's easier for - // us to do the clone directly. - ctx.clone() - } - - /// obtain the context corresponding to the current connection - pub fn get_ssl_context(&self) -> SslContext { + /// Changes the context corresponding to the current connection. + pub fn set_ssl_context(&mut self, ctx: &SslContextRef) -> Result<(), ErrorStack> { unsafe { - let ssl_ctx = ffi::SSL_get_SSL_CTX(self.ssl); - SslContext::new_ref(ssl_ctx) + try_ssl_null!(ffi::SSL_set_SSL_CTX(self.as_ptr(), ctx.as_ptr())); } + Ok(()) } -} -macro_rules! make_LibSslError { - ($($variant:ident = $value:ident),+) => { - #[derive(Debug)] - #[repr(i32)] - enum LibSslError { - $($variant = ffi::$value),+ + /// Returns the context corresponding to the current connection + pub fn ssl_context(&self) -> SslContextRef<'a> { + unsafe { + let ssl_ctx = ffi::SSL_get_SSL_CTX(self.as_ptr()); + SslContextRef::from_ptr(ssl_ctx) } + } +} - impl LibSslError { - fn from_i32(val: i32) -> Option<LibSslError> { - match val { - $(ffi::$value => Some(LibSslError::$variant),)+ - _ => None - } - } - } +pub struct Ssl(SslRef<'static>); + +impl fmt::Debug for Ssl { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Ssl") + .field("state", &self.state_string_long()) + .finish() } } -make_LibSslError! { - ErrorNone = SSL_ERROR_NONE, - ErrorSsl = SSL_ERROR_SSL, - ErrorWantRead = SSL_ERROR_WANT_READ, - ErrorWantWrite = SSL_ERROR_WANT_WRITE, - ErrorWantX509Lookup = SSL_ERROR_WANT_X509_LOOKUP, - ErrorSyscall = SSL_ERROR_SYSCALL, - ErrorZeroReturn = SSL_ERROR_ZERO_RETURN, - ErrorWantConnect = SSL_ERROR_WANT_CONNECT, - ErrorWantAccept = SSL_ERROR_WANT_ACCEPT +impl Drop for Ssl { + fn drop(&mut self) { + unsafe { ffi::SSL_free(self.as_ptr()) } + } +} + +impl Deref for Ssl { + type Target = SslRef<'static>; + + fn deref(&self) -> &SslRef<'static> { + &self.0 + } +} + +impl DerefMut for Ssl { + fn deref_mut(&mut self) -> &mut SslRef<'static> { + &mut self.0 + } +} + +impl Ssl { + pub fn new(ctx: &SslContext) -> Result<Ssl, ErrorStack> { + unsafe { + let ssl = try_ssl_null!(ffi::SSL_new(ctx.as_ptr())); + Ok(Ssl::from_ptr(ssl)) + } + } + + pub unsafe fn from_ptr(ssl: *mut ffi::SSL) -> Ssl { + Ssl(SslRef::from_ptr(ssl)) + } } /// A stream wrapper which handles SSL encryption for an underlying stream. @@ -1228,19 +1102,7 @@ pub struct SslStream<S> { _p: PhantomData<S>, } -/// # Deprecated -/// -/// This method does not behave as expected and will be removed in a future -/// release. -impl<S: Clone + Read + Write> Clone for SslStream<S> { - fn clone(&self) -> SslStream<S> { - SslStream { - ssl: self.ssl.clone(), - _method: self._method.clone(), - _p: PhantomData, - } - } -} +unsafe impl<S: Send> Send for SslStream<S> {} impl<S> fmt::Debug for SslStream<S> where S: fmt::Debug @@ -1253,25 +1115,11 @@ impl<S> fmt::Debug for SslStream<S> } } -#[cfg(unix)] -impl<S: AsRawFd> AsRawFd for SslStream<S> { - fn as_raw_fd(&self) -> RawFd { - self.get_ref().as_raw_fd() - } -} - -#[cfg(windows)] -impl<S: AsRawSocket> AsRawSocket for SslStream<S> { - fn as_raw_socket(&self) -> RawSocket { - self.get_ref().as_raw_socket() - } -} - impl<S: Read + Write> SslStream<S> { fn new_base(ssl: Ssl, stream: S) -> Self { unsafe { let (bio, method) = bio::new(stream).unwrap(); - ffi::SSL_set_bio(ssl.ssl, bio, bio); + ffi::SSL_set_bio(ssl.as_ptr(), bio, bio); SslStream { ssl: ssl, @@ -1282,49 +1130,53 @@ 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> { - let ssl = try!(ssl.into_ssl()); + pub fn connect<T: IntoSsl>(ssl: T, stream: S) + -> Result<Self, HandshakeError<S>>{ + let ssl = try!(ssl.into_ssl().map_err(|e| { + HandshakeError::Failure(Error::Ssl(e)) + })); 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) { + e @ Error::WantWrite(_) | + e @ Error::WantRead(_) => { + Err(HandshakeError::Interrupted(MidHandshakeSslStream { + stream: stream, + error: e, + })) + } + err => Err(HandshakeError::Failure(err)), } } } /// Creates an SSL/TLS server operating over the provided stream. - pub fn accept<T: IntoSsl>(ssl: T, stream: S) -> Result<Self, SslError> { - let ssl = try!(ssl.into_ssl()); + pub fn accept<T: IntoSsl>(ssl: T, stream: S) + -> Result<Self, HandshakeError<S>> { + let ssl = try!(ssl.into_ssl().map_err(|e| { + HandshakeError::Failure(Error::Ssl(e)) + })); 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) { + e @ Error::WantWrite(_) | + e @ Error::WantRead(_) => { + Err(HandshakeError::Interrupted(MidHandshakeSslStream { + stream: stream, + error: e, + })) + } + err => Err(HandshakeError::Failure(err)), } } } - /// ### Deprecated - /// - /// Use `connect`. - pub fn connect_generic<T: IntoSsl>(ssl: T, stream: S) -> Result<SslStream<S>, SslError> { - Self::connect(ssl, stream) - } - - /// ### Deprecated - /// - /// Use `accept`. - pub fn accept_generic<T: IntoSsl>(ssl: T, stream: S) -> Result<SslStream<S>, SslError> { - Self::accept(ssl, stream) - } - /// Like `read`, but returns an `ssl::Error` rather than an `io::Error`. /// /// This is particularly useful with a nonblocking socket, where the error @@ -1352,63 +1204,112 @@ impl<S: Read + Write> SslStream<S> { } } -impl<S> SslStream<S> { - fn make_error(&mut self, ret: c_int) -> Error { - self.check_panic(); +/// An error or intermediate state after a TLS handshake attempt. +#[derive(Debug)] +pub enum HandshakeError<S> { + /// The handshake failed. + Failure(Error), + /// The handshake was interrupted midway through. + Interrupted(MidHandshakeSslStream<S>), +} - match self.ssl.get_error(ret) { - LibSslError::ErrorSsl => Error::Ssl(OpenSslError::get_stack()), - LibSslError::ErrorSyscall => { - let errs = OpenSslError::get_stack(); - if errs.is_empty() { - if ret == 0 { - Error::Stream(io::Error::new(io::ErrorKind::ConnectionAborted, - "unexpected EOF observed")) - } else { - Error::Stream(self.get_bio_error()) - } - } else { - Error::Ssl(errs) +impl<S: Any + fmt::Debug> stderror::Error for HandshakeError<S> { + fn description(&self) -> &str { + match *self { + HandshakeError::Failure(ref e) => e.description(), + HandshakeError::Interrupted(ref e) => e.error.description(), + } + } + + fn cause(&self) -> Option<&stderror::Error> { + match *self { + HandshakeError::Failure(ref e) => Some(e), + HandshakeError::Interrupted(ref e) => Some(&e.error), + } + } +} + +impl<S: Any + fmt::Debug> fmt::Display for HandshakeError<S> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(f.write_str(stderror::Error::description(self))); + if let Some(e) = stderror::Error::cause(self) { + try!(write!(f, ": {}", e)); + } + Ok(()) + } +} + +/// An SSL stream midway through the handshake process. +#[derive(Debug)] +pub struct MidHandshakeSslStream<S> { + stream: SslStream<S>, + error: Error, +} + +impl<S> MidHandshakeSslStream<S> { + /// Returns a shared reference to the inner stream. + pub fn get_ref(&self) -> &S { + self.stream.get_ref() + } + + /// Returns a mutable reference to the inner stream. + pub fn get_mut(&mut self) -> &mut S { + self.stream.get_mut() + } + + /// Returns a shared reference to the `Ssl` of the stream. + pub fn ssl(&self) -> &Ssl { + self.stream.ssl() + } + + /// Returns the underlying error which interrupted this handshake. + pub fn error(&self) -> &Error { + &self.error + } + + /// Restarts the handshake process. + pub fn handshake(mut self) -> Result<SslStream<S>, HandshakeError<S>> { + let ret = self.stream.ssl.handshake(); + if ret > 0 { + Ok(self.stream) + } else { + match self.stream.make_error(ret) { + e @ Error::WantWrite(_) | + e @ Error::WantRead(_) => { + self.error = e; + Err(HandshakeError::Interrupted(self)) } - } - LibSslError::ErrorZeroReturn => Error::ZeroReturn, - LibSslError::ErrorWantWrite => Error::WantWrite(self.get_bio_error()), - LibSslError::ErrorWantRead => Error::WantRead(self.get_bio_error()), - err => { - Error::Stream(io::Error::new(io::ErrorKind::Other, - format!("unexpected error {:?}", err))) + err => Err(HandshakeError::Failure(err)), } } } +} - fn make_old_error(&mut self, ret: c_int) -> Option<SslError> { +impl<S> SslStream<S> { + fn make_error(&mut self, ret: c_int) -> Error { 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 { + ffi::SSL_ERROR_SSL => Error::Ssl(ErrorStack::get()), + ffi::SSL_ERROR_SYSCALL => { + let errs = ErrorStack::get(); + if errs.errors().is_empty() { if ret == 0 { - Some(SslError::StreamError(io::Error::new(io::ErrorKind::ConnectionAborted, - "unexpected EOF observed"))) + Error::Stream(io::Error::new(io::ErrorKind::ConnectionAborted, + "unexpected EOF observed")) } else { - Some(SslError::StreamError(self.get_bio_error())) + Error::Stream(self.get_bio_error()) } } else { - Some(err) + Error::Ssl(errs) } } - LibSslError::ErrorZeroReturn => Some(SslError::SslSessionClosed), - LibSslError::ErrorWantWrite | - LibSslError::ErrorWantRead => None, + ffi::SSL_ERROR_ZERO_RETURN => Error::ZeroReturn, + ffi::SSL_ERROR_WANT_WRITE => Error::WantWrite(self.get_bio_error()), + ffi::SSL_ERROR_WANT_READ => Error::WantRead(self.get_bio_error()), err => { - Some(SslError::StreamError(io::Error::new(io::ErrorKind::Other, - format!("unexpected error {:?}", err)))) + Error::Stream(io::Error::new(io::ErrorKind::InvalidData, + format!("unexpected error {}", err))) } } } @@ -1461,20 +1362,6 @@ impl<S> SslStream<S> { } } -impl SslStream<::std::net::TcpStream> { - /// # Deprecated - /// - /// This method does not behave as expected and will be removed in a future - /// release. - pub fn try_clone(&self) -> io::Result<SslStream<::std::net::TcpStream>> { - Ok(SslStream { - ssl: self.ssl.clone(), - _method: self._method.clone(), - _p: PhantomData, - }) - } -} - impl<S: Read + Write> Read for SslStream<S> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { match self.ssl_read(buf) { @@ -1506,222 +1393,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) } } - -/// A utility type to help in cases where the use of SSL is decided at runtime. -#[derive(Debug)] -pub enum MaybeSslStream<S> - where S: Read + Write -{ - /// A connection using SSL - Ssl(SslStream<S>), - /// A connection not using SSL - Normal(S), -} - -impl<S> Read for MaybeSslStream<S> - where S: Read + Write -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - match *self { - MaybeSslStream::Ssl(ref mut s) => s.read(buf), - MaybeSslStream::Normal(ref mut s) => s.read(buf), - } - } -} - -impl<S> Write for MaybeSslStream<S> - where S: Read + Write -{ - fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - match *self { - MaybeSslStream::Ssl(ref mut s) => s.write(buf), - MaybeSslStream::Normal(ref mut s) => s.write(buf), - } - } - - fn flush(&mut self) -> io::Result<()> { - match *self { - MaybeSslStream::Ssl(ref mut s) => s.flush(), - MaybeSslStream::Normal(ref mut s) => s.flush(), - } - } -} - -impl<S> MaybeSslStream<S> - where S: Read + Write -{ - /// Returns a reference to the underlying stream. - pub fn get_ref(&self) -> &S { - match *self { - MaybeSslStream::Ssl(ref s) => s.get_ref(), - MaybeSslStream::Normal(ref s) => s, - } - } - - /// Returns a mutable reference to the underlying stream. - /// - /// ## Warning - /// - /// It is inadvisable to read from or write to the underlying stream. - pub fn get_mut(&mut self) -> &mut S { - match *self { - MaybeSslStream::Ssl(ref mut s) => s.get_mut(), - MaybeSslStream::Normal(ref mut s) => s, - } - } -} - -impl MaybeSslStream<net::TcpStream> { - /// Like `TcpStream::try_clone`. - pub fn try_clone(&self) -> io::Result<MaybeSslStream<net::TcpStream>> { - match *self { - MaybeSslStream::Ssl(ref s) => s.try_clone().map(MaybeSslStream::Ssl), - MaybeSslStream::Normal(ref s) => s.try_clone().map(MaybeSslStream::Normal), - } - } -} - -/// # Deprecated -/// -/// Use `SslStream` with `ssl_read` and `ssl_write`. -pub struct NonblockingSslStream<S>(SslStream<S>); - -impl<S: Clone + Read + Write> Clone for NonblockingSslStream<S> { - fn clone(&self) -> Self { - NonblockingSslStream(self.0.clone()) - } -} - -#[cfg(unix)] -impl<S: AsRawFd> AsRawFd for NonblockingSslStream<S> { - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -#[cfg(windows)] -impl<S: AsRawSocket> AsRawSocket for NonblockingSslStream<S> { - fn as_raw_socket(&self) -> RawSocket { - self.0.as_raw_socket() - } -} - -impl NonblockingSslStream<net::TcpStream> { - pub fn try_clone(&self) -> io::Result<NonblockingSslStream<net::TcpStream>> { - self.0.try_clone().map(NonblockingSslStream) - } -} - -impl<S> NonblockingSslStream<S> { - /// Returns a reference to the underlying stream. - pub fn get_ref(&self) -> &S { - self.0.get_ref() - } - - /// Returns a mutable reference to the underlying stream. - /// - /// ## Warning - /// - /// It is inadvisable to read from or write to the underlying stream as it - /// will most likely corrupt the SSL session. - pub fn get_mut(&mut self) -> &mut S { - self.0.get_mut() - } - - /// Returns a reference to the Ssl. - pub fn ssl(&self) -> &Ssl { - self.0.ssl() - } -} - -impl<S: Read + Write> NonblockingSslStream<S> { - /// Create a new nonblocking client ssl connection on wrapped `stream`. - /// - /// Note that this method will most likely not actually complete the SSL - /// handshake because doing so requires several round trips; the handshake will - /// be completed in subsequent read/write calls managed by your event loop. - pub fn connect<T: IntoSsl>(ssl: T, stream: S) -> Result<NonblockingSslStream<S>, SslError> { - SslStream::connect(ssl, stream).map(NonblockingSslStream) - } - - /// Create a new nonblocking server ssl connection on wrapped `stream`. - /// - /// Note that this method will most likely not actually complete the SSL - /// handshake because doing so requires several round trips; the handshake will - /// be completed in subsequent read/write calls managed by your event loop. - pub fn accept<T: IntoSsl>(ssl: T, stream: S) -> Result<NonblockingSslStream<S>, SslError> { - SslStream::accept(ssl, stream).map(NonblockingSslStream) - } - - fn convert_err(&self, err: Error) -> NonblockingSslError { - match err { - Error::ZeroReturn => SslError::SslSessionClosed.into(), - Error::WantRead(_) => NonblockingSslError::WantRead, - Error::WantWrite(_) => NonblockingSslError::WantWrite, - Error::WantX509Lookup => unreachable!(), - Error::Stream(e) => SslError::StreamError(e).into(), - Error::Ssl(e) => { - SslError::OpenSslErrors(e.iter() - .map(|e| OpensslError::from_error_code(e.error_code())) - .collect()) - .into() - } - } - } - - /// Read bytes from the SSL stream into `buf`. - /// - /// Given the SSL state machine, this method may return either `WantWrite` - /// or `WantRead` to indicate that your event loop should respectively wait - /// for write or read readiness on the underlying stream. Upon readiness, - /// repeat your `read()` call with the same arguments each time until you - /// receive an `Ok(count)`. - /// - /// An `SslError` return value, is terminal; do not re-attempt your read. - /// - /// As expected of a nonblocking API, this method will never block your - /// thread on I/O. - /// - /// On a return value of `Ok(count)`, count is the number of decrypted - /// plaintext bytes copied into the `buf` slice. - pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, NonblockingSslError> { - match self.0.ssl_read(buf) { - Ok(n) => Ok(n), - Err(Error::ZeroReturn) => Ok(0), - Err(e) => Err(self.convert_err(e)), - } - } - - /// Write bytes from `buf` to the SSL stream. - /// - /// Given the SSL state machine, this method may return either `WantWrite` - /// or `WantRead` to indicate that your event loop should respectively wait - /// for write or read readiness on the underlying stream. Upon readiness, - /// repeat your `write()` call with the same arguments each time until you - /// receive an `Ok(count)`. - /// - /// An `SslError` return value, is terminal; do not re-attempt your write. - /// - /// As expected of a nonblocking API, this method will never block your - /// thread on I/O. - /// - /// Given a return value of `Ok(count)`, count is the number of plaintext bytes - /// from the `buf` slice that were encrypted and written onto the stream. - pub fn write(&mut self, buf: &[u8]) -> Result<usize, NonblockingSslError> { - self.0.ssl_write(buf).map_err(|e| self.convert_err(e)) - } -} diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs index 5339f27e..bfcaa5e4 100644 --- a/openssl/src/ssl/tests/mod.rs +++ b/openssl/src/ssl/tests/mod.rs @@ -17,9 +17,9 @@ use crypto::hash::Type::SHA256; use ssl; use ssl::SSL_VERIFY_PEER; use ssl::SslMethod::Sslv23; -use ssl::SslMethod; -use ssl::error::NonblockingSslError; -use ssl::{SslContext, SslStream, VerifyCallback, NonblockingSslStream}; +use ssl::{SslMethod, HandshakeError}; +use ssl::error::Error; +use ssl::{SslContext, SslStream}; use x509::X509StoreContext; use x509::X509FileType; use x509::X509; @@ -133,6 +133,7 @@ impl Drop for Server { } #[cfg(feature = "dtlsv1")] +#[derive(Debug)] struct UdpConnected(UdpSocket); #[cfg(feature = "dtlsv1")] @@ -194,9 +195,9 @@ macro_rules! run_test( use std::net::TcpStream; use ssl; use ssl::SslMethod; - use ssl::{SslContext, Ssl, SslStream, VerifyCallback}; + use ssl::{SslContext, Ssl, SslStream}; use ssl::SSL_VERIFY_PEER; - use crypto::hash::Type::SHA1; + use crypto::hash::Type::{SHA1, SHA256}; use x509::X509StoreContext; use serialize::hex::FromHex; use super::Server; @@ -222,19 +223,19 @@ run_test!(new_ctx, |method, _| { }); run_test!(new_sslstream, |method, stream| { - SslStream::connect_generic(&SslContext::new(method).unwrap(), stream).unwrap(); + SslStream::connect(&SslContext::new(method).unwrap(), stream).unwrap(); }); run_test!(get_ssl_method, |method, _| { let ssl = Ssl::new(&SslContext::new(method).unwrap()).unwrap(); - assert_eq!(ssl.get_ssl_method(), Some(method)); + assert_eq!(ssl.ssl_method(), method); }); run_test!(verify_untrusted, |method, stream| { let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); - match SslStream::connect_generic(&ctx, stream) { + match SslStream::connect(&ctx, stream) { Ok(_) => panic!("expected failure"), Err(err) => println!("error {:?}", err), } @@ -242,127 +243,95 @@ run_test!(verify_untrusted, |method, stream| { run_test!(verify_trusted, |method, stream| { let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - match SslStream::connect_generic(&ctx, stream) { + match SslStream::connect(&ctx, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err), } }); run_test!(verify_untrusted_callback_override_ok, |method, stream| { - fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool { - true - } - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, _| true); - match SslStream::connect_generic(&ctx, stream) { + match SslStream::connect(&ctx, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err), } }); run_test!(verify_untrusted_callback_override_bad, |method, stream| { - fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool { - false - } - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, _| false); - assert!(SslStream::connect_generic(&ctx, stream).is_err()); + assert!(SslStream::connect(&ctx, stream).is_err()); }); run_test!(verify_trusted_callback_override_ok, |method, stream| { - fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool { - true - } - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, _| true); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - match SslStream::connect_generic(&ctx, stream) { + match SslStream::connect(&ctx, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err), } }); run_test!(verify_trusted_callback_override_bad, |method, stream| { - fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool { - false - } - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, _| false); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - assert!(SslStream::connect_generic(&ctx, stream).is_err()); + assert!(SslStream::connect(&ctx, stream).is_err()); }); run_test!(verify_callback_load_certs, |method, stream| { - fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { - assert!(x509_ctx.get_current_cert().is_some()); - true - } - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, x509_ctx| { + assert!(x509_ctx.current_cert().is_some()); + true + }); - assert!(SslStream::connect_generic(&ctx, stream).is_ok()); + assert!(SslStream::connect(&ctx, stream).is_ok()); }); run_test!(verify_trusted_get_error_ok, |method, stream| { - fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { - assert!(x509_ctx.get_error().is_none()); - true - } - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, x509_ctx| { + assert!(x509_ctx.error().is_none()); + true + }); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - assert!(SslStream::connect_generic(&ctx, stream).is_ok()); + assert!(SslStream::connect(&ctx, stream).is_ok()); }); run_test!(verify_trusted_get_error_err, |method, stream| { - fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { - assert!(x509_ctx.get_error().is_some()); - false - } - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, x509_ctx| { + assert!(x509_ctx.error().is_some()); + false + }); - assert!(SslStream::connect_generic(&ctx, stream).is_err()); + assert!(SslStream::connect(&ctx, stream).is_err()); }); run_test!(verify_callback_data, |method, stream| { - fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext, node_id: &Vec<u8>) -> bool { - let cert = x509_ctx.get_current_cert(); - match cert { - None => false, - Some(cert) => { - let fingerprint = cert.fingerprint(SHA1).unwrap(); - &fingerprint == node_id - } - } - } let mut ctx = SslContext::new(method).unwrap(); // Node id was generated as SHA256 hash of certificate "test/cert.pem" @@ -371,10 +340,19 @@ run_test!(verify_callback_data, |method, stream| { // Please update if "test/cert.pem" will ever change let node_hash_str = "E19427DAC79FBE758394945276A6E4F15F0BEBE6"; let node_id = node_hash_str.from_hex().unwrap(); - ctx.set_verify_with_data(SSL_VERIFY_PEER, callback, node_id); + ctx.set_verify_callback(SSL_VERIFY_PEER, move |_preverify_ok, x509_ctx| { + let cert = x509_ctx.current_cert(); + match cert { + None => false, + Some(cert) => { + let fingerprint = cert.fingerprint(SHA1).unwrap(); + fingerprint == node_id + } + } + }); ctx.set_verify_depth(1); - match SslStream::connect_generic(&ctx, stream) { + match SslStream::connect(&ctx, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err), } @@ -393,7 +371,7 @@ run_test!(ssl_verify_callback, |method, stream| { let node_id = node_hash_str.from_hex().unwrap(); ssl.set_verify_callback(SSL_VERIFY_PEER, move |_, x509| { CHECKED.store(1, Ordering::SeqCst); - match x509.get_current_cert() { + match x509.current_cert() { None => false, Some(cert) => { let fingerprint = cert.fingerprint(SHA1).unwrap(); @@ -402,7 +380,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), } @@ -419,14 +397,14 @@ fn test_write_hits_stream() { let guard = thread::spawn(move || { let ctx = SslContext::new(Sslv23).unwrap(); let stream = TcpStream::connect(addr).unwrap(); - let mut stream = SslStream::connect_generic(&ctx, stream).unwrap(); + let mut stream = SslStream::connect(&ctx, stream).unwrap(); stream.write_all(b"hello").unwrap(); stream }); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM).unwrap(); ctx.set_private_key_file(&Path::new("test/key.pem"), X509FileType::PEM).unwrap(); let stream = listener.accept().unwrap().0; @@ -440,17 +418,10 @@ fn test_write_hits_stream() { #[test] fn test_set_certificate_and_private_key() { - let key_path = Path::new("test/key.pem"); - let cert_path = Path::new("test/cert.pem"); - let mut key_file = File::open(&key_path) - .ok() - .expect("Failed to open `test/key.pem`"); - let mut cert_file = File::open(&cert_path) - .ok() - .expect("Failed to open `test/cert.pem`"); - - let key = PKey::private_key_from_pem(&mut key_file).unwrap(); - let cert = X509::from_pem(&mut cert_file).unwrap(); + let key = include_bytes!("../../../test/key.pem"); + let key = PKey::private_key_from_pem(key).unwrap(); + let cert = include_bytes!("../../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); let mut ctx = SslContext::new(Sslv23).unwrap(); ctx.set_private_key(&key).unwrap(); @@ -460,8 +431,8 @@ fn test_set_certificate_and_private_key() { } run_test!(get_ctx_options, |method, _| { - let mut ctx = SslContext::new(method).unwrap(); - ctx.get_options(); + let ctx = SslContext::new(method).unwrap(); + ctx.options(); }); run_test!(set_ctx_options, |method, _| { @@ -480,7 +451,7 @@ run_test!(clear_ctx_options, |method, _| { #[test] fn test_write() { let (_s, stream) = Server::new(); - let mut stream = SslStream::connect_generic(&SslContext::new(Sslv23).unwrap(), stream).unwrap(); + let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), stream).unwrap(); stream.write_all("hello".as_bytes()).unwrap(); stream.flush().unwrap(); stream.write_all(" there".as_bytes()).unwrap(); @@ -498,7 +469,7 @@ fn test_write_direct() { } run_test!(get_peer_certificate, |method, stream| { - let stream = SslStream::connect_generic(&SslContext::new(method).unwrap(), stream).unwrap(); + let stream = SslStream::connect(&SslContext::new(method).unwrap(), stream).unwrap(); let cert = stream.ssl().peer_certificate().unwrap(); let fingerprint = cert.fingerprint(SHA1).unwrap(); let node_hash_str = "E19427DAC79FBE758394945276A6E4F15F0BEBE6"; @@ -511,7 +482,7 @@ run_test!(get_peer_certificate, |method, stream| { fn test_write_dtlsv1() { let (_s, stream) = Server::new_dtlsv1(iter::repeat("y\n")); - let mut stream = SslStream::connect_generic(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap(); + let mut stream = SslStream::connect(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap(); stream.write_all(b"hello").unwrap(); stream.flush().unwrap(); stream.write_all(b" there").unwrap(); @@ -521,7 +492,7 @@ fn test_write_dtlsv1() { #[test] fn test_read() { let (_s, tcp) = Server::new(); - let mut stream = SslStream::connect_generic(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); + let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); stream.flush().unwrap(); io::copy(&mut stream, &mut io::sink()).ok().expect("read error"); @@ -539,7 +510,7 @@ fn test_read_direct() { #[test] fn test_pending() { let (_s, tcp) = Server::new(); - let mut stream = SslStream::connect_generic(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); + let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); stream.flush().unwrap(); @@ -562,7 +533,7 @@ fn test_pending() { #[test] fn test_state() { let (_s, tcp) = Server::new(); - let stream = SslStream::connect_generic(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); + let stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); assert_eq!(stream.ssl().state_string(), "SSLOK "); assert_eq!(stream.ssl().state_string_long(), "SSL negotiation finished successfully"); @@ -575,7 +546,7 @@ fn test_state() { fn test_connect_with_unilateral_alpn() { let (_s, stream) = Server::new(); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} @@ -597,13 +568,13 @@ fn test_connect_with_unilateral_alpn() { fn test_connect_with_unilateral_npn() { let (_s, stream) = Server::new(); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - let stream = match SslStream::connect_generic(&ctx, stream) { + let stream = match SslStream::connect(&ctx, stream) { Ok(stream) => stream, Err(err) => panic!("Expected success, got {:?}", err), }; @@ -619,7 +590,7 @@ fn test_connect_with_unilateral_npn() { fn test_connect_with_alpn_successful_multiple_matching() { let (_s, stream) = Server::new_alpn(); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"spdy/3.1", b"http/1.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} @@ -641,13 +612,13 @@ fn test_connect_with_alpn_successful_multiple_matching() { fn test_connect_with_npn_successful_multiple_matching() { let (_s, stream) = Server::new_alpn(); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_npn_protocols(&[b"spdy/3.1", b"http/1.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - let stream = match SslStream::connect_generic(&ctx, stream) { + let stream = match SslStream::connect(&ctx, stream) { Ok(stream) => stream, Err(err) => panic!("Expected success, got {:?}", err), }; @@ -664,7 +635,7 @@ fn test_connect_with_npn_successful_multiple_matching() { fn test_connect_with_alpn_successful_single_match() { let (_s, stream) = Server::new_alpn(); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"spdy/3.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} @@ -688,13 +659,13 @@ fn test_connect_with_alpn_successful_single_match() { fn test_connect_with_npn_successful_single_match() { let (_s, stream) = Server::new_alpn(); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_npn_protocols(&[b"spdy/3.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - let stream = match SslStream::connect_generic(&ctx, stream) { + let stream = match SslStream::connect(&ctx, stream) { Ok(stream) => stream, Err(err) => panic!("Expected success, got {:?}", err), }; @@ -713,7 +684,7 @@ fn test_npn_server_advertise_multiple() { // We create a different context instance for the server... let listener_ctx = { let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]); assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM) .is_ok()); @@ -728,7 +699,7 @@ fn test_npn_server_advertise_multiple() { }); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_npn_protocols(&[b"spdy/3.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} @@ -736,7 +707,7 @@ fn test_npn_server_advertise_multiple() { } // Now connect to the socket and make sure the protocol negotiation works... let stream = TcpStream::connect(localhost).unwrap(); - let stream = match SslStream::connect_generic(&ctx, stream) { + let stream = match SslStream::connect(&ctx, stream) { Ok(stream) => stream, Err(err) => panic!("Expected success, got {:?}", err), }; @@ -754,7 +725,7 @@ fn test_alpn_server_advertise_multiple() { // We create a different context instance for the server... let listener_ctx = { let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]); assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM) .is_ok()); @@ -769,7 +740,7 @@ fn test_alpn_server_advertise_multiple() { }); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"spdy/3.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} @@ -795,7 +766,7 @@ fn test_alpn_server_select_none() { // We create a different context instance for the server... let listener_ctx = { let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]); assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM) .is_ok()); @@ -810,7 +781,7 @@ fn test_alpn_server_select_none() { }); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"http/2"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} @@ -838,7 +809,7 @@ mod dtlsv1 { use crypto::hash::Type::SHA256; use ssl::SslMethod; use ssl::SslMethod::Dtlsv1; - use ssl::{SslContext, SslStream, VerifyCallback}; + use ssl::{SslContext, SslStream}; use ssl::SSL_VERIFY_PEER; use x509::X509StoreContext; @@ -855,7 +826,7 @@ mod dtlsv1 { fn test_read_dtlsv1() { let (_s, stream) = Server::new_dtlsv1(Some("hello")); - let mut stream = SslStream::connect_generic(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap(); + let mut stream = SslStream::connect(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap(); let mut buf = [0u8; 100]; assert!(stream.read(&mut buf).is_ok()); } @@ -864,15 +835,15 @@ fn test_read_dtlsv1() { #[cfg(feature = "sslv2")] fn test_sslv2_connect_failure() { let (_s, tcp) = Server::new_tcp(&["-no_ssl2", "-www"]); - SslStream::connect_generic(&SslContext::new(Sslv2).unwrap(), tcp) + SslStream::connect(&SslContext::new(Sslv2).unwrap(), tcp) .err() .unwrap(); } -fn wait_io(stream: &NonblockingSslStream<TcpStream>, read: bool, timeout_ms: u32) -> bool { +fn wait_io(stream: &TcpStream, read: bool, timeout_ms: u32) -> bool { unsafe { let mut set: select::fd_set = mem::zeroed(); - select::fd_set(&mut set, stream.get_ref()); + select::fd_set(&mut set, stream); let write = if read { 0 as *mut _ @@ -884,7 +855,19 @@ fn wait_io(stream: &NonblockingSslStream<TcpStream>, read: bool, timeout_ms: u32 } else { &mut set as *mut _ }; - select::select(stream.get_ref(), read, write, 0 as *mut _, timeout_ms).unwrap() + select::select(stream, read, write, 0 as *mut _, timeout_ms).unwrap() + } +} + +fn handshake(res: Result<SslStream<TcpStream>, HandshakeError<TcpStream>>) + -> SslStream<TcpStream> { + match res { + Ok(s) => s, + Err(HandshakeError::Interrupted(s)) => { + wait_io(s.get_ref(), true, 1_000); + handshake(s.handshake()) + } + Err(err) => panic!("error on handshake {:?}", err), } } @@ -893,7 +876,7 @@ fn test_write_nonblocking() { let (_s, stream) = Server::new(); stream.set_nonblocking(true).unwrap(); let cx = SslContext::new(Sslv23).unwrap(); - let mut stream = NonblockingSslStream::connect(&cx, stream).unwrap(); + let mut stream = handshake(SslStream::connect(&cx, stream)); let mut iterations = 0; loop { @@ -903,16 +886,16 @@ fn test_write_nonblocking() { // openssl. panic!("Too many read/write round trips in handshake!!"); } - let result = stream.write(b"hello"); + let result = stream.ssl_write(b"hello"); match result { Ok(_) => { break; } - Err(NonblockingSslError::WantRead) => { - assert!(wait_io(&stream, true, 1000)); + Err(Error::WantRead(_)) => { + assert!(wait_io(stream.get_ref(), true, 1000)); } - Err(NonblockingSslError::WantWrite) => { - assert!(wait_io(&stream, false, 1000)); + Err(Error::WantWrite(_)) => { + assert!(wait_io(stream.get_ref(), false, 1000)); } Err(other) => { panic!("Unexpected SSL Error: {:?}", other); @@ -930,7 +913,7 @@ fn test_read_nonblocking() { let (_s, stream) = Server::new(); stream.set_nonblocking(true).unwrap(); let cx = SslContext::new(Sslv23).unwrap(); - let mut stream = NonblockingSslStream::connect(&cx, stream).unwrap(); + let mut stream = handshake(SslStream::connect(&cx, stream)); let mut iterations = 0; loop { @@ -940,17 +923,17 @@ fn test_read_nonblocking() { // openssl. panic!("Too many read/write round trips in handshake!!"); } - let result = stream.write(b"GET /\r\n\r\n"); + let result = stream.ssl_write(b"GET /\r\n\r\n"); match result { Ok(n) => { assert_eq!(n, 9); break; } - Err(NonblockingSslError::WantRead) => { - assert!(wait_io(&stream, true, 1000)); + Err(Error::WantRead(..)) => { + assert!(wait_io(stream.get_ref(), true, 1000)); } - Err(NonblockingSslError::WantWrite) => { - assert!(wait_io(&stream, false, 1000)); + Err(Error::WantWrite(..)) => { + assert!(wait_io(stream.get_ref(), false, 1000)); } Err(other) => { panic!("Unexpected SSL Error: {:?}", other); @@ -958,7 +941,7 @@ fn test_read_nonblocking() { } } let mut input_buffer = [0u8; 1500]; - let result = stream.read(&mut input_buffer); + let result = stream.ssl_read(&mut input_buffer); let bytes_read = match result { Ok(n) => { // This branch is unlikely, but on an overloaded VM with @@ -966,8 +949,8 @@ fn test_read_nonblocking() { // be in the receive buffer before we issue the read() syscall... n } - Err(NonblockingSslError::WantRead) => { - assert!(wait_io(&stream, true, 3000)); + Err(Error::WantRead(..)) => { + assert!(wait_io(stream.get_ref(), true, 3000)); // Second read should return application data. stream.read(&mut input_buffer).unwrap() } @@ -980,14 +963,6 @@ fn test_read_nonblocking() { } #[test] -fn broken_try_clone_doesnt_crash() { - let context = SslContext::new(SslMethod::Sslv23).unwrap(); - let inner = TcpStream::connect("example.com:443").unwrap(); - let stream1 = SslStream::connect(&context, inner).unwrap(); - let _stream2 = stream1.try_clone().unwrap(); -} - -#[test] #[should_panic(expected = "blammo")] #[cfg(feature = "nightly")] fn write_panic() { @@ -1077,7 +1052,7 @@ fn flush_panic() { #[test] fn refcount_ssl_context() { - let ssl = { + let mut ssl = { let ctx = SslContext::new(SslMethod::Sslv23).unwrap(); ssl::Ssl::new(&ctx).unwrap() }; @@ -1093,7 +1068,7 @@ fn refcount_ssl_context() { fn default_verify_paths() { let mut ctx = SslContext::new(SslMethod::Sslv23).unwrap(); ctx.set_default_verify_paths().unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); let s = TcpStream::connect("google.com:443").unwrap(); let mut socket = SslStream::connect(&ctx, s).unwrap(); |