Loading openssl-sys/src/lib.rs +1 −0 Original line number Diff line number Diff line Loading @@ -2495,6 +2495,7 @@ extern "C" { #[cfg(not(ossl101))] 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))] pub fn SSL_CTX_set_alpn_select_cb( ssl: *mut SSL_CTX, Loading openssl/src/ssl/callbacks.rs +23 −81 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -111,60 +111,34 @@ where } } pub unsafe fn select_proto_using( #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] pub extern "C" fn raw_alpn_select<F>( ssl: *mut ffi::SSL, out: *mut *mut c_uchar, out: *mut *const 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 _arg: *mut c_void, ) -> c_int where F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send, { ffi::SSL_TLSEXT_ERR_NOACK } else { 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, } /// 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( 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) } } pub unsafe extern "C" fn raw_tmp_dh<F>( Loading Loading @@ -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 } openssl/src/ssl/mod.rs +100 −113 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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 { Loading @@ -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); Loading Loading @@ -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)); /// 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. /// /// This corresponds to [`SSL_CTX_set_alpn_protos`]. /// /// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0. /// /// [`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_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { 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( assert!(protocols.len() <= c_uint::max_value() as usize); let r = ffi::SSL_CTX_set_alpn_protos( self.as_ptr(), raw_next_protos_advertise_cb, ptr::null_mut(), protocols.as_ptr(), protocols.len() as c_uint, ); // fun fact, SSL_CTX_set_alpn_protos has a reversed return code D: if r == 0 { Ok(()) } else { Err(ErrorStack::get()) } } } /// 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 callback used by a server to select a protocol for Application Layer Protocol /// Negotiation (ALPN). /// /// Note that ordering of the protocols controls the priority with which they are chosen. /// 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. // FIXME overhaul /// /// [`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_protocols(&mut self, protocols: &[&[u8]]) -> Result<(), ErrorStack> { let protocols: Box<Vec<u8>> = Box::new(ssl_encode_byte_strings(protocols)); 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 { // Set the context's internal protocol list for use if we are a server let r = ffi::SSL_CTX_set_alpn_protos( let callback = Box::new(callback); ffi::SSL_CTX_set_ex_data( self.as_ptr(), protocols.as_ptr(), protocols.len() as c_uint, get_callback_idx::<F>(), Box::into_raw(callback) as *mut c_void, ); // fun fact, SSL_CTX_set_alpn_protos has a reversed return code D: if r != 0 { return 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( ffi::SSL_CTX_set_alpn_select_cb( 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(()) callbacks::raw_alpn_select::<F>, ptr::null_mut(), ); } } Loading Loading @@ -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. Loading openssl/src/ssl/test.rs +23 −99 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
openssl-sys/src/lib.rs +1 −0 Original line number Diff line number Diff line Loading @@ -2495,6 +2495,7 @@ extern "C" { #[cfg(not(ossl101))] 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))] pub fn SSL_CTX_set_alpn_select_cb( ssl: *mut SSL_CTX, Loading
openssl/src/ssl/callbacks.rs +23 −81 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -111,60 +111,34 @@ where } } pub unsafe fn select_proto_using( #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] pub extern "C" fn raw_alpn_select<F>( ssl: *mut ffi::SSL, out: *mut *mut c_uchar, out: *mut *const 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 _arg: *mut c_void, ) -> c_int where F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send, { ffi::SSL_TLSEXT_ERR_NOACK } else { 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, } /// 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( 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) } } pub unsafe extern "C" fn raw_tmp_dh<F>( Loading Loading @@ -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 }
openssl/src/ssl/mod.rs +100 −113 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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 { Loading @@ -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); Loading Loading @@ -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)); /// 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. /// /// This corresponds to [`SSL_CTX_set_alpn_protos`]. /// /// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0. /// /// [`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_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { 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( assert!(protocols.len() <= c_uint::max_value() as usize); let r = ffi::SSL_CTX_set_alpn_protos( self.as_ptr(), raw_next_protos_advertise_cb, ptr::null_mut(), protocols.as_ptr(), protocols.len() as c_uint, ); // fun fact, SSL_CTX_set_alpn_protos has a reversed return code D: if r == 0 { Ok(()) } else { Err(ErrorStack::get()) } } } /// 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 callback used by a server to select a protocol for Application Layer Protocol /// Negotiation (ALPN). /// /// Note that ordering of the protocols controls the priority with which they are chosen. /// 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. // FIXME overhaul /// /// [`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_protocols(&mut self, protocols: &[&[u8]]) -> Result<(), ErrorStack> { let protocols: Box<Vec<u8>> = Box::new(ssl_encode_byte_strings(protocols)); 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 { // Set the context's internal protocol list for use if we are a server let r = ffi::SSL_CTX_set_alpn_protos( let callback = Box::new(callback); ffi::SSL_CTX_set_ex_data( self.as_ptr(), protocols.as_ptr(), protocols.len() as c_uint, get_callback_idx::<F>(), Box::into_raw(callback) as *mut c_void, ); // fun fact, SSL_CTX_set_alpn_protos has a reversed return code D: if r != 0 { return 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( ffi::SSL_CTX_set_alpn_select_cb( 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(()) callbacks::raw_alpn_select::<F>, ptr::null_mut(), ); } } Loading Loading @@ -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. Loading
openssl/src/ssl/test.rs +23 −99 File changed.Preview size limit exceeded, changes collapsed. Show changes