use ffi;
use foreign_types::{ForeignType, ForeignTypeRef};
use libc::{c_int, c_void, c_long, c_ulong};
use libc::{c_uchar, c_uint};
use std::any::Any;
use std::any::TypeId;
use std::borrow::Borrow;
use std::cmp;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::fmt;
use std::io;
use std::io::prelude::*;
use std::marker::PhantomData;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::panic::resume_unwind;
use std::path::Path;
use std::ptr;
use std::slice;
use std::str;
use std::sync::Mutex;
use {init, cvt, cvt_p};
use dh::{Dh, DhRef};
use ec::EcKeyRef;
#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
use ec::EcKey;
use x509::{X509StoreContextRef, X509FileType, X509, X509Ref, X509VerifyError, X509Name};
use x509::store::{X509StoreBuilderRef, X509StoreRef};
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
use x509::store::X509Store;
#[cfg(any(ossl102, ossl110))]
use verify::X509VerifyParamRef;
use pkey::PKeyRef;
use error::ErrorStack;
use util::Opaque;
use stack::{Stack, StackRef};
mod error;
mod connector;
mod bio;
#[cfg(test)]
mod tests;
use self::bio::BioMethod;
pub use ssl::connector::{SslConnectorBuilder, SslConnector, SslAcceptorBuilder, SslAcceptor};
pub use ssl::error::{Error, HandshakeError};
bitflags! {
pub struct SslOption: c_ulong {
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_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;
const SSL_OP_NO_TLSV1_2 = ffi::SSL_OP_NO_TLSv1_2;
const SSL_OP_NO_TLSV1_1 = ffi::SSL_OP_NO_TLSv1_1;
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
const SSL_OP_NO_DTLSV1 = ffi::SSL_OP_NO_DTLSv1;
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
const SSL_OP_NO_DTLSV1_2 = ffi::SSL_OP_NO_DTLSv1_2;
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
const SSL_OP_NO_SSL_MASK = ffi::SSL_OP_NO_SSL_MASK;
}
}
bitflags! {
pub struct SslMode: c_long {
const SSL_MODE_ENABLE_PARTIAL_WRITE = ffi::SSL_MODE_ENABLE_PARTIAL_WRITE;
const SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER = ffi::SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
const SSL_MODE_AUTO_RETRY = ffi::SSL_MODE_AUTO_RETRY;
const SSL_MODE_NO_AUTO_CHAIN = ffi::SSL_MODE_NO_AUTO_CHAIN;
const SSL_MODE_RELEASE_BUFFERS = ffi::SSL_MODE_RELEASE_BUFFERS;
#[cfg(not(libressl))]
const SSL_MODE_SEND_CLIENTHELLO_TIME = ffi::SSL_MODE_SEND_CLIENTHELLO_TIME;
#[cfg(not(libressl))]
const SSL_MODE_SEND_SERVERHELLO_TIME = ffi::SSL_MODE_SEND_SERVERHELLO_TIME;
#[cfg(not(libressl))]
const SSL_MODE_SEND_FALLBACK_SCSV = ffi::SSL_MODE_SEND_FALLBACK_SCSV;
}
}
#[derive(Copy, Clone)]
pub struct SslMethod(*const ffi::SSL_METHOD);
impl SslMethod {
pub fn tls() -> SslMethod {
SslMethod(compat::tls_method())
}
pub fn dtls() -> SslMethod {
SslMethod(compat::dtls_method())
}
pub unsafe fn from_ptr(ptr: *const ffi::SSL_METHOD) -> SslMethod {
SslMethod(ptr)
}
pub fn as_ptr(&self) -> *const ffi::SSL_METHOD {
self.0
}
}
bitflags! {
pub struct SslVerifyMode: i32 {
const SSL_VERIFY_PEER = ::ffi::SSL_VERIFY_PEER;
const SSL_VERIFY_NONE = ::ffi::SSL_VERIFY_NONE;
const SSL_VERIFY_FAIL_IF_NO_PEER_CERT = ::ffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
}
}
#[derive(Copy, Clone)]
pub struct StatusType(c_int);
impl StatusType {
pub fn from_raw(raw: c_int) -> StatusType {
StatusType(raw)
}
pub fn as_raw(&self) -> c_int {
self.0
}
}
pub const STATUS_TYPE_OCSP: StatusType = StatusType(ffi::TLSEXT_STATUSTYPE_ocsp);
lazy_static! {
static ref INDEXES: Mutex<HashMap<TypeId, c_int>> = Mutex::new(HashMap::new());
static ref SSL_INDEXES: Mutex<HashMap<TypeId, c_int>> = Mutex::new(HashMap::new());
}
fn get_callback_idx<T: Any + 'static>() -> c_int {
*INDEXES.lock().unwrap().entry(TypeId::of::<T>()).or_insert_with(|| get_new_idx::<T>())
}
fn get_ssl_callback_idx<T: Any + 'static>() -> c_int {
*SSL_INDEXES.lock().unwrap().entry(TypeId::of::<T>()).or_insert_with(|| get_new_ssl_idx::<T>())
}
lazy_static! {
static ref NPN_PROTOS_IDX: c_int = get_new_idx::<Vec<u8>>();
}
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
lazy_static! {
static ref ALPN_PROTOS_IDX: c_int = get_new_idx::<Vec<u8>>();
}
unsafe extern "C" fn free_data_box<T>(_parent: *mut c_void,
ptr: *mut c_void,
_ad: *mut ffi::CRYPTO_EX_DATA,
_idx: c_int,
_argl: c_long,
_argp: *mut c_void) {
if !ptr.is_null() {
Box::<T>::from_raw(ptr as *mut T);
}
}
fn get_new_idx<T>() -> c_int {
unsafe {
let idx = compat::get_new_idx(free_data_box::<T>);
assert!(idx >= 0);
idx
}
}
fn get_new_ssl_idx<T>() -> c_int {
unsafe {
let idx = compat::get_new_ssl_idx(free_data_box::<T>);
assert!(idx >= 0);
idx
}
}
extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
where F: Fn(bool, &X509StoreContextRef) -> 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 as *const _);
let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_callback_idx::<F>());
let verify: &F = &*(verify as *mut F);
let ctx = X509StoreContextRef::from_ptr(x509_ctx);
verify(preverify_ok != 0, ctx) as c_int
}
}
extern "C" fn ssl_raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
where F: Fn(bool, &X509StoreContextRef) -> 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 verify = ffi::SSL_get_ex_data(ssl as *const _, get_ssl_callback_idx::<F>());
let verify: &F = &*(verify as *mut F);
let ctx = X509StoreContextRef::from_ptr(x509_ctx);
verify(preverify_ok != 0, ctx) as c_int
}
}
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, get_callback_idx::<F>());
let callback: &F = &*(callback as *mut F);
let ssl = SslRef::from_ptr_mut(ssl);
match callback(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,
}
}
}
unsafe fn select_proto_using(ssl: *mut ffi::SSL,
out: *mut *mut c_uchar,
outlen: *mut c_uchar,
inbuf: *const c_uchar,
inlen: c_uint,
ex_data: c_int)
-> c_int {
let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl);
let protocols = ffi::SSL_CTX_get_ex_data(ssl_ctx, ex_data);
let protocols: &Vec<u8> = &*(protocols as *mut Vec<u8>);
let client = protocols.as_ptr();
let client_len = protocols.len() as c_uint;
if ffi::SSL_select_next_proto(out, outlen, inbuf, inlen, client, client_len) !=
ffi::OPENSSL_NPN_NEGOTIATED {
ffi::SSL_TLSEXT_ERR_NOACK
} else {
ffi::SSL_TLSEXT_ERR_OK
}
}
extern "C" fn raw_next_proto_select_cb(ssl: *mut ffi::SSL,
out: *mut *mut c_uchar,
outlen: *mut c_uchar,
inbuf: *const c_uchar,
inlen: c_uint,
_arg: *mut c_void)
-> c_int {
unsafe { select_proto_using(ssl, out, outlen, inbuf, inlen, *NPN_PROTOS_IDX) }
}
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
extern "C" fn raw_alpn_select_cb(ssl: *mut ffi::SSL,
out: *mut *const c_uchar,
outlen: *mut c_uchar,
inbuf: *const c_uchar,
inlen: c_uint,
_arg: *mut c_void)
-> c_int {
unsafe { select_proto_using(ssl, out as *mut _, outlen, inbuf, inlen, *ALPN_PROTOS_IDX) }
}
unsafe extern fn raw_tmp_dh<F>(ssl: *mut ffi::SSL,
is_export: c_int,
keylength: c_int)
-> *mut ffi::DH
where F: Fn(&mut SslRef, bool, u32) -> Result<Dh, ErrorStack> + Any + 'static + Sync + Send
{
let ctx = ffi::SSL_get_SSL_CTX(ssl);
let callback = ffi::SSL_CTX_get_ex_data(ctx, get_callback_idx::<F>());
let callback = &*(callback as *mut F);
let ssl = SslRef::from_ptr_mut(ssl);
match callback(ssl, is_export != 0, keylength as u32) {
Ok(dh) => {
let ptr = dh.as_ptr();
mem::forget(dh);
ptr
}
Err(_) => {
ptr::null_mut()
}
}
}
#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
unsafe extern fn raw_tmp_ecdh<F>(ssl: *mut ffi::SSL,
is_export: c_int,
keylength: c_int)
-> *mut ffi::EC_KEY
where F: Fn(&mut SslRef, bool, u32) -> Result<EcKey, ErrorStack> + Any + 'static + Sync + Send
{
let ctx = ffi::SSL_get_SSL_CTX(ssl);
let callback = ffi::SSL_CTX_get_ex_data(ctx, get_callback_idx::<F>());
let callback = &*(callback as *mut F);
let ssl = SslRef::from_ptr_mut(ssl);
match callback(ssl, is_export != 0, keylength as u32) {
Ok(ec_key) => {
let ptr = ec_key.as_ptr();
mem::forget(ec_key);
ptr
}
Err(_) => {
ptr::null_mut()
}
}
}
unsafe extern fn raw_tmp_dh_ssl<F>(ssl: *mut ffi::SSL,
is_export: c_int,
keylength: c_int)
-> *mut ffi::DH
where F: Fn(&mut SslRef, bool, u32) -> Result<Dh, ErrorStack> + Any + 'static + Sync + Send
{
let callback = ffi::SSL_get_ex_data(ssl, get_ssl_callback_idx::<F>());
let callback = &*(callback as *mut F);
let ssl = SslRef::from_ptr_mut(ssl);
match callback(ssl, is_export != 0, keylength as u32) {
Ok(dh) => {
let ptr = dh.as_ptr();
mem::forget(dh);
ptr
}
Err(_) => {
ptr::null_mut()
}
}
}
#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
unsafe extern fn raw_tmp_ecdh_ssl<F>(ssl: *mut ffi::SSL,
is_export: c_int,
keylength: c_int)
-> *mut ffi::EC_KEY
where F: Fn(&mut SslRef, bool, u32) -> Result<EcKey, ErrorStack> + Any + 'static + Sync + Send
{
let callback = ffi::SSL_get_ex_data(ssl, get_ssl_callback_idx::<F>());
let callback = &*(callback as *mut F);
let ssl = SslRef::from_ptr_mut(ssl);
match callback(ssl, is_export != 0, keylength as u32) {
Ok(ec_key) => {
let ptr = ec_key.as_ptr();
mem::forget(ec_key);
ptr
}
Err(_) => {
ptr::null_mut()
}
}
}
unsafe extern fn raw_tlsext_status<F>(ssl: *mut ffi::SSL, _: *mut c_void) -> c_int
where F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + Any + 'static + Sync + Send
{
let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl as *const _);
let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_callback_idx::<F>());
let callback = &*(callback as *mut F);
let ssl = SslRef::from_ptr_mut(ssl);
let ret = callback(ssl);
if ssl.is_server() {
match ret {
Ok(true) => ffi::SSL_TLSEXT_ERR_OK,
Ok(false) => ffi::SSL_TLSEXT_ERR_NOACK,
Err(_) => {
ffi::SSL_TLSEXT_ERR_ALERT_FATAL
}
}
} else {
match ret {
Ok(true) => 1,
Ok(false) => 0,
Err(_) => {
-1
}
}
}
}
extern "C" fn raw_next_protos_advertise_cb(ssl: *mut ffi::SSL,
out: *mut *const c_uchar,
outlen: *mut c_uint,
_arg: *mut c_void)
-> c_int {
unsafe {
let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl);
let protocols = ffi::SSL_CTX_get_ex_data(ssl_ctx, *NPN_PROTOS_IDX);
if protocols.is_null() {
*out = b"".as_ptr();
*outlen = 0;
} else {
let protocols: &Vec<u8> = &*(protocols as *mut Vec<u8>);
*out = protocols.as_ptr();
*outlen = protocols.len() as c_uint;
}
}
ffi::SSL_TLSEXT_ERR_OK
}
fn ssl_encode_byte_strings(strings: &[&[u8]]) -> Vec<u8> {
let mut enc = Vec::new();
for string in strings {
let len = string.len() as u8;
if len as usize != string.len() {
continue;
}
enc.push(len);
enc.extend(string[..len as usize].to_vec());
}
enc
}
pub enum SniError {
Fatal(c_int),
Warning(c_int),
NoAck,
}
pub struct SslContextBuilder(*mut ffi::SSL_CTX);
unsafe impl Sync for SslContextBuilder {}
unsafe impl Send for SslContextBuilder {}
impl Drop for SslContextBuilder {
fn drop(&mut self) {
unsafe { ffi::SSL_CTX_free(self.as_ptr()) }
}
}
impl SslContextBuilder {
pub fn new(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
unsafe {
init();
let ctx = try!(cvt_p(ffi::SSL_CTX_new(method.as_ptr())));
Ok(SslContextBuilder::from_ptr(ctx))
}
}
pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder {
SslContextBuilder(ctx)
}
pub fn as_ptr(&self) -> *mut ffi::SSL_CTX {
self.0
}
pub fn set_verify(&mut self, mode: SslVerifyMode) {
unsafe {
ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, None);
}
}
pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F)
where F: Fn(bool, &X509StoreContextRef) -> bool + Any + 'static + Sync + Send
{
unsafe {
let verify = Box::new(verify);
ffi::SSL_CTX_set_ex_data(self.as_ptr(),
get_callback_idx::<F>(),
mem::transmute(verify));
ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, Some(raw_verify::<F>));
}
}
pub fn set_servername_callback<F>(&mut self, callback: F)
where F: Fn(&mut SslRef) -> Result<(), SniError> + Any + 'static + Sync + Send
{
unsafe {
let callback = Box::new(callback);
ffi::SSL_CTX_set_ex_data(self.as_ptr(),
get_callback_idx::<F>(),
mem::transmute(callback));
let f: extern "C" fn(_, _, _) -> _ = raw_sni::<F>;
let f: extern "C" fn() = mem::transmute(f);
ffi::SSL_CTX_set_tlsext_servername_callback(self.as_ptr(), Some(f));
}
}
pub fn set_verify_depth(&mut self, depth: u32) {
unsafe {
ffi::SSL_CTX_set_verify_depth(self.as_ptr(), depth as c_int);
}
}
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> {
unsafe {
let ptr = cert_store.as_ptr();
try!(cvt(ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), ptr) as c_int));
mem::forget(cert_store);
Ok(())
}
}
pub fn set_read_ahead(&mut self, read_ahead: bool) {
unsafe {
ffi::SSL_CTX_set_read_ahead(self.as_ptr(), read_ahead as c_long);
}
}
pub fn set_mode(&mut self, mode: SslMode) -> SslMode {
unsafe {
let mode = ffi::SSL_CTX_set_mode(self.as_ptr(), mode.bits());
SslMode::from_bits(mode).unwrap()
}
}
pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) }
}
pub fn set_tmp_dh_callback<F>(&mut self, callback: F)
where F: Fn(&mut SslRef, bool, u32) -> Result<Dh, ErrorStack> + Any + 'static + Sync + Send
{
unsafe {
let callback = Box::new(callback);
ffi::SSL_CTX_set_ex_data(self.as_ptr(),
get_callback_idx::<F>(),
Box::into_raw(callback) as *mut c_void);
let f: unsafe extern fn (_, _, _) -> _ = raw_tmp_dh::<F>;
ffi::SSL_CTX_set_tmp_dh_callback(self.as_ptr(), f);
}
}
pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) }
}
#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
pub fn set_tmp_ecdh_callback<F>(&mut self, callback: F)
where F: Fn(&mut SslRef, bool, u32) -> Result<EcKey, ErrorStack> + Any + 'static + Sync + Send
{
unsafe {
let callback = Box::new(callback);
ffi::SSL_CTX_set_ex_data(self.as_ptr(),
get_callback_idx::<F>(),
Box::into_raw(callback) as *mut c_void);
let f: unsafe extern fn(_, _, _) -> _ = raw_tmp_ecdh::<F>;
ffi::SSL_CTX_set_tmp_ecdh_callback(self.as_ptr(), f);
}
}
pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_set_default_verify_paths(self.as_ptr())).map(|_| ()) }
}
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().unwrap()).unwrap();
unsafe {
cvt(ffi::SSL_CTX_load_verify_locations(self.as_ptr(),
file.as_ptr() as *const _,
ptr::null()))
.map(|_| ())
}
}
pub fn set_client_ca_list(&mut self, list: Stack<X509Name>) {
unsafe {
ffi::SSL_CTX_set_client_CA_list(self.as_ptr(), list.as_ptr());
mem::forget(list);
}
}
pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), ErrorStack> {
unsafe {
assert!(sid_ctx.len() <= c_uint::max_value() as usize);
cvt(ffi::SSL_CTX_set_session_id_context(self.as_ptr(),
sid_ctx.as_ptr(),
sid_ctx.len() as c_uint))
.map(|_| ())
}
}
pub fn set_certificate_file<P: AsRef<Path>>(&mut self,
file: P,
file_type: X509FileType)
-> Result<(), ErrorStack> {
let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
unsafe {
cvt(ffi::SSL_CTX_use_certificate_file(self.as_ptr(),
file.as_ptr() as *const _,
file_type.as_raw()))
.map(|_| ())
}
}
pub fn set_certificate_chain_file<P: AsRef<Path>>(&mut self,
file: P)
-> Result<(), ErrorStack> {
let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
unsafe {
cvt(ffi::SSL_CTX_use_certificate_chain_file(self.as_ptr(), file.as_ptr() as *const _))
.map(|_| ())
}
}
pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr())).map(|_| ()) }
}
pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> {
unsafe {
try!(cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int));
mem::forget(cert);
Ok(())
}
}
pub fn set_private_key_file<P: AsRef<Path>>(&mut self,
file: P,
file_type: X509FileType)
-> Result<(), ErrorStack> {
let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
unsafe {
cvt(ffi::SSL_CTX_use_PrivateKey_file(self.as_ptr(),
file.as_ptr() as *const _,
file_type.as_raw()))
.map(|_| ())
}
}
pub fn set_private_key(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_use_PrivateKey(self.as_ptr(), key.as_ptr())).map(|_| ()) }
}
pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> {
let cipher_list = CString::new(cipher_list).unwrap();
unsafe {
cvt(ffi::SSL_CTX_set_cipher_list(self.as_ptr(), cipher_list.as_ptr() as *const _))
.map(|_| ())
}
}
#[cfg(all(feature = "v102", any(ossl102, libressl)))]
pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> {
self._set_ecdh_auto(onoff)
}
#[cfg(any(ossl102,libressl))]
fn _set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_set_ecdh_auto(self.as_ptr(), onoff as c_int)).map(|_| ()) }
}
pub fn set_options(&mut self, option: SslOption) -> SslOption {
let ret = unsafe { compat::SSL_CTX_set_options(self.as_ptr(), option.bits()) };
SslOption::from_bits(ret).unwrap()
}
pub fn options(&self) -> SslOption {
let ret = unsafe { compat::SSL_CTX_get_options(self.as_ptr()) };
SslOption::from_bits(ret).unwrap()
}
pub fn clear_options(&mut self, option: SslOption) -> SslOption {
let ret = unsafe { compat::SSL_CTX_clear_options(self.as_ptr(), option.bits()) };
SslOption::from_bits(ret).unwrap()
}
pub fn set_npn_protocols(&mut self, protocols: &[&[u8]]) -> Result<(), ErrorStack> {
let protocols: Box<Vec<u8>> = Box::new(ssl_encode_byte_strings(protocols));
unsafe {
try!(cvt(ffi::SSL_CTX_set_ex_data(self.as_ptr(),
*NPN_PROTOS_IDX,
Box::into_raw(protocols) as *mut c_void)));
ffi::SSL_CTX_set_next_proto_select_cb(self.as_ptr(),
raw_next_proto_select_cb,
ptr::null_mut());
ffi::SSL_CTX_set_next_protos_advertised_cb(self.as_ptr(),
raw_next_protos_advertise_cb,
ptr::null_mut());
Ok(())
}
}
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
pub fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> Result<(), ErrorStack> {
let protocols: Box<Vec<u8>> = Box::new(ssl_encode_byte_strings(protocols));
unsafe {
let r = ffi::SSL_CTX_set_alpn_protos(self.as_ptr(),
protocols.as_ptr(),
protocols.len() as c_uint);
if r != 0 {
return Err(ErrorStack::get());
}
try!(cvt(ffi::SSL_CTX_set_ex_data(self.as_ptr(),
*ALPN_PROTOS_IDX,
Box::into_raw(protocols) as *mut c_void)));
ffi::SSL_CTX_set_alpn_select_cb(self.as_ptr(), raw_alpn_select_cb, ptr::null_mut());
Ok(())
}
}
pub fn check_private_key(&self) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_check_private_key(self.as_ptr())).map(|_| ()) }
}
pub fn cert_store(&self) -> &X509StoreBuilderRef {
unsafe { X509StoreBuilderRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) }
}
pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef {
unsafe { X509StoreBuilderRef::from_ptr_mut(ffi::SSL_CTX_get_cert_store(self.as_ptr())) }
}
pub fn set_status_callback<F>(&mut self, callback: F) -> Result<(), ErrorStack>
where F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + Any + 'static + Sync + Send
{
unsafe {
let callback = Box::new(callback);
ffi::SSL_CTX_set_ex_data(self.as_ptr(),
get_callback_idx::<F>(),
Box::into_raw(callback) as *mut c_void);
let f: unsafe extern fn (_, _) -> _ = raw_tlsext_status::<F>;
cvt(ffi::SSL_CTX_set_tlsext_status_cb(self.as_ptr(), Some(f)) as c_int).map(|_| ())
}
}
pub fn build(self) -> SslContext {
let ctx = SslContext(self.0);
mem::forget(self);
ctx
}
}
foreign_type! {
type CType = ffi::SSL_CTX;
fn drop = ffi::SSL_CTX_free;
pub struct SslContext;
pub struct SslContextRef;
}
unsafe impl Send for SslContext {}
unsafe impl Sync for SslContext {}
impl Clone for SslContext {
fn clone(&self) -> Self {
unsafe {
compat::SSL_CTX_up_ref(self.as_ptr());
SslContext::from_ptr(self.as_ptr())
}
}
}
impl fmt::Debug for SslContext {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "SslContext")
}
}
impl SslContext {
pub fn builder(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
SslContextBuilder::new(method)
}
}
impl SslContextRef {
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
pub fn certificate(&self) -> Option<&X509Ref> {
unsafe {
let ptr = ffi::SSL_CTX_get0_certificate(self.as_ptr());
if ptr.is_null() {
None
} else {
Some(X509Ref::from_ptr(ptr))
}
}
}
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
pub fn private_key(&self) -> Option<&PKeyRef> {
unsafe {
let ptr = ffi::SSL_CTX_get0_privatekey(self.as_ptr());
if ptr.is_null() {
None
} else {
Some(PKeyRef::from_ptr(ptr))
}
}
}
pub fn cert_store(&self) -> &X509StoreRef {
unsafe {
X509StoreRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr()))
}
}
pub fn extra_chain_certs(&self) -> &StackRef<X509> {
unsafe {
let mut chain = ptr::null_mut();
ffi::SSL_CTX_get_extra_chain_certs(self.as_ptr(), &mut chain);
assert!(!chain.is_null());
StackRef::from_ptr(chain)
}
}
}
pub struct CipherBits {
pub secret: i32,
pub algorithm: i32,
}
pub struct SslCipher(*mut ffi::SSL_CIPHER);
impl ForeignType for SslCipher {
type CType = ffi::SSL_CIPHER;
type Ref = SslCipherRef;
#[inline]
unsafe fn from_ptr(ptr: *mut ffi::SSL_CIPHER) -> SslCipher {
SslCipher(ptr)
}
#[inline]
fn as_ptr(&self) -> *mut ffi::SSL_CIPHER {
self.0
}
}
impl Deref for SslCipher {
type Target = SslCipherRef;
fn deref(&self) -> &SslCipherRef {
unsafe { SslCipherRef::from_ptr(self.0) }
}
}
impl DerefMut for SslCipher {
fn deref_mut(&mut self) -> &mut SslCipherRef {
unsafe { SslCipherRef::from_ptr_mut(self.0) }
}
}
pub struct SslCipherRef(Opaque);
impl ForeignTypeRef for SslCipherRef {
type CType = ffi::SSL_CIPHER;
}
impl SslCipherRef {
pub fn name(&self) -> &str {
let name = unsafe {
let ptr = ffi::SSL_CIPHER_get_name(self.as_ptr());
CStr::from_ptr(ptr as *const _)
};
str::from_utf8(name.to_bytes()).unwrap()
}
pub fn version(&self) -> &str {
let version = unsafe {
let ptr = ffi::SSL_CIPHER_get_version(self.as_ptr());
CStr::from_ptr(ptr as *const _)
};
str::from_utf8(version.to_bytes()).unwrap()
}
pub fn bits(&self) -> CipherBits {
unsafe {
let mut algo_bits = 0;
let secret_bits = ffi::SSL_CIPHER_get_bits(self.as_ptr(), &mut algo_bits);
CipherBits {
secret: secret_bits.into(),
algorithm: algo_bits.into(),
}
}
}
pub fn description(&self) -> String {
unsafe {
let mut buf = [0; 128];
let ptr = ffi::SSL_CIPHER_description(self.as_ptr(), buf.as_mut_ptr(), 128);
String::from_utf8(CStr::from_ptr(ptr as *const _).to_bytes().to_vec()).unwrap()
}
}
}
foreign_type! {
type CType = ffi::SSL_SESSION;
fn drop = ffi::SSL_SESSION_free;
pub struct SslSession;
pub struct SslSessionRef;
}
unsafe impl Sync for SslSession {}
unsafe impl Send for SslSession {}
impl Clone for SslSession {
fn clone(&self) -> SslSession {
self.to_owned()
}
}
impl Borrow<SslSessionRef> for SslSession {
fn borrow(&self) -> &SslSessionRef {
&self
}
}
impl ToOwned for SslSessionRef {
type Owned = SslSession;
fn to_owned(&self) -> SslSession {
unsafe {
compat::SSL_SESSION_up_ref(self.as_ptr());
SslSession(self.as_ptr())
}
}
}
impl SslSessionRef {
pub fn id(&self) -> &[u8] {
unsafe {
let mut len = 0;
let p = ffi::SSL_SESSION_get_id(self.as_ptr(), &mut len);
slice::from_raw_parts(p as *const u8, len as usize)
}
}
pub fn master_key_len(&self) -> usize {
unsafe { compat::SSL_SESSION_get_master_key(self.as_ptr(), ptr::null_mut(), 0) }
}
pub fn master_key(&self, buf: &mut [u8]) -> usize {
unsafe { compat::SSL_SESSION_get_master_key(self.as_ptr(), buf.as_mut_ptr(), buf.len()) }
}
}
foreign_type! {
type CType = ffi::SSL;
fn drop = ffi::SSL_free;
pub struct Ssl;
pub struct SslRef;
}
impl fmt::Debug for SslRef {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut builder = fmt.debug_struct("Ssl");
builder.field("state", &self.state_string_long());
if let Some(err) = self.verify_result() {
builder.field("verify_result", &err);
}
builder.finish()
}
}
impl SslRef {
fn get_raw_rbio(&self) -> *mut ffi::BIO {
unsafe { ffi::SSL_get_rbio(self.as_ptr()) }
}
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.as_ptr(), buf.as_ptr() as *mut c_void, len) }
}
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.as_ptr(), buf.as_ptr() as *const c_void, len) }
}
fn get_error(&self, ret: c_int) -> c_int {
unsafe { ffi::SSL_get_error(self.as_ptr(), ret) }
}
pub fn set_verify(&mut self, mode: SslVerifyMode) {
unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, None) }
}
pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F)
where F: Fn(bool, &X509StoreContextRef) -> bool + Any + 'static + Sync + Send
{
unsafe {
let verify = Box::new(verify);
ffi::SSL_set_ex_data(self.as_ptr(),
get_ssl_callback_idx::<F>(),
mem::transmute(verify));
ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, Some(ssl_raw_verify::<F>));
}
}
pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) }
}
pub fn set_tmp_dh_callback<F>(&mut self, callback: F)
where F: Fn(&mut SslRef, bool, u32) -> Result<Dh, ErrorStack> + Any + 'static + Sync + Send
{
unsafe {
let callback = Box::new(callback);
ffi::SSL_set_ex_data(self.as_ptr(),
get_ssl_callback_idx::<F>(),
Box::into_raw(callback) as *mut c_void);
let f: unsafe extern fn (_, _, _) -> _ = raw_tmp_dh_ssl::<F>;
ffi::SSL_set_tmp_dh_callback(self.as_ptr(), f);
}
}
pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) }
}
#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
pub fn set_tmp_ecdh_callback<F>(&mut self, callback: F)
where F: Fn(&mut SslRef, bool, u32) -> Result<EcKey, ErrorStack> + Any + 'static + Sync + Send
{
unsafe {
let callback = Box::new(callback);
ffi::SSL_set_ex_data(self.as_ptr(),
get_ssl_callback_idx::<F>(),
Box::into_raw(callback) as *mut c_void);
let f: unsafe extern fn(_, _, _) -> _ = raw_tmp_ecdh_ssl::<F>;
ffi::SSL_set_tmp_ecdh_callback(self.as_ptr(), f);
}
}
#[cfg(all(feature = "v102", ossl102))]
pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_set_ecdh_auto(self.as_ptr(), onoff as c_int)).map(|_| ()) }
}
pub fn current_cipher(&self) -> Option<&SslCipherRef> {
unsafe {
let ptr = ffi::SSL_get_current_cipher(self.as_ptr());
if ptr.is_null() {
None
} else {
Some(SslCipherRef::from_ptr(ptr as *mut _))
}
}
}
pub fn state_string(&self) -> &'static str {
let state = unsafe {
let ptr = ffi::SSL_state_string(self.as_ptr());
CStr::from_ptr(ptr as *const _)
};
str::from_utf8(state.to_bytes()).unwrap()
}
pub fn state_string_long(&self) -> &'static str {
let state = unsafe {
let ptr = ffi::SSL_state_string_long(self.as_ptr());
CStr::from_ptr(ptr as *const _)
};
str::from_utf8(state.to_bytes()).unwrap()
}
pub fn set_hostname(&mut self, hostname: &str) -> Result<(), ErrorStack> {
let cstr = CString::new(hostname).unwrap();
unsafe {
cvt(ffi::SSL_set_tlsext_host_name(self.as_ptr(), cstr.as_ptr() as *mut _) as c_int)
.map(|_| ())
}
}
pub fn peer_certificate(&self) -> Option<X509> {
unsafe {
let ptr = ffi::SSL_get_peer_certificate(self.as_ptr());
if ptr.is_null() {
None
} else {
Some(X509::from_ptr(ptr))
}
}
}
pub fn certificate(&self) -> Option<&X509Ref> {
unsafe {
let ptr = ffi::SSL_get_certificate(self.as_ptr());
if ptr.is_null() {
None
} else {
Some(X509Ref::from_ptr(ptr))
}
}
}
pub fn private_key(&self) -> Option<&PKeyRef> {
unsafe {
let ptr = ffi::SSL_get_privatekey(self.as_ptr());
if ptr.is_null() {
None
} else {
Some(PKeyRef::from_ptr(ptr))
}
}
}
pub fn version(&self) -> &'static str {
let version = unsafe {
let ptr = ffi::SSL_get_version(self.as_ptr());
CStr::from_ptr(ptr as *const _)
};
str::from_utf8(version.to_bytes()).unwrap()
}
pub fn selected_npn_protocol(&self) -> Option<&[u8]> {
unsafe {
let mut data: *const c_uchar = ptr::null();
let mut len: c_uint = 0;
ffi::SSL_get0_next_proto_negotiated(self.as_ptr(), &mut data, &mut len);
if data.is_null() {
None
} else {
Some(slice::from_raw_parts(data, len as usize))
}
}
}
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
pub fn selected_alpn_protocol(&self) -> Option<&[u8]> {
unsafe {
let mut data: *const c_uchar = ptr::null();
let mut len: c_uint = 0;
ffi::SSL_get0_alpn_selected(self.as_ptr(), &mut data, &mut len);
if data.is_null() {
None
} else {
Some(slice::from_raw_parts(data, len as usize))
}
}
}
pub fn pending(&self) -> usize {
unsafe { ffi::SSL_pending(self.as_ptr()) as usize }
}
pub fn compression(&self) -> Option<&str> {
self._compression()
}
#[cfg(not(osslconf = "OPENSSL_NO_COMP"))]
fn _compression(&self) -> Option<&str> {
unsafe {
let ptr = ffi::SSL_get_current_compression(self.as_ptr());
if ptr == ptr::null() {
return None;
}
let meth = ffi::SSL_COMP_get_name(ptr);
Some(str::from_utf8(CStr::from_ptr(meth as *const _).to_bytes()).unwrap())
}
}
#[cfg(osslconf = "OPENSSL_NO_COMP")]
fn _compression(&self) -> Option<&str> {
None
}
pub fn servername(&self) -> Option<&str> {
unsafe {
let name = ffi::SSL_get_servername(self.as_ptr(), ffi::TLSEXT_NAMETYPE_host_name);
if name == ptr::null() {
return None;
}
Some(str::from_utf8(CStr::from_ptr(name as *const _).to_bytes()).unwrap())
}
}
pub fn set_ssl_context(&mut self, ctx: &SslContextRef) -> Result<(), ErrorStack> {
unsafe { cvt_p(ffi::SSL_set_SSL_CTX(self.as_ptr(), ctx.as_ptr())).map(|_| ()) }
}
pub fn ssl_context(&self) -> &SslContextRef {
unsafe {
let ssl_ctx = ffi::SSL_get_SSL_CTX(self.as_ptr());
SslContextRef::from_ptr(ssl_ctx)
}
}
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
pub fn param_mut(&mut self) -> &mut X509VerifyParamRef {
self._param_mut()
}
#[cfg(any(ossl102, ossl110))]
fn _param_mut(&mut self) -> &mut X509VerifyParamRef {
unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_get0_param(self.as_ptr())) }
}
pub fn verify_result(&self) -> Option<X509VerifyError> {
unsafe { X509VerifyError::from_raw(ffi::SSL_get_verify_result(self.as_ptr())) }
}
pub fn session(&self) -> Option<&SslSessionRef> {
unsafe {
let p = ffi::SSL_get_session(self.as_ptr());
if p.is_null() {
None
} else {
Some(SslSessionRef::from_ptr(p))
}
}
}
pub unsafe fn set_session(&mut self, session: &SslSessionRef) -> Result<(), ErrorStack> {
cvt(ffi::SSL_set_session(self.as_ptr(), session.as_ptr())).map(|_| ())
}
pub fn session_reused(&self) -> bool {
unsafe {
ffi::SSL_session_reused(self.as_ptr()) != 0
}
}
pub fn set_status_type(&mut self, type_: StatusType) -> Result<(), ErrorStack> {
unsafe {
cvt(ffi::SSL_set_tlsext_status_type(self.as_ptr(), type_.as_raw()) as c_int).map(|_| ())
}
}
pub fn ocsp_status(&self) -> Option<&[u8]> {
unsafe {
let mut p = ptr::null_mut();
let len = ffi::SSL_get_tlsext_status_ocsp_resp(self.as_ptr(), &mut p);
if len < 0 {
None
} else {
Some(slice::from_raw_parts(p as *const u8, len as usize))
}
}
}
pub fn set_ocsp_status(&mut self, response: &[u8]) -> Result<(), ErrorStack> {
unsafe {
assert!(response.len() <= c_int::max_value() as usize);
let p = try!(cvt_p(ffi::CRYPTO_malloc(response.len() as _,
concat!(file!(), "\0").as_ptr() as *const _,
line!() as c_int)));
ptr::copy_nonoverlapping(response.as_ptr(), p as *mut u8, response.len());
cvt(ffi::SSL_set_tlsext_status_ocsp_resp(self.as_ptr(),
p as *mut c_uchar,
response.len() as c_long) as c_int)
.map(|_| ())
}
}
pub fn is_server(&self) -> bool {
unsafe {
compat::SSL_is_server(self.as_ptr()) != 0
}
}
}
unsafe impl Sync for Ssl {}
unsafe impl Send for Ssl {}
impl fmt::Debug for Ssl {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&**self, fmt)
}
}
impl Ssl {
pub fn new(ctx: &SslContext) -> Result<Ssl, ErrorStack> {
unsafe {
let ssl = try!(cvt_p(ffi::SSL_new(ctx.as_ptr())));
Ok(Ssl::from_ptr(ssl))
}
}
pub fn connect<S>(self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
where S: Read + Write
{
let mut stream = SslStream::new_base(self, stream);
let ret = unsafe { ffi::SSL_connect(stream.ssl.as_ptr()) };
if ret > 0 {
Ok(stream)
} else {
match stream.make_error(ret) {
e @ Error::WantWrite(_) |
e @ Error::WantRead(_) => {
Err(HandshakeError::Interrupted(MidHandshakeSslStream {
stream: stream,
error: e,
}))
}
err => {
Err(HandshakeError::Failure(MidHandshakeSslStream {
stream: stream,
error: err,
}))
}
}
}
}
pub fn accept<S>(self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
where S: Read + Write
{
let mut stream = SslStream::new_base(self, stream);
let ret = unsafe { ffi::SSL_accept(stream.ssl.as_ptr()) };
if ret > 0 {
Ok(stream)
} else {
match stream.make_error(ret) {
e @ Error::WantWrite(_) |
e @ Error::WantRead(_) => {
Err(HandshakeError::Interrupted(MidHandshakeSslStream {
stream: stream,
error: e,
}))
}
err => {
Err(HandshakeError::Failure(MidHandshakeSslStream {
stream: stream,
error: err,
}))
}
}
}
}
}
#[derive(Debug)]
pub struct MidHandshakeSslStream<S> {
stream: SslStream<S>,
error: Error,
}
impl<S> MidHandshakeSslStream<S> {
pub fn get_ref(&self) -> &S {
self.stream.get_ref()
}
pub fn get_mut(&mut self) -> &mut S {
self.stream.get_mut()
}
pub fn ssl(&self) -> &SslRef {
self.stream.ssl()
}
pub fn error(&self) -> &Error {
&self.error
}
pub fn into_error(self) -> Error {
self.error
}
pub fn handshake(mut self) -> Result<SslStream<S>, HandshakeError<S>> {
let ret = unsafe { ffi::SSL_do_handshake(self.stream.ssl.as_ptr()) };
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))
}
err => {
self.error = err;
Err(HandshakeError::Failure(self))
}
}
}
}
}
pub struct SslStream<S> {
ssl: Ssl,
_method: BioMethod, _p: PhantomData<S>,
}
impl<S> fmt::Debug for SslStream<S>
where S: fmt::Debug
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("SslStream")
.field("stream", &self.get_ref())
.field("ssl", &self.ssl())
.finish()
}
}
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.as_ptr(), bio, bio);
SslStream {
ssl: ssl,
_method: method,
_p: PhantomData,
}
}
}
pub fn ssl_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
if buf.len() == 0 {
return Ok(0)
}
let ret = self.ssl.read(buf);
if ret > 0 {
Ok(ret as usize)
} else {
match self.make_error(ret) {
Error::Stream(ref e) if e.kind() == io::ErrorKind::ConnectionAborted => Ok(0),
e => Err(e),
}
}
}
pub fn ssl_write(&mut self, buf: &[u8]) -> Result<usize, Error> {
if buf.len() == 0 {
return Ok(0)
}
let ret = self.ssl.write(buf);
if ret > 0 {
Ok(ret as usize)
} else {
Err(self.make_error(ret))
}
}
pub fn shutdown(&mut self) -> Result<ShutdownResult, Error> {
match unsafe { ffi::SSL_shutdown(self.ssl.as_ptr()) } {
0 => Ok(ShutdownResult::Sent),
1 => Ok(ShutdownResult::Received),
n => Err(self.make_error(n)),
}
}
}
impl<S> SslStream<S> {
fn make_error(&mut self, ret: c_int) -> Error {
self.check_panic();
match self.ssl.get_error(ret) {
ffi::SSL_ERROR_SSL => Error::Ssl(ErrorStack::get()),
ffi::SSL_ERROR_SYSCALL => {
let errs = ErrorStack::get();
if errs.errors().is_empty() {
match self.get_bio_error() {
Some(err) => Error::Stream(err),
None => {
Error::Stream(io::Error::new(io::ErrorKind::ConnectionAborted,
"unexpected EOF observed"))
}
}
} else {
Error::Ssl(errs)
}
}
ffi::SSL_ERROR_ZERO_RETURN => Error::ZeroReturn,
ffi::SSL_ERROR_WANT_WRITE => {
let err = match self.get_bio_error() {
Some(err) => err,
None => {
io::Error::new(io::ErrorKind::Other,
"BUG: got an SSL_ERROR_WANT_WRITE with no error in the BIO")
}
};
Error::WantWrite(err)
},
ffi::SSL_ERROR_WANT_READ => {
let err = match self.get_bio_error() {
Some(err) => err,
None => {
io::Error::new(io::ErrorKind::Other,
"BUG: got an SSL_ERROR_WANT_WRITE with no error in the BIO")
}
};
Error::WantRead(err)
},
err => {
Error::Stream(io::Error::new(io::ErrorKind::InvalidData,
format!("unexpected error {}", err)))
}
}
}
fn check_panic(&mut self) {
if let Some(err) = unsafe { bio::take_panic::<S>(self.ssl.get_raw_rbio()) } {
resume_unwind(err)
}
}
fn get_bio_error(&mut self) -> Option<io::Error> {
unsafe { bio::take_error::<S>(self.ssl.get_raw_rbio()) }
}
pub fn get_ref(&self) -> &S {
unsafe {
let bio = self.ssl.get_raw_rbio();
bio::get_ref(bio)
}
}
pub fn get_mut(&mut self) -> &mut S {
unsafe {
let bio = self.ssl.get_raw_rbio();
bio::get_mut(bio)
}
}
pub fn ssl(&self) -> &SslRef {
&self.ssl
}
}
impl<S: Read + Write> Read for SslStream<S> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.ssl_read(buf) {
Ok(n) => Ok(n),
Err(Error::ZeroReturn) => Ok(0),
Err(Error::Stream(e)) => Err(e),
Err(Error::WantRead(e)) => Err(e),
Err(Error::WantWrite(e)) => Err(e),
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
}
}
}
impl<S: Read + Write> Write for SslStream<S> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.ssl_write(buf).map_err(|e| {
match e {
Error::Stream(e) => e,
Error::WantRead(e) => e,
Error::WantWrite(e) => e,
e => io::Error::new(io::ErrorKind::Other, e),
}
})
}
fn flush(&mut self) -> io::Result<()> {
self.get_mut().flush()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ShutdownResult {
Sent,
Received,
}
#[cfg(ossl110)]
mod compat {
use std::ptr;
use ffi;
use libc::c_int;
pub use ffi::{SSL_CTX_get_options, SSL_CTX_set_options, SSL_CTX_clear_options, SSL_CTX_up_ref,
SSL_SESSION_get_master_key, SSL_is_server, SSL_SESSION_up_ref};
pub unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int {
ffi::CRYPTO_get_ex_new_index(ffi::CRYPTO_EX_INDEX_SSL_CTX,
0,
ptr::null_mut(),
None,
None,
Some(f))
}
pub unsafe fn get_new_ssl_idx(f: ffi::CRYPTO_EX_free) -> c_int {
ffi::CRYPTO_get_ex_new_index(ffi::CRYPTO_EX_INDEX_SSL,
0,
ptr::null_mut(),
None,
None,
Some(f))
}
pub fn tls_method() -> *const ffi::SSL_METHOD {
unsafe { ffi::TLS_method() }
}
pub fn dtls_method() -> *const ffi::SSL_METHOD {
unsafe { ffi::DTLS_method() }
}
}
#[cfg(ossl10x)]
#[allow(bad_style)]
mod compat {
use std::ptr;
use ffi;
use libc::{self, c_long, c_ulong, c_int, size_t, c_uchar};
pub unsafe fn SSL_CTX_get_options(ctx: *const ffi::SSL_CTX) -> c_ulong {
ffi::SSL_CTX_ctrl(ctx as *mut _, ffi::SSL_CTRL_OPTIONS, 0, ptr::null_mut()) as c_ulong
}
pub unsafe fn SSL_CTX_set_options(ctx: *const ffi::SSL_CTX, op: c_ulong) -> c_ulong {
ffi::SSL_CTX_ctrl(ctx as *mut _,
ffi::SSL_CTRL_OPTIONS,
op as c_long,
ptr::null_mut()) as c_ulong
}
pub unsafe fn SSL_CTX_clear_options(ctx: *const ffi::SSL_CTX, op: c_ulong) -> c_ulong {
ffi::SSL_CTX_ctrl(ctx as *mut _,
ffi::SSL_CTRL_CLEAR_OPTIONS,
op as c_long,
ptr::null_mut()) as c_ulong
}
pub unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int {
ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), None, None, Some(f))
}
pub unsafe fn get_new_ssl_idx(f: ffi::CRYPTO_EX_free) -> c_int {
ffi::SSL_get_ex_new_index(0, ptr::null_mut(), None, None, Some(f))
}
pub unsafe fn SSL_CTX_up_ref(ssl: *mut ffi::SSL_CTX) -> libc::c_int {
ffi::CRYPTO_add_lock(&mut (*ssl).references,
1,
ffi::CRYPTO_LOCK_SSL_CTX,
"mod.rs\0".as_ptr() as *const _,
line!() as libc::c_int);
0
}
pub unsafe fn SSL_SESSION_get_master_key(session: *const ffi::SSL_SESSION,
out: *mut c_uchar,
mut outlen: size_t) -> size_t {
if outlen == 0 {
return (*session).master_key_length as size_t;
}
if outlen > (*session).master_key_length as size_t {
outlen = (*session).master_key_length as size_t;
}
ptr::copy_nonoverlapping((*session).master_key.as_ptr(), out, outlen);
outlen
}
pub fn tls_method() -> *const ffi::SSL_METHOD {
unsafe { ffi::SSLv23_method() }
}
pub fn dtls_method() -> *const ffi::SSL_METHOD {
unsafe { ffi::DTLSv1_method() }
}
pub unsafe fn SSL_is_server(s: *mut ffi::SSL) -> c_int {
(*s).server
}
pub unsafe fn SSL_SESSION_up_ref(ses: *mut ffi::SSL_SESSION) -> c_int {
ffi::CRYPTO_add_lock(&mut (*ses).references,
1,
ffi::CRYPTO_LOCK_SSL_CTX,
"mod.rs\0".as_ptr() as *const _,
line!() as libc::c_int);
0
}
}