commit
e1d442e65b
|
|
@ -2495,6 +2495,7 @@ extern "C" {
|
||||||
#[cfg(not(ossl101))]
|
#[cfg(not(ossl101))]
|
||||||
pub fn SSL_set_alpn_protos(s: *mut SSL, data: *const c_uchar, len: c_uint) -> c_int;
|
pub fn SSL_set_alpn_protos(s: *mut SSL, data: *const c_uchar, len: c_uint) -> c_int;
|
||||||
|
|
||||||
|
// FIXME should take an Option<unsafe extern "C" fn>
|
||||||
#[cfg(not(ossl101))]
|
#[cfg(not(ossl101))]
|
||||||
pub fn SSL_CTX_set_alpn_select_cb(
|
pub fn SSL_CTX_set_alpn_select_cb(
|
||||||
ssl: *mut SSL_CTX,
|
ssl: *mut SSL_CTX,
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ use error::ErrorStack;
|
||||||
use dh::Dh;
|
use dh::Dh;
|
||||||
#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
|
#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
|
||||||
use ec::EcKey;
|
use ec::EcKey;
|
||||||
use ssl::{get_callback_idx, get_ssl_callback_idx, SniError, SslRef, NPN_PROTOS_IDX};
|
use ssl::{get_callback_idx, get_ssl_callback_idx, SniError, SslRef};
|
||||||
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
|
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
|
||||||
use ssl::ALPN_PROTOS_IDX;
|
use ssl::AlpnError;
|
||||||
use x509::X509StoreContextRef;
|
use x509::X509StoreContextRef;
|
||||||
|
|
||||||
pub extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
|
pub extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
|
||||||
|
|
@ -111,60 +111,34 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub 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 {
|
|
||||||
// First, get the list of protocols (that the client should support) saved in the context
|
|
||||||
// extra data.
|
|
||||||
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>);
|
|
||||||
// Prepare the client list parameters to be passed to the OpenSSL function...
|
|
||||||
let client = protocols.as_ptr();
|
|
||||||
let client_len = protocols.len() as c_uint;
|
|
||||||
// Finally, let OpenSSL find a protocol to be used, by matching the given server and
|
|
||||||
// client lists.
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The function is given as the callback to `SSL_CTX_set_next_proto_select_cb`.
|
|
||||||
///
|
|
||||||
/// It chooses the protocol that the client wishes to use, out of the given list of protocols
|
|
||||||
/// supported by the server. It achieves this by delegating to the `SSL_select_next_proto`
|
|
||||||
/// function. The list of protocols supported by the client is found in the extra data of the
|
|
||||||
/// OpenSSL context.
|
|
||||||
pub 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)))]
|
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
|
||||||
pub extern "C" fn raw_alpn_select_cb(
|
pub extern "C" fn raw_alpn_select<F>(
|
||||||
ssl: *mut ffi::SSL,
|
ssl: *mut ffi::SSL,
|
||||||
out: *mut *const c_uchar,
|
out: *mut *const c_uchar,
|
||||||
outlen: *mut c_uchar,
|
outlen: *mut c_uchar,
|
||||||
inbuf: *const c_uchar,
|
inbuf: *const c_uchar,
|
||||||
inlen: c_uint,
|
inlen: c_uint,
|
||||||
_arg: *mut c_void,
|
_arg: *mut c_void,
|
||||||
) -> c_int {
|
) -> c_int
|
||||||
unsafe { select_proto_using(ssl, out as *mut _, outlen, inbuf, inlen, *ALPN_PROTOS_IDX) }
|
where
|
||||||
|
F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + '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);
|
||||||
|
let protos = slice::from_raw_parts(inbuf as *const u8, inlen as usize);
|
||||||
|
|
||||||
|
match callback(ssl, protos) {
|
||||||
|
Ok(proto) => {
|
||||||
|
*out = proto.as_ptr() as *const c_uchar;
|
||||||
|
*outlen = proto.len() as c_uchar;
|
||||||
|
ffi::SSL_TLSEXT_ERR_OK
|
||||||
|
}
|
||||||
|
Err(e) => e.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn raw_tmp_dh<F>(
|
pub unsafe extern "C" fn raw_tmp_dh<F>(
|
||||||
|
|
@ -302,35 +276,3 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The function is given as the callback to `SSL_CTX_set_next_protos_advertised_cb`.
|
|
||||||
///
|
|
||||||
/// It causes the parameter `out` to point at a `*const c_uchar` instance that
|
|
||||||
/// represents the list of protocols that the server should advertise as those
|
|
||||||
/// that it supports.
|
|
||||||
/// The list of supported protocols is found in the extra data of the OpenSSL
|
|
||||||
/// context.
|
|
||||||
pub 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 {
|
|
||||||
// First, get the list of (supported) protocols saved in the context extra data.
|
|
||||||
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 {
|
|
||||||
// If the pointer is valid, put the pointer to the actual byte array into the
|
|
||||||
// output parameter `out`, as well as its length into `outlen`.
|
|
||||||
let protocols: &Vec<u8> = &*(protocols as *mut Vec<u8>);
|
|
||||||
*out = protocols.as_ptr();
|
|
||||||
*outlen = protocols.len() as c_uint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ffi::SSL_TLSEXT_ERR_OK
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -357,15 +357,6 @@ fn get_ssl_callback_idx<T: 'static>() -> c_int {
|
||||||
.or_insert_with(|| get_new_ssl_idx::<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>(
|
unsafe extern "C" fn free_data_box<T>(
|
||||||
_parent: *mut c_void,
|
_parent: *mut c_void,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
|
|
@ -395,22 +386,6 @@ fn get_new_ssl_idx<T>() -> c_int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a set of byte slices into a series of byte strings encoded for SSL. Encoding is a byte
|
|
||||||
/// containing the length followed by the string.
|
|
||||||
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() {
|
|
||||||
// If the item does not fit, discard it
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
enc.push(len);
|
|
||||||
enc.extend(string[..len as usize].to_vec());
|
|
||||||
}
|
|
||||||
enc
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME look into this
|
// FIXME look into this
|
||||||
/// An error returned from an SNI callback.
|
/// An error returned from an SNI callback.
|
||||||
pub enum SniError {
|
pub enum SniError {
|
||||||
|
|
@ -419,6 +394,57 @@ pub enum SniError {
|
||||||
NoAck,
|
NoAck,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error returned from an ALPN selection callback.
|
||||||
|
///
|
||||||
|
/// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0.
|
||||||
|
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
|
||||||
|
pub struct AlpnError(c_int);
|
||||||
|
|
||||||
|
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
|
||||||
|
impl AlpnError {
|
||||||
|
/// Terminate the handshake with a fatal alert.
|
||||||
|
///
|
||||||
|
/// Requires the `v110` feature and OpenSSL 1.1.0.
|
||||||
|
#[cfg(all(feature = "v110", ossl110))]
|
||||||
|
pub const ALERT_FATAL: AlpnError = AlpnError(ffi::SSL_TLSEXT_ERR_ALERT_FATAL);
|
||||||
|
|
||||||
|
/// Do not select a protocol, but continue the handshake.
|
||||||
|
pub const NOACK: AlpnError = AlpnError(ffi::SSL_TLSEXT_ERR_NOACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A standard implementation of protocol selection for Application Layer Protocol Negotiation
|
||||||
|
/// (ALPN).
|
||||||
|
///
|
||||||
|
/// `server` should contain the server's list of supported protocols and `client` the client's. They
|
||||||
|
/// must both be in the ALPN wire format. See the documentation for
|
||||||
|
/// [`SslContextBuilder::set_alpn_protos`] for details.
|
||||||
|
///
|
||||||
|
/// It will select the first protocol supported by the server which is also supported by the client.
|
||||||
|
///
|
||||||
|
/// This corresponds to [`SSL_select_next_proto`].
|
||||||
|
///
|
||||||
|
/// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos
|
||||||
|
/// [`SSL_select_next_proto`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_alpn_protos.html
|
||||||
|
pub fn select_next_proto<'a>(server: &[u8], client: &'a [u8]) -> Option<&'a [u8]> {
|
||||||
|
unsafe {
|
||||||
|
let mut out = ptr::null_mut();
|
||||||
|
let mut outlen = 0;
|
||||||
|
let r = ffi::SSL_select_next_proto(
|
||||||
|
&mut out,
|
||||||
|
&mut outlen,
|
||||||
|
server.as_ptr(),
|
||||||
|
server.len() as c_uint,
|
||||||
|
client.as_ptr(),
|
||||||
|
client.len() as c_uint,
|
||||||
|
);
|
||||||
|
if r == ffi::OPENSSL_NPN_NEGOTIATED {
|
||||||
|
Some(slice::from_raw_parts(out as *const u8, outlen as usize))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A builder for `SslContext`s.
|
/// A builder for `SslContext`s.
|
||||||
pub struct SslContextBuilder(*mut ffi::SSL_CTX);
|
pub struct SslContextBuilder(*mut ffi::SSL_CTX);
|
||||||
|
|
||||||
|
|
@ -888,82 +914,68 @@ impl SslContextBuilder {
|
||||||
SslOptions::from_bits(ret).unwrap()
|
SslOptions::from_bits(ret).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the protocols to be used during Next Protocol Negotiation (the protocols
|
/// Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN).
|
||||||
/// supported by the application).
|
|
||||||
// FIXME overhaul
|
|
||||||
#[cfg(not(any(libressl261, libressl262, libressl26x)))]
|
|
||||||
pub fn set_npn_protocols(&mut self, protocols: &[&[u8]]) -> Result<(), ErrorStack> {
|
|
||||||
// Firstly, convert the list of protocols to a byte-array that can be passed to OpenSSL
|
|
||||||
// APIs -- a list of length-prefixed strings.
|
|
||||||
let protocols: Box<Vec<u8>> = Box::new(ssl_encode_byte_strings(protocols));
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
// Attach the protocol list to the OpenSSL context structure,
|
|
||||||
// so that we can refer to it within the callback.
|
|
||||||
cvt(ffi::SSL_CTX_set_ex_data(
|
|
||||||
self.as_ptr(),
|
|
||||||
*NPN_PROTOS_IDX,
|
|
||||||
Box::into_raw(protocols) as *mut c_void,
|
|
||||||
))?;
|
|
||||||
// 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.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.as_ptr(),
|
|
||||||
raw_next_protos_advertise_cb,
|
|
||||||
ptr::null_mut(),
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the protocols to be used during ALPN (application layer protocol negotiation).
|
|
||||||
/// If this is a server, these are the protocols we report to the client.
|
|
||||||
/// If this is a client, these are the protocols we try to match with those reported by the
|
|
||||||
/// server.
|
|
||||||
///
|
///
|
||||||
/// Note that ordering of the protocols controls the priority with which they are chosen.
|
/// The input must be in ALPN "wire format". It consists of a sequence of supported protocol
|
||||||
|
/// names prefixed by their byte length. For example, the protocol list consisting of `spdy/1`
|
||||||
|
/// and `http/1.1` is encoded as `b"\x06spdy/1\x08http/1.1"`. The protocols are ordered by
|
||||||
|
/// preference.
|
||||||
|
///
|
||||||
|
/// This corresponds to [`SSL_CTX_set_alpn_protos`].
|
||||||
///
|
///
|
||||||
/// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0.
|
/// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0.
|
||||||
// FIXME overhaul
|
///
|
||||||
|
/// [`SSL_CTX_set_alpn_protos`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_alpn_protos.html
|
||||||
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
|
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
|
||||||
pub fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> Result<(), ErrorStack> {
|
pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> {
|
||||||
let protocols: Box<Vec<u8>> = Box::new(ssl_encode_byte_strings(protocols));
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Set the context's internal protocol list for use if we are a server
|
assert!(protocols.len() <= c_uint::max_value() as usize);
|
||||||
let r = ffi::SSL_CTX_set_alpn_protos(
|
let r = ffi::SSL_CTX_set_alpn_protos(
|
||||||
self.as_ptr(),
|
self.as_ptr(),
|
||||||
protocols.as_ptr(),
|
protocols.as_ptr(),
|
||||||
protocols.len() as c_uint,
|
protocols.len() as c_uint,
|
||||||
);
|
);
|
||||||
// fun fact, SSL_CTX_set_alpn_protos has a reversed return code D:
|
// fun fact, SSL_CTX_set_alpn_protos has a reversed return code D:
|
||||||
if r != 0 {
|
if r == 0 {
|
||||||
return Err(ErrorStack::get());
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ErrorStack::get())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rather than use the argument to the callback to contain our data, store it in the
|
/// Sets the callback used by a server to select a protocol for Application Layer Protocol
|
||||||
// ssl ctx's ex_data so that we can configure a function to free it later. In the
|
/// Negotiation (ALPN).
|
||||||
// future, it might make sense to pull this into our internal struct Ssl instead of
|
///
|
||||||
// leaning on openssl and using function pointers.
|
/// The callback is provided with the client's protocol list in ALPN wire format. See the
|
||||||
cvt(ffi::SSL_CTX_set_ex_data(
|
/// documentation for [`SslContextBuilder::set_alpn_protos`] for details. It should return one
|
||||||
|
/// of those protocols on success. The [`select_next_proto`] function implements the standard
|
||||||
|
/// protocol selection algorithm.
|
||||||
|
///
|
||||||
|
/// This corresponds to [`SSL_CTX_set_alpn_select_cb`].
|
||||||
|
///
|
||||||
|
/// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0.
|
||||||
|
///
|
||||||
|
/// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos
|
||||||
|
/// [`select_next_proto`]: fn.select_next_proto.html
|
||||||
|
/// [`SSL_CTX_set_alpn_select_cb`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_alpn_protos.html
|
||||||
|
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
|
||||||
|
pub fn set_alpn_select_callback<F>(&mut self, callback: F)
|
||||||
|
where
|
||||||
|
F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let callback = Box::new(callback);
|
||||||
|
ffi::SSL_CTX_set_ex_data(
|
||||||
self.as_ptr(),
|
self.as_ptr(),
|
||||||
*ALPN_PROTOS_IDX,
|
get_callback_idx::<F>(),
|
||||||
Box::into_raw(protocols) as *mut c_void,
|
Box::into_raw(callback) as *mut c_void,
|
||||||
))?;
|
);
|
||||||
|
ffi::SSL_CTX_set_alpn_select_cb(
|
||||||
// Now register the callback that performs the default protocol
|
self.as_ptr(),
|
||||||
// matching based on the client-supported list of protocols that
|
callbacks::raw_alpn_select::<F>,
|
||||||
// has been saved.
|
ptr::null_mut(),
|
||||||
ffi::SSL_CTX_set_alpn_select_cb(self.as_ptr(), raw_alpn_select_cb, ptr::null_mut());
|
);
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1719,32 +1731,7 @@ impl SslRef {
|
||||||
str::from_utf8(version.to_bytes()).unwrap()
|
str::from_utf8(version.to_bytes()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the protocol selected by performing Next Protocol Negotiation, if any.
|
/// Returns the protocol selected via Application Layer Protocol Negotiation (ALPN).
|
||||||
///
|
|
||||||
/// The protocol's name is returned is an opaque sequence of bytes. It is up to the client
|
|
||||||
/// to interpret it.
|
|
||||||
///
|
|
||||||
/// This corresponds to [`SSL_get0_next_proto_negotiated`].
|
|
||||||
///
|
|
||||||
/// [`SSL_get0_next_proto_negotiated`]: https://www.openssl.org/docs/manmaster/man3/SSL_get0_next_proto_negotiated.html
|
|
||||||
#[cfg(not(any(libressl261, libressl262, libressl26x)))]
|
|
||||||
pub fn selected_npn_protocol(&self) -> Option<&[u8]> {
|
|
||||||
unsafe {
|
|
||||||
let mut data: *const c_uchar = ptr::null();
|
|
||||||
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.as_ptr(), &mut data, &mut len);
|
|
||||||
|
|
||||||
if data.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(slice::from_raw_parts(data, len as usize))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the protocol selected by performing ALPN, if any.
|
|
||||||
///
|
///
|
||||||
/// The protocol's name is returned is an opaque sequence of bytes. It is up to the client
|
/// The protocol's name is returned is an opaque sequence of bytes. It is up to the client
|
||||||
/// to interpret it.
|
/// to interpret it.
|
||||||
|
|
|
||||||
|
|
@ -479,7 +479,7 @@ fn test_connect_with_unilateral_alpn() {
|
||||||
let (_s, stream) = Server::new();
|
let (_s, stream) = Server::new();
|
||||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||||
ctx.set_verify(SslVerifyMode::PEER);
|
ctx.set_verify(SslVerifyMode::PEER);
|
||||||
ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap();
|
ctx.set_alpn_protos(b"\x08http/1.1\x08spdy/3.1").unwrap();
|
||||||
match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
|
match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => panic!("Unexpected error {:?}", err),
|
Err(err) => panic!("Unexpected error {:?}", err),
|
||||||
|
|
@ -493,28 +493,6 @@ fn test_connect_with_unilateral_alpn() {
|
||||||
assert!(stream.ssl().selected_alpn_protocol().is_none());
|
assert!(stream.ssl().selected_alpn_protocol().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests that connecting with the client using NPN, but the server not does not
|
|
||||||
/// break the existing connection behavior.
|
|
||||||
#[test]
|
|
||||||
#[cfg(not(any(libressl261, libressl262, libressl26x)))]
|
|
||||||
fn test_connect_with_unilateral_npn() {
|
|
||||||
let (_s, stream) = Server::new();
|
|
||||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
|
||||||
ctx.set_verify(SslVerifyMode::PEER);
|
|
||||||
ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap();
|
|
||||||
match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) => panic!("Unexpected error {:?}", err),
|
|
||||||
}
|
|
||||||
let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
|
|
||||||
Ok(stream) => stream,
|
|
||||||
Err(err) => panic!("Expected success, got {:?}", err),
|
|
||||||
};
|
|
||||||
// Since the socket to which we connected is not configured to use NPN,
|
|
||||||
// there should be no selected protocol...
|
|
||||||
assert!(stream.ssl().selected_npn_protocol().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests that when both the client as well as the server use ALPN and their
|
/// Tests that when both the client as well as the server use ALPN and their
|
||||||
/// lists of supported protocols have an overlap, the correct protocol is chosen.
|
/// lists of supported protocols have an overlap, the correct protocol is chosen.
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -523,7 +501,7 @@ fn test_connect_with_alpn_successful_multiple_matching() {
|
||||||
let (_s, stream) = Server::new_alpn();
|
let (_s, stream) = Server::new_alpn();
|
||||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||||
ctx.set_verify(SslVerifyMode::PEER);
|
ctx.set_verify(SslVerifyMode::PEER);
|
||||||
ctx.set_alpn_protocols(&[b"spdy/3.1", b"http/1.1"]).unwrap();
|
ctx.set_alpn_protos(b"\x08http/1.1\x08spdy/3.1").unwrap();
|
||||||
match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
|
match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => panic!("Unexpected error {:?}", err),
|
Err(err) => panic!("Unexpected error {:?}", err),
|
||||||
|
|
@ -537,28 +515,6 @@ fn test_connect_with_alpn_successful_multiple_matching() {
|
||||||
assert_eq!(b"http/1.1", stream.ssl().selected_alpn_protocol().unwrap());
|
assert_eq!(b"http/1.1", stream.ssl().selected_alpn_protocol().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests that when both the client as well as the server use NPN and their
|
|
||||||
/// lists of supported protocols have an overlap, the correct protocol is chosen.
|
|
||||||
#[test]
|
|
||||||
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
|
|
||||||
fn test_connect_with_npn_successful_multiple_matching() {
|
|
||||||
let (_s, stream) = Server::new_alpn();
|
|
||||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
|
||||||
ctx.set_verify(SslVerifyMode::PEER);
|
|
||||||
ctx.set_npn_protocols(&[b"spdy/3.1", b"http/1.1"]).unwrap();
|
|
||||||
match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) => panic!("Unexpected error {:?}", err),
|
|
||||||
}
|
|
||||||
let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
|
|
||||||
Ok(stream) => stream,
|
|
||||||
Err(err) => panic!("Expected success, got {:?}", err),
|
|
||||||
};
|
|
||||||
// The server prefers "http/1.1", so that is chosen, even though the client
|
|
||||||
// would prefer "spdy/3.1"
|
|
||||||
assert_eq!(b"http/1.1", stream.ssl().selected_npn_protocol().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests that when both the client as well as the server use ALPN and their
|
/// Tests that when both the client as well as the server use ALPN and their
|
||||||
/// lists of supported protocols have an overlap -- with only ONE protocol
|
/// lists of supported protocols have an overlap -- with only ONE protocol
|
||||||
/// being valid for both.
|
/// being valid for both.
|
||||||
|
|
@ -568,7 +524,7 @@ fn test_connect_with_alpn_successful_single_match() {
|
||||||
let (_s, stream) = Server::new_alpn();
|
let (_s, stream) = Server::new_alpn();
|
||||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||||
ctx.set_verify(SslVerifyMode::PEER);
|
ctx.set_verify(SslVerifyMode::PEER);
|
||||||
ctx.set_alpn_protocols(&[b"spdy/3.1"]).unwrap();
|
ctx.set_alpn_protos(b"\x08spdy/3.1").unwrap();
|
||||||
match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
|
match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => panic!("Unexpected error {:?}", err),
|
Err(err) => panic!("Unexpected error {:?}", err),
|
||||||
|
|
@ -582,72 +538,6 @@ fn test_connect_with_alpn_successful_single_match() {
|
||||||
assert_eq!(b"spdy/3.1", stream.ssl().selected_alpn_protocol().unwrap());
|
assert_eq!(b"spdy/3.1", stream.ssl().selected_alpn_protocol().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests that when both the client as well as the server use NPN and their
|
|
||||||
/// lists of supported protocols have an overlap -- with only ONE protocol
|
|
||||||
/// being valid for both.
|
|
||||||
#[test]
|
|
||||||
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
|
|
||||||
fn test_connect_with_npn_successful_single_match() {
|
|
||||||
let (_s, stream) = Server::new_alpn();
|
|
||||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
|
||||||
ctx.set_verify(SslVerifyMode::PEER);
|
|
||||||
ctx.set_npn_protocols(&[b"spdy/3.1"]).unwrap();
|
|
||||||
match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) => panic!("Unexpected error {:?}", err),
|
|
||||||
}
|
|
||||||
let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
|
|
||||||
Ok(stream) => stream,
|
|
||||||
Err(err) => panic!("Expected success, got {:?}", err),
|
|
||||||
};
|
|
||||||
// The client now only supports one of the server's protocols, so that one
|
|
||||||
// is used.
|
|
||||||
assert_eq!(b"spdy/3.1", stream.ssl().selected_npn_protocol().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests that when the `SslStream` is created as a server stream, the protocols
|
|
||||||
/// are correctly advertised to the client.
|
|
||||||
#[test]
|
|
||||||
#[cfg(not(any(libressl261, libressl262, libressl26x)))]
|
|
||||||
fn test_npn_server_advertise_multiple() {
|
|
||||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
|
||||||
let localhost = listener.local_addr().unwrap();
|
|
||||||
// We create a different context instance for the server...
|
|
||||||
let listener_ctx = {
|
|
||||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
|
||||||
ctx.set_verify(SslVerifyMode::PEER);
|
|
||||||
ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap();
|
|
||||||
assert!(
|
|
||||||
ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM)
|
|
||||||
.is_ok()
|
|
||||||
);
|
|
||||||
ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM)
|
|
||||||
.unwrap();
|
|
||||||
ctx.build()
|
|
||||||
};
|
|
||||||
// Have the listener wait on the connection in a different thread.
|
|
||||||
thread::spawn(move || {
|
|
||||||
let (stream, _) = listener.accept().unwrap();
|
|
||||||
Ssl::new(&listener_ctx).unwrap().accept(stream).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
|
||||||
ctx.set_verify(SslVerifyMode::PEER);
|
|
||||||
ctx.set_npn_protocols(&[b"spdy/3.1"]).unwrap();
|
|
||||||
match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) => panic!("Unexpected error {:?}", err),
|
|
||||||
}
|
|
||||||
// Now connect to the socket and make sure the protocol negotiation works...
|
|
||||||
let stream = TcpStream::connect(localhost).unwrap();
|
|
||||||
let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
|
|
||||||
Ok(stream) => stream,
|
|
||||||
Err(err) => panic!("Expected success, got {:?}", err),
|
|
||||||
};
|
|
||||||
// SPDY is selected since that's the only thing the client supports.
|
|
||||||
assert_eq!(b"spdy/3.1", stream.ssl().selected_npn_protocol().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests that when the `SslStream` is created as a server stream, the protocols
|
/// Tests that when the `SslStream` is created as a server stream, the protocols
|
||||||
/// are correctly advertised to the client.
|
/// are correctly advertised to the client.
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -658,8 +548,9 @@ fn test_alpn_server_advertise_multiple() {
|
||||||
// We create a different context instance for the server...
|
// We create a different context instance for the server...
|
||||||
let listener_ctx = {
|
let listener_ctx = {
|
||||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||||
ctx.set_verify(SslVerifyMode::PEER);
|
ctx.set_alpn_select_callback(|_, client| {
|
||||||
ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap();
|
ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client).ok_or(ssl::AlpnError::NOACK)
|
||||||
|
});
|
||||||
assert!(
|
assert!(
|
||||||
ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM)
|
ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
|
|
@ -676,7 +567,7 @@ fn test_alpn_server_advertise_multiple() {
|
||||||
|
|
||||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||||
ctx.set_verify(SslVerifyMode::PEER);
|
ctx.set_verify(SslVerifyMode::PEER);
|
||||||
ctx.set_alpn_protocols(&[b"spdy/3.1"]).unwrap();
|
ctx.set_alpn_protos(b"\x08spdy/3.1").unwrap();
|
||||||
match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
|
match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => panic!("Unexpected error {:?}", err),
|
Err(err) => panic!("Unexpected error {:?}", err),
|
||||||
|
|
@ -691,18 +582,18 @@ fn test_alpn_server_advertise_multiple() {
|
||||||
assert_eq!(b"spdy/3.1", stream.ssl().selected_alpn_protocol().unwrap());
|
assert_eq!(b"spdy/3.1", stream.ssl().selected_alpn_protocol().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test that Servers supporting ALPN don't report a protocol when none of their protocols match
|
|
||||||
/// the client's reported protocol.
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
|
#[cfg(all(feature = "v110", ossl110))]
|
||||||
fn test_alpn_server_select_none() {
|
fn test_alpn_server_select_none_fatal() {
|
||||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
let localhost = listener.local_addr().unwrap();
|
let localhost = listener.local_addr().unwrap();
|
||||||
// We create a different context instance for the server...
|
// We create a different context instance for the server...
|
||||||
let listener_ctx = {
|
let listener_ctx = {
|
||||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||||
ctx.set_verify(SslVerifyMode::PEER);
|
ctx.set_alpn_select_callback(|_, client| {
|
||||||
ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap();
|
ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client)
|
||||||
|
.ok_or(ssl::AlpnError::ALERT_FATAL)
|
||||||
|
});
|
||||||
assert!(
|
assert!(
|
||||||
ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM)
|
ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
|
|
@ -719,7 +610,40 @@ fn test_alpn_server_select_none() {
|
||||||
|
|
||||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||||
ctx.set_verify(SslVerifyMode::PEER);
|
ctx.set_verify(SslVerifyMode::PEER);
|
||||||
ctx.set_alpn_protocols(&[b"http/2"]).unwrap();
|
ctx.set_alpn_protos(b"\x06http/2").unwrap();
|
||||||
|
ctx.set_ca_file(&Path::new("test/root-ca.pem")).unwrap();
|
||||||
|
let stream = TcpStream::connect(localhost).unwrap();
|
||||||
|
Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
|
||||||
|
fn test_alpn_server_select_none() {
|
||||||
|
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
|
let localhost = listener.local_addr().unwrap();
|
||||||
|
// We create a different context instance for the server...
|
||||||
|
let listener_ctx = {
|
||||||
|
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||||
|
ctx.set_alpn_select_callback(|_, client| {
|
||||||
|
ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client).ok_or(ssl::AlpnError::NOACK)
|
||||||
|
});
|
||||||
|
assert!(
|
||||||
|
ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM)
|
||||||
|
.unwrap();
|
||||||
|
ctx.build()
|
||||||
|
};
|
||||||
|
// Have the listener wait on the connection in a different thread.
|
||||||
|
thread::spawn(move || {
|
||||||
|
let (stream, _) = listener.accept().unwrap();
|
||||||
|
Ssl::new(&listener_ctx).unwrap().accept(stream).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||||
|
ctx.set_verify(SslVerifyMode::PEER);
|
||||||
|
ctx.set_alpn_protos(b"\x06http/2").unwrap();
|
||||||
ctx.set_ca_file(&Path::new("test/root-ca.pem")).unwrap();
|
ctx.set_ca_file(&Path::new("test/root-ca.pem")).unwrap();
|
||||||
// Now connect to the socket and make sure the protocol negotiation works...
|
// Now connect to the socket and make sure the protocol negotiation works...
|
||||||
let stream = TcpStream::connect(localhost).unwrap();
|
let stream = TcpStream::connect(localhost).unwrap();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue