aboutsummaryrefslogtreecommitdiff
path: root/openssl/src
diff options
context:
space:
mode:
authorSteven Fackler <[email protected]>2017-12-27 16:30:28 -0700
committerGitHub <[email protected]>2017-12-27 16:30:28 -0700
commite1d442e65b3553e17d78f12c4fc3ebb96058d9d8 (patch)
tree4ae5545289c2730f161aee630a0f20adbcda63c1 /openssl/src
parentMerge pull request #802 from sfackler/ssl-error (diff)
parentOverhaul ALPN (diff)
downloadrust-openssl-e1d442e65b3553e17d78f12c4fc3ebb96058d9d8.tar.xz
rust-openssl-e1d442e65b3553e17d78f12c4fc3ebb96058d9d8.zip
Merge pull request #804 from sfackler/alpn-overhaul
Overhaul ALPN
Diffstat (limited to 'openssl/src')
-rw-r--r--openssl/src/ssl/callbacks.rs104
-rw-r--r--openssl/src/ssl/mod.rs213
-rw-r--r--openssl/src/ssl/test.rs122
3 files changed, 146 insertions, 293 deletions
diff --git a/openssl/src/ssl/callbacks.rs b/openssl/src/ssl/callbacks.rs
index 26aa8b86..0d211691 100644
--- a/openssl/src/ssl/callbacks.rs
+++ b/openssl/src/ssl/callbacks.rs
@@ -10,9 +10,9 @@ use error::ErrorStack;
use dh::Dh;
#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
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)))]
-use ssl::ALPN_PROTOS_IDX;
+use ssl::AlpnError;
use x509::X509StoreContextRef;
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)))]
-pub extern "C" fn raw_alpn_select_cb(
+pub extern "C" fn raw_alpn_select<F>(
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) }
+) -> c_int
+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>(
@@ -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
-}
diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs
index 5ef04fca..dbe53300 100644
--- a/openssl/src/ssl/mod.rs
+++ b/openssl/src/ssl/mod.rs
@@ -357,15 +357,6 @@ fn get_ssl_callback_idx<T: 'static>() -> c_int {
.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,
@@ -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
/// An error returned from an SNI callback.
pub enum SniError {
@@ -419,6 +394,57 @@ pub enum SniError {
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.
pub struct SslContextBuilder(*mut ffi::SSL_CTX);
@@ -888,82 +914,68 @@ impl SslContextBuilder {
SslOptions::from_bits(ret).unwrap()
}
- /// Set the protocols to be used during Next Protocol Negotiation (the protocols
- /// 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.
+ /// Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN).
+ ///
+ /// 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.
///
- /// Note that ordering of the protocols controls the priority with which they are chosen.
+ /// This corresponds to [`SSL_CTX_set_alpn_protos`].
///
/// 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)))]
- pub fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> Result<(), ErrorStack> {
- let protocols: Box<Vec<u8>> = Box::new(ssl_encode_byte_strings(protocols));
+ pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> {
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(
self.as_ptr(),
protocols.as_ptr(),
protocols.len() as c_uint,
);
// fun fact, SSL_CTX_set_alpn_protos has a reversed return code D:
- if r != 0 {
- return Err(ErrorStack::get());
+ if r == 0 {
+ Ok(())
+ } else {
+ Err(ErrorStack::get())
}
+ }
+ }
- // 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.
- cvt(ffi::SSL_CTX_set_ex_data(
+ /// Sets the callback used by a server to select a protocol for Application Layer Protocol
+ /// Negotiation (ALPN).
+ ///
+ /// The callback is provided with the client's protocol list in ALPN wire format. See the
+ /// 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(),
- *ALPN_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_alpn_select_cb(self.as_ptr(), raw_alpn_select_cb, ptr::null_mut());
-
- Ok(())
+ get_callback_idx::<F>(),
+ Box::into_raw(callback) as *mut c_void,
+ );
+ ffi::SSL_CTX_set_alpn_select_cb(
+ self.as_ptr(),
+ callbacks::raw_alpn_select::<F>,
+ ptr::null_mut(),
+ );
}
}
@@ -1719,32 +1731,7 @@ impl SslRef {
str::from_utf8(version.to_bytes()).unwrap()
}
- /// Returns the protocol selected by performing Next Protocol Negotiation, if any.
- ///
- /// 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.
+ /// 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.
diff --git a/openssl/src/ssl/test.rs b/openssl/src/ssl/test.rs
index 3beaf846..f4e5b11b 100644
--- a/openssl/src/ssl/test.rs
+++ b/openssl/src/ssl/test.rs
@@ -479,7 +479,7 @@ fn test_connect_with_unilateral_alpn() {
let (_s, stream) = Server::new();
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
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")) {
Ok(_) => {}
Err(err) => panic!("Unexpected error {:?}", err),
@@ -493,28 +493,6 @@ fn test_connect_with_unilateral_alpn() {
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
/// lists of supported protocols have an overlap, the correct protocol is chosen.
#[test]
@@ -523,7 +501,7 @@ fn test_connect_with_alpn_successful_multiple_matching() {
let (_s, stream) = Server::new_alpn();
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
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")) {
Ok(_) => {}
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());
}
-/// 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
/// lists of supported protocols have an overlap -- with only ONE protocol
/// being valid for both.
@@ -568,7 +524,7 @@ fn test_connect_with_alpn_successful_single_match() {
let (_s, stream) = Server::new_alpn();
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
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")) {
Ok(_) => {}
Err(err) => panic!("Unexpected error {:?}", err),
@@ -582,41 +538,19 @@ fn test_connect_with_alpn_successful_single_match() {
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() {
+#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
+fn test_alpn_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();
+ 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()
@@ -633,7 +567,7 @@ fn test_npn_server_advertise_multiple() {
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SslVerifyMode::PEER);
- ctx.set_npn_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")) {
Ok(_) => {}
Err(err) => panic!("Unexpected error {:?}", err),
@@ -645,21 +579,21 @@ fn test_npn_server_advertise_multiple() {
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());
+ assert_eq!(b"spdy/3.1", stream.ssl().selected_alpn_protocol().unwrap());
}
-/// Tests that when the `SslStream` is created as a server stream, the protocols
-/// are correctly advertised to the client.
#[test]
-#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
-fn test_alpn_server_advertise_multiple() {
+#[cfg(all(feature = "v110", ossl110))]
+fn test_alpn_server_select_none_fatal() {
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_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap();
+ ctx.set_alpn_select_callback(|_, client| {
+ ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client)
+ .ok_or(ssl::AlpnError::ALERT_FATAL)
+ });
assert!(
ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM)
.is_ok()
@@ -676,23 +610,12 @@ fn test_alpn_server_advertise_multiple() {
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SslVerifyMode::PEER);
- ctx.set_alpn_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...
+ 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();
- 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_alpn_protocol().unwrap());
+ Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap_err();
}
-/// Test that Servers supporting ALPN don't report a protocol when none of their protocols match
-/// the client's reported protocol.
#[test]
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
fn test_alpn_server_select_none() {
@@ -701,8 +624,9 @@ fn test_alpn_server_select_none() {
// 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_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).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()
@@ -719,7 +643,7 @@ fn test_alpn_server_select_none() {
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
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();
// Now connect to the socket and make sure the protocol negotiation works...
let stream = TcpStream::connect(localhost).unwrap();