Skip to content

Instantly share code, notes, and snippets.

@cpu

cpu/rustls.rs Secret

Created March 30, 2024 17:18
Show Gist options
  • Save cpu/888f13381ef86f084f447d7133791e69 to your computer and use it in GitHub Desktop.
Save cpu/888f13381ef86f084f447d7133791e69 to your computer and use it in GitHub Desktop.
cargo expand of rustls-ffi 10c5324a43a84afc4b13da0c148e6bdd74b4caa1
#![feature(prelude_import)]
#![crate_type = "staticlib"]
#![allow(non_camel_case_types)]
#![allow(clippy::not_unsafe_ptr_arg_deref)]
#![allow(clippy::arc_with_non_send_sync)]
//! This package contains bindings for using rustls via a C API. If
//! you're looking at this on docs.rs, [you may want the rustls docs
//! instead](https://docs.rs/rustls/latest/rustls/).
//!
//! Even though this is a C API, it is published on crates.io so other crates that
//! wrap a different C API (like curl) can depend on it.
//!
//! [You may also want to read the rustls-ffi README](https://github.com/rustls/rustls-ffi#rustls-ffi-bindings).
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use crate::rslice::rustls_str;
use libc::c_void;
use std::cell::RefCell;
use std::mem;
use std::sync::Arc;
pub mod acceptor {
use std::sync::Arc;
use libc::{c_void, size_t, EINVAL, EIO};
use rustls::server::{Accepted, AcceptedAlert, Acceptor};
use rustls::ServerConfig;
use crate::connection::rustls_connection;
use crate::error::{map_error, rustls_io_result};
use crate::io::{
rustls_read_callback, rustls_write_callback, CallbackReader, CallbackWriter,
ReadCallback,
};
use crate::rslice::{rustls_slice_bytes, rustls_str};
use crate::server::rustls_server_config;
use crate::{
box_castable, ffi_panic_boundary, free_box, rustls_result, set_boxed_mut_ptr,
to_box, to_boxed_mut_ptr, try_callback, try_clone_arc, try_mut_from_ptr,
try_ref_from_ptr, try_take,
};
use rustls_result::NullParameter;
/// A buffer and parser for ClientHello bytes. This allows reading ClientHello
/// before choosing a rustls_server_config. It's useful when the server
/// config will be based on parameters in the ClientHello: server name
/// indication (SNI), ALPN protocols, signature schemes, and cipher suites. In
/// particular, if a server wants to do some potentially expensive work to load a
/// certificate for a given hostname, rustls_acceptor allows doing that asynchronously,
/// as opposed to rustls_server_config_builder_set_hello_callback(), which doesn't
/// work well for asynchronous I/O.
///
/// The general flow is:
/// - rustls_acceptor_new()
/// - Loop:
/// - Read bytes from the network it with rustls_acceptor_read_tls().
/// - If successful, parse those bytes with rustls_acceptor_accept().
/// - If that returns RUSTLS_RESULT_ACCEPTOR_NOT_READY, continue.
/// - Otherwise, break.
/// - If rustls_acceptor_accept() returned RUSTLS_RESULT_OK:
/// - Examine the resulting rustls_accepted.
/// - Create or select a rustls_server_config.
/// - Call rustls_accepted_into_connection().
/// - Otherwise, there was a problem with the ClientHello data and the
/// connection should be rejected.
pub struct rustls_acceptor {
_private: [u8; 0],
}
impl crate::Castable for rustls_acceptor {
type Ownership = crate::OwnershipBox;
type RustType = Acceptor;
}
/// A parsed ClientHello produced by a rustls_acceptor. It is used to check
/// server name indication (SNI), ALPN protocols, signature schemes, and
/// cipher suites. It can be combined with a rustls_server_config to build a
/// rustls_connection.
pub struct rustls_accepted {
_private: [u8; 0],
}
impl crate::Castable for rustls_accepted {
type Ownership = crate::OwnershipBox;
type RustType = Option<Accepted>;
}
impl rustls_acceptor {
/// Create and return a new rustls_acceptor.
///
/// Caller owns the pointed-to memory and must eventually free it with
/// `rustls_acceptor_free()`.
#[no_mangle]
pub extern "C" fn rustls_acceptor_new() -> *mut rustls_acceptor {
match ::std::panic::catch_unwind(|| {
to_boxed_mut_ptr(Acceptor::default())
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Free a rustls_acceptor.
///
/// Parameters:
///
/// acceptor: The rustls_acceptor to free.
///
/// Calling with NULL is fine. Must not be called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_acceptor_free(acceptor: *mut rustls_acceptor) {
match ::std::panic::catch_unwind(|| {
to_box(acceptor);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Read some TLS bytes from the network into internal buffers. The actual network
/// I/O is performed by `callback`, which you provide. Rustls will invoke your
/// callback with a suitable buffer to store the read bytes into. You don't have
/// to fill it up, just fill with as many bytes as you get in one syscall.
///
/// Parameters:
///
/// acceptor: The rustls_acceptor to read bytes into.
/// callback: A function that will perform the actual network I/O.
/// Must be valid to call with the given userdata parameter until
/// this function call returns.
/// userdata: An opaque parameter to be passed directly to `callback`.
/// Note: this is distinct from the `userdata` parameter set with
/// `rustls_connection_set_userdata`.
/// out_n: An output parameter. This will be passed through to `callback`,
/// which should use it to store the number of bytes written.
///
/// Returns:
///
/// - 0: Success. You should call `rustls_acceptor_accept()` next.
/// - Any non-zero value: error.
///
/// This function passes through return values from `callback`. Typically
/// `callback` should return an errno value. See `rustls_read_callback()` for
/// more details.
#[no_mangle]
pub extern "C" fn rustls_acceptor_read_tls(
acceptor: *mut rustls_acceptor,
callback: rustls_read_callback,
userdata: *mut c_void,
out_n: *mut size_t,
) -> rustls_io_result {
match ::std::panic::catch_unwind(|| {
let acceptor: &mut Acceptor = match crate::try_from_mut(acceptor) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
if out_n.is_null() {
return rustls_io_result(EINVAL);
}
let callback: ReadCallback = match callback {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let mut reader = CallbackReader {
callback,
userdata,
};
let n_read: usize = match acceptor.read_tls(&mut reader) {
Ok(n) => n,
Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)),
};
unsafe {
*out_n = n_read;
}
rustls_io_result(0)
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Parse all TLS bytes read so far. If those bytes make up a ClientHello,
/// create a rustls_accepted from them.
///
/// Parameters:
///
/// acceptor: The rustls_acceptor to access.
/// out_accepted: An output parameter. The pointed-to pointer will be set
/// to a new rustls_accepted only when the function returns
/// RUSTLS_RESULT_OK. The memory is owned by the caller and must eventually
/// be freed
/// out_alert: An output parameter. The pointed-to pointer will be set
/// to a new rustls_accepted_alert only when the function returns
/// a non-OK result. The memory is owned by the caller and must eventually
/// be freed with rustls_accepted_alert_free. The caller should call
/// rustls_accepted_alert_write_tls to write the alert bytes to the TLS
/// connection before freeing the rustls_accepted_alert.
///
/// At most one of out_accepted or out_alert will be set.
///
/// Returns:
///
/// - RUSTLS_RESULT_OK: a ClientHello has successfully been parsed.
/// A pointer to a newly allocated rustls_accepted has been written to
/// *out_accepted.
/// - RUSTLS_RESULT_ACCEPTOR_NOT_READY: a full ClientHello has not yet been read.
/// Read more TLS bytes to continue.
/// - Any other rustls_result: the TLS bytes read so far cannot be parsed
/// as a ClientHello, and reading additional bytes won't help.
///
/// Memory and lifetimes:
///
/// After this method returns RUSTLS_RESULT_OK, `acceptor` is
/// still allocated and valid. It needs to be freed regardless of success
/// or failure of this function.
///
/// Calling `rustls_acceptor_accept()` multiple times on the same
/// `rustls_acceptor` is acceptable from a memory perspective but pointless
/// from a protocol perspective.
#[no_mangle]
pub extern "C" fn rustls_acceptor_accept(
acceptor: *mut rustls_acceptor,
out_accepted: *mut *mut rustls_accepted,
out_alert: *mut *mut rustls_accepted_alert,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let acceptor: &mut Acceptor = match crate::try_from_mut(acceptor) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
if out_accepted.is_null() {
return NullParameter;
}
if out_alert.is_null() {
return NullParameter;
}
match acceptor.accept() {
Ok(None) => rustls_result::AcceptorNotReady,
Ok(Some(accepted)) => {
set_boxed_mut_ptr(out_accepted, Some(accepted));
rustls_result::Ok
}
Err((e, accepted_alert)) => {
set_boxed_mut_ptr(out_alert, accepted_alert);
map_error(e)
}
}
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
impl rustls_accepted {
/// Get the server name indication (SNI) from the ClientHello.
///
/// Parameters:
///
/// accepted: The rustls_accepted to access.
///
/// Returns:
///
/// A rustls_str containing the SNI field.
///
/// The returned value is valid until rustls_accepted_into_connection or
/// rustls_accepted_free is called on the same `accepted`. It is not owned
/// by the caller and does not need to be freed.
///
/// This will be a zero-length rustls_str in these error cases:
///
/// - The SNI contains a NUL byte.
/// - The `accepted` parameter was NULL.
/// - The `accepted` parameter was already transformed into a connection
/// with rustls_accepted_into_connection.
#[no_mangle]
pub extern "C" fn rustls_accepted_server_name(
accepted: *const rustls_accepted,
) -> rustls_str<'static> {
match ::std::panic::catch_unwind(|| {
let accepted: &Option<Accepted> = match crate::try_from(accepted) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let accepted = match accepted {
Some(a) => a,
None => return Default::default(),
};
let hello = accepted.client_hello();
let sni = match hello.server_name() {
Some(s) => s,
None => return Default::default(),
};
match rustls_str::try_from(sni) {
Ok(s) => unsafe { s.into_static() }
Err(_) => Default::default(),
}
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Get the i'th in the list of signature schemes offered in the ClientHello.
/// This is useful in selecting a server certificate when there are multiple
/// available for the same server name, for instance when selecting
/// between an RSA and an ECDSA certificate.
///
/// Parameters:
///
/// accepted: The rustls_accepted to access.
/// i: Fetch the signature scheme at this offset.
///
/// Returns:
///
/// A TLS Signature Scheme from <https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme>
///
/// This will be 0 in these cases:
/// - i is greater than the number of available cipher suites.
/// - accepted is NULL.
/// - rustls_accepted_into_connection has already been called with `accepted`.
#[no_mangle]
pub extern "C" fn rustls_accepted_signature_scheme(
accepted: *const rustls_accepted,
i: usize,
) -> u16 {
match ::std::panic::catch_unwind(|| {
let accepted: &Option<Accepted> = match crate::try_from(accepted) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let accepted = match accepted {
Some(a) => a,
None => return 0,
};
let hello = accepted.client_hello();
let signature_schemes = hello.signature_schemes();
match signature_schemes.get(i) {
Some(s) => u16::from(*s),
None => 0,
}
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Get the i'th in the list of cipher suites offered in the ClientHello.
///
/// Parameters:
///
/// accepted: The rustls_accepted to access.
/// i: Fetch the cipher suite at this offset.
///
/// Returns:
///
/// A cipher suite value from <https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4.>
///
/// This will be 0 in these cases:
/// - i is greater than the number of available cipher suites.
/// - accepted is NULL.
/// - rustls_accepted_into_connection has already been called with `accepted`.
///
/// Note that 0 is technically a valid cipher suite "TLS_NULL_WITH_NULL_NULL",
/// but this library will never support null ciphers.
#[no_mangle]
pub extern "C" fn rustls_accepted_cipher_suite(
accepted: *const rustls_accepted,
i: usize,
) -> u16 {
match ::std::panic::catch_unwind(|| {
let accepted: &Option<Accepted> = match crate::try_from(accepted) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let accepted = match accepted {
Some(a) => a,
None => return 0,
};
let hello = accepted.client_hello();
let cipher_suites = hello.cipher_suites();
match cipher_suites.get(i) {
Some(cs) => u16::from(*cs),
None => 0,
}
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Get the i'th in the list of ALPN protocols requested in the ClientHello.
///
/// accepted: The rustls_accepted to access.
/// i: Fetch the ALPN value at this offset.
///
/// Returns:
///
/// A rustls_slice_bytes containing the i'th ALPN protocol. This may
/// contain internal NUL bytes and is not guaranteed to contain valid
/// UTF-8.
///
/// This will be a zero-length rustls_slice bytes in these cases:
/// - i is greater than the number of offered ALPN protocols.
/// - The client did not offer the ALPN extension.
/// - The `accepted` parameter was already transformed into a connection
/// with rustls_accepted_into_connection.
///
/// The returned value is valid until rustls_accepted_into_connection or
/// rustls_accepted_free is called on the same `accepted`. It is not owned
/// by the caller and does not need to be freed.
///
/// If you are calling this from Rust, note that the `'static` lifetime
/// in the return signature is fake and must not be relied upon.
#[no_mangle]
pub extern "C" fn rustls_accepted_alpn(
accepted: *const rustls_accepted,
i: usize,
) -> rustls_slice_bytes<'static> {
match ::std::panic::catch_unwind(|| {
let accepted: &Option<Accepted> = match crate::try_from(accepted) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let accepted = match accepted {
Some(a) => a,
None => return Default::default(),
};
let mut alpn_iter = match accepted.client_hello().alpn() {
Some(iter) => iter,
None => return Default::default(),
};
match alpn_iter.nth(i) {
Some(slice_bytes) => slice_bytes.into(),
None => rustls_slice_bytes::default(),
}
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Turn a rustls_accepted into a rustls_connection, given the provided
/// rustls_server_config.
///
/// Parameters:
///
/// accepted: The rustls_accepted to transform.
/// config: The configuration with which to create this connection.
/// out_conn: An output parameter. The pointed-to pointer will be set
/// to a new rustls_connection only when the function returns
/// RUSTLS_RESULT_OK.
/// out_alert: An output parameter. The pointed-to pointer will be set
/// to a new rustls_accepted_alert when, and only when, the function returns
/// a non-OK result. The memory is owned by the caller and must eventually
/// be freed with rustls_accepted_alert_free. The caller should call
/// rustls_accepted_alert_write_tls to write the alert bytes to
/// the TLS connection before freeing the rustls_accepted_alert.
///
/// At most one of out_conn or out_alert will be set.
///
/// Returns:
///
/// - RUSTLS_RESULT_OK: The `accepted` parameter was successfully
/// transformed into a rustls_connection, and *out_conn was written to.
/// - RUSTLS_RESULT_ALREADY_USED: This function was called twice on the
/// same rustls_connection.
/// - RUSTLS_RESULT_NULL_PARAMETER: One of the input parameters was NULL.
///
/// Memory and lifetimes:
///
/// In both success and failure cases, this consumes the contents of
/// `accepted` but does not free its allocated memory. In either case,
/// call rustls_accepted_free to avoid a memory leak.
///
/// Calling accessor methods on an `accepted` after consuming it will
/// return zero or default values.
///
/// The rustls_connection emitted by this function in the success case
/// is owned by the caller and must eventually be freed.
///
/// This function does not take ownership of `config`. It does increment
/// `config`'s internal reference count, indicating that the
/// rustls_connection may hold a reference to it until it is done.
/// See the documentation for rustls_connection for details.
#[no_mangle]
pub extern "C" fn rustls_accepted_into_connection(
accepted: *mut rustls_accepted,
config: *const rustls_server_config,
out_conn: *mut *mut rustls_connection,
out_alert: *mut *mut rustls_accepted_alert,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let accepted: &mut Option<Accepted> = match crate::try_from_mut(
accepted,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let accepted = match accepted.take() {
None => {
return crate::rustls_result::AlreadyUsed;
}
Some(x) => x,
};
let config: Arc<ServerConfig> = match crate::clone_arc(config) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
if out_conn.is_null() {
return NullParameter;
}
if out_alert.is_null() {
return NullParameter;
}
match accepted.into_connection(config) {
Ok(built) => {
let wrapped = crate::connection::Connection::from_server(built);
set_boxed_mut_ptr(out_conn, wrapped);
rustls_result::Ok
}
Err((e, accepted_alert)) => {
set_boxed_mut_ptr(out_alert, accepted_alert);
map_error(e)
}
}
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Free a rustls_accepted.
///
/// Parameters:
///
/// accepted: The rustls_accepted to free.
///
/// Calling with NULL is fine. Must not be called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_accepted_free(accepted: *mut rustls_accepted) {
match ::std::panic::catch_unwind(|| {
free_box(accepted);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
/// Represents a TLS alert resulting from accepting a client.
pub struct rustls_accepted_alert {
_private: [u8; 0],
}
impl crate::Castable for rustls_accepted_alert {
type Ownership = crate::OwnershipBox;
type RustType = AcceptedAlert;
}
impl rustls_accepted_alert {
/// Write some TLS bytes (an alert) to the network. The actual network I/O is
/// performed by `callback`, which you provide. Rustls will invoke your callback with a
/// suitable buffer containing TLS bytes to send. You don't have to write them
/// all, just as many as you can in one syscall.
/// The `userdata` parameter is passed through directly to `callback`. Note that
/// this is distinct from the `userdata` parameter set with
/// `rustls_connection_set_userdata`.
/// Returns 0 for success, or an errno value on error. Passes through return values
/// from callback. See [`rustls_write_callback`] or [`AcceptedAlert`] for
/// more details.
#[no_mangle]
pub extern "C" fn rustls_accepted_alert_write_tls(
accepted_alert: *mut rustls_accepted_alert,
callback: rustls_write_callback,
userdata: *mut c_void,
out_n: *mut size_t,
) -> rustls_io_result {
match ::std::panic::catch_unwind(|| {
if out_n.is_null() {
return rustls_io_result(EINVAL);
}
let accepted_alert: &mut AcceptedAlert = match crate::try_from_mut(
accepted_alert,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let mut writer = CallbackWriter {
callback: match callback {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
},
userdata,
};
let n_written: usize = match accepted_alert.write(&mut writer) {
Ok(n) => n,
Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)),
};
unsafe {
*out_n = n_written;
}
rustls_io_result(0)
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Free a rustls_accepted_alert.
///
/// Parameters:
///
/// accepted_alert: The rustls_accepted_alert to free.
///
/// Calling with NULL is fine. Must not be called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_accepted_alert_free(
accepted_alert: *mut rustls_accepted_alert,
) {
match ::std::panic::catch_unwind(|| {
free_box(accepted_alert);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
}
pub mod cipher {
use libc::{c_char, size_t};
use std::ffi::{CStr, OsStr};
use std::fs::File;
use std::io::{BufReader, Cursor};
use std::marker::PhantomData;
use std::ptr::null;
use std::slice;
use std::sync::Arc;
use pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer};
use rustls::client::danger::ServerCertVerifier;
use rustls::client::WebPkiServerVerifier;
use rustls::crypto::ring::{ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES};
use rustls::server::danger::ClientCertVerifier;
use rustls::server::WebPkiClientVerifier;
use rustls::sign::CertifiedKey;
use rustls::{DistinguishedName, RootCertStore, SupportedCipherSuite};
use rustls_pemfile::{certs, crls, pkcs8_private_keys, rsa_private_keys};
use webpki::{RevocationCheckDepth, UnknownStatusPolicy};
use crate::error::{self, rustls_result};
use crate::rslice::{rustls_slice_bytes, rustls_str};
use crate::{
arc_castable, box_castable, ffi_panic_boundary, free_arc, free_box, ref_castable,
set_arc_mut_ptr, set_boxed_mut_ptr, to_arc_const_ptr, to_boxed_mut_ptr,
try_clone_arc, try_mut_from_ptr, try_ref_from_ptr, try_slice, try_take,
};
use rustls_result::{AlreadyUsed, NullParameter};
/// An X.509 certificate, as used in rustls.
/// Corresponds to `CertificateDer` in the Rust pki-types API.
/// <https://docs.rs/rustls-pki-types/latest/rustls_pki_types/struct.CertificateDer.html>
pub struct rustls_certificate<'a> {
_private: [u8; 0],
_marker: PhantomData<&'a ()>,
}
impl<'a> crate::Castable for rustls_certificate<'a> {
type Ownership = crate::OwnershipRef;
type RustType = CertificateDer<'a>;
}
/// Get the DER data of the certificate itself.
/// The data is owned by the certificate and has the same lifetime.
#[no_mangle]
pub extern "C" fn rustls_certificate_get_der(
cert: *const rustls_certificate,
out_der_data: *mut *const u8,
out_der_len: *mut size_t,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let cert = match crate::try_from(cert) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
if out_der_data.is_null() || out_der_len.is_null() {
return NullParameter;
}
let der = cert.as_ref();
unsafe {
*out_der_data = der.as_ptr();
*out_der_len = der.len();
}
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// A cipher suite supported by rustls.
pub struct rustls_supported_ciphersuite {
_private: [u8; 0],
}
impl crate::Castable for rustls_supported_ciphersuite {
type Ownership = crate::OwnershipRef;
type RustType = SupportedCipherSuite;
}
impl rustls_supported_ciphersuite {
/// Return a 16-bit unsigned integer corresponding to this cipher suite's assignment from
/// <https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4>.
/// The bytes from the assignment are interpreted in network order.
#[no_mangle]
pub extern "C" fn rustls_supported_ciphersuite_get_suite(
supported_ciphersuite: *const rustls_supported_ciphersuite,
) -> u16 {
let supported_ciphersuite = match crate::try_from(supported_ciphersuite) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
u16::from(
match supported_ciphersuite {
rustls::SupportedCipherSuite::Tls12(sc) => &sc.common,
rustls::SupportedCipherSuite::Tls13(sc) => &sc.common,
}
.suite,
)
}
}
/// Returns the name of the ciphersuite as a `rustls_str`. If the provided
/// ciphersuite is invalid, the rustls_str will contain the empty string. The
/// lifetime of the `rustls_str` is the lifetime of the program, it does not
/// need to be freed.
#[no_mangle]
pub extern "C" fn rustls_supported_ciphersuite_get_name(
supported_ciphersuite: *const rustls_supported_ciphersuite,
) -> rustls_str<'static> {
let supported_ciphersuite = match crate::try_from(supported_ciphersuite) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let s = supported_ciphersuite.suite().as_str().unwrap_or("");
match rustls_str::try_from(s) {
Ok(s) => s,
Err(_) => rustls_str::from_str_unchecked(""),
}
}
/// Return the length of rustls' list of supported cipher suites.
#[no_mangle]
pub extern "C" fn rustls_all_ciphersuites_len() -> usize {
ALL_CIPHER_SUITES.len()
}
/// Get a pointer to a member of rustls' list of supported cipher suites. This will return non-NULL
/// for i < rustls_all_ciphersuites_len().
/// The returned pointer is valid for the lifetime of the program and may be used directly when
/// building a ClientConfig or ServerConfig.
#[no_mangle]
pub extern "C" fn rustls_all_ciphersuites_get_entry(
i: size_t,
) -> *const rustls_supported_ciphersuite {
match ALL_CIPHER_SUITES.get(i) {
Some(cs) => cs as *const SupportedCipherSuite as *const _,
None => null(),
}
}
/// Return the length of rustls' list of default cipher suites.
#[no_mangle]
pub extern "C" fn rustls_default_ciphersuites_len() -> usize {
DEFAULT_CIPHER_SUITES.len()
}
/// Get a pointer to a member of rustls' list of supported cipher suites. This will return non-NULL
/// for i < rustls_default_ciphersuites_len().
/// The returned pointer is valid for the lifetime of the program and may be used directly when
/// building a ClientConfig or ServerConfig.
#[no_mangle]
pub extern "C" fn rustls_default_ciphersuites_get_entry(
i: size_t,
) -> *const rustls_supported_ciphersuite {
match DEFAULT_CIPHER_SUITES.get(i) {
Some(cs) => cs as *const SupportedCipherSuite as *const _,
None => null(),
}
}
/// Rustls' list of supported cipher suites. This is an array of pointers, and
/// its length is given by `RUSTLS_ALL_CIPHER_SUITES_LEN`. The pointers will
/// always be valid. The contents and order of this array may change between
/// releases.
#[no_mangle]
pub static mut RUSTLS_ALL_CIPHER_SUITES: [*const rustls_supported_ciphersuite; 9] = [
&rustls::crypto::ring::cipher_suite::TLS13_AES_256_GCM_SHA384
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
as *const SupportedCipherSuite as *const _,
];
/// The length of the array `RUSTLS_ALL_CIPHER_SUITES`.
#[no_mangle]
pub static RUSTLS_ALL_CIPHER_SUITES_LEN: usize = unsafe {
RUSTLS_ALL_CIPHER_SUITES.len()
};
/// Rustls' list of default cipher suites. This is an array of pointers, and
/// its length is given by `RUSTLS_DEFAULT_CIPHER_SUITES_LEN`. The pointers
/// will always be valid. The contents and order of this array may change
/// between releases.
#[no_mangle]
pub static mut RUSTLS_DEFAULT_CIPHER_SUITES: [*const rustls_supported_ciphersuite; 9] = [
&rustls::crypto::ring::cipher_suite::TLS13_AES_256_GCM_SHA384
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
as *const SupportedCipherSuite as *const _,
&rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
as *const SupportedCipherSuite as *const _,
];
/// The length of the array `RUSTLS_DEFAULT_CIPHER_SUITES`.
#[no_mangle]
pub static RUSTLS_DEFAULT_CIPHER_SUITES_LEN: usize = unsafe {
RUSTLS_DEFAULT_CIPHER_SUITES.len()
};
/// The complete chain of certificates to send during a TLS handshake,
/// plus a private key that matches the end-entity (leaf) certificate.
/// Corresponds to `CertifiedKey` in the Rust API.
/// <https://docs.rs/rustls/latest/rustls/sign/struct.CertifiedKey.html>
pub struct rustls_certified_key {
_private: [u8; 0],
}
impl crate::Castable for rustls_certified_key {
type Ownership = crate::OwnershipArc;
type RustType = CertifiedKey;
}
impl rustls_certified_key {
/// Build a `rustls_certified_key` from a certificate chain and a private key.
/// `cert_chain` must point to a buffer of `cert_chain_len` bytes, containing
/// a series of PEM-encoded certificates, with the end-entity (leaf)
/// certificate first.
///
/// `private_key` must point to a buffer of `private_key_len` bytes, containing
/// a PEM-encoded private key in either PKCS#1 or PKCS#8 format.
///
/// On success, this writes a pointer to the newly created
/// `rustls_certified_key` in `certified_key_out`. That pointer must later
/// be freed with `rustls_certified_key_free` to avoid memory leaks. Note that
/// internally, this is an atomically reference-counted pointer, so even after
/// the original caller has called `rustls_certified_key_free`, other objects
/// may retain a pointer to the object. The memory will be freed when all
/// references are gone.
///
/// This function does not take ownership of any of its input pointers. It
/// parses the pointed-to data and makes a copy of the result. You may
/// free the cert_chain and private_key pointers after calling it.
///
/// Typically, you will build a `rustls_certified_key`, use it to create a
/// `rustls_server_config` (which increments the reference count), and then
/// immediately call `rustls_certified_key_free`. That leaves the
/// `rustls_server_config` in possession of the sole reference, so the
/// `rustls_certified_key`'s memory will automatically be released when
/// the `rustls_server_config` is freed.
#[no_mangle]
pub extern "C" fn rustls_certified_key_build(
cert_chain: *const u8,
cert_chain_len: size_t,
private_key: *const u8,
private_key_len: size_t,
certified_key_out: *mut *const rustls_certified_key,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let certified_key_out: &mut *const rustls_certified_key = unsafe {
match certified_key_out.as_mut() {
Some(c) => c,
None => return NullParameter,
}
};
let certified_key = match rustls_certified_key::certified_key_build(
cert_chain,
cert_chain_len,
private_key,
private_key_len,
) {
Ok(key) => Box::new(key),
Err(rr) => return rr,
};
let certified_key = Arc::into_raw(Arc::new(*certified_key)) as *const _;
*certified_key_out = certified_key;
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Return the i-th rustls_certificate in the rustls_certified_key. 0 gives the
/// end-entity certificate. 1 and higher give certificates from the chain.
/// Indexes higher than the last available certificate return NULL.
///
/// The returned certificate is valid until the rustls_certified_key is freed.
#[no_mangle]
pub extern "C" fn rustls_certified_key_get_certificate<'a>(
certified_key: *const rustls_certified_key,
i: size_t,
) -> *const rustls_certificate<'a> {
match ::std::panic::catch_unwind(|| {
let certified_key: &CertifiedKey = match crate::try_from(certified_key) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
match certified_key.cert.get(i) {
Some(cert) => cert as *const CertificateDer as *const _,
None => null(),
}
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Create a copy of the rustls_certified_key with the given OCSP response data
/// as DER encoded bytes. The OCSP response may be given as NULL to clear any
/// possibly present OCSP data from the cloned key.
/// The cloned key is independent from its original and needs to be freed
/// by the application.
#[no_mangle]
pub extern "C" fn rustls_certified_key_clone_with_ocsp(
certified_key: *const rustls_certified_key,
ocsp_response: *const rustls_slice_bytes,
cloned_key_out: *mut *const rustls_certified_key,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let cloned_key_out: &mut *const rustls_certified_key = unsafe {
match cloned_key_out.as_mut() {
Some(c) => c,
None => return NullParameter,
}
};
let certified_key: &CertifiedKey = match crate::try_from(certified_key) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let mut new_key = certified_key.clone();
if !ocsp_response.is_null() {
let ocsp_slice = unsafe { &*ocsp_response };
new_key
.ocsp = Some(
Vec::from(
if ocsp_slice.data.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe {
slice::from_raw_parts(
ocsp_slice.data,
ocsp_slice.len as usize,
)
}
},
),
);
} else {
new_key.ocsp = None;
}
*cloned_key_out = to_arc_const_ptr(new_key);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// "Free" a certified_key previously returned from
/// rustls_certified_key_build. Since certified_key is actually an
/// atomically reference-counted pointer, extant certified_key may still
/// hold an internal reference to the Rust object. However, C code must
/// consider this pointer unusable after "free"ing it.
/// Calling with NULL is fine. Must not be called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_certified_key_free(key: *const rustls_certified_key) {
match ::std::panic::catch_unwind(|| {
free_arc(key);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
fn certified_key_build(
cert_chain: *const u8,
cert_chain_len: size_t,
private_key: *const u8,
private_key_len: size_t,
) -> Result<CertifiedKey, rustls_result> {
let mut cert_chain: &[u8] = unsafe {
if cert_chain.is_null() {
return Err(NullParameter);
}
slice::from_raw_parts(cert_chain, cert_chain_len)
};
let private_key_der: &[u8] = unsafe {
if private_key.is_null() {
return Err(NullParameter);
}
slice::from_raw_parts(private_key, private_key_len)
};
let private_key: PrivateKeyDer = match pkcs8_private_keys(
&mut Cursor::new(private_key_der),
)
.next()
{
Some(Ok(p)) => p.into(),
Some(Err(_)) => return Err(rustls_result::PrivateKeyParseError),
None => {
let rsa_private_key: PrivateKeyDer = match rsa_private_keys(
&mut Cursor::new(private_key_der),
)
.next()
{
Some(Ok(p)) => p.into(),
_ => return Err(rustls_result::PrivateKeyParseError),
};
rsa_private_key
}
};
let signing_key = match rustls::crypto::ring::sign::any_supported_type(
&private_key,
) {
Ok(key) => key,
Err(_) => return Err(rustls_result::PrivateKeyParseError),
};
let parsed_chain: Result<Vec<CertificateDer>, _> = certs(&mut cert_chain)
.collect();
let parsed_chain = match parsed_chain {
Ok(v) => v,
Err(_) => return Err(rustls_result::CertificateParseError),
};
Ok(rustls::sign::CertifiedKey::new(parsed_chain, signing_key))
}
}
/// A `rustls_root_cert_store` being constructed.
///
/// A builder can be modified by adding trust anchor root certificates with
/// `rustls_root_cert_store_builder_add_pem`. Once you're done adding root certificates,
/// call `rustls_root_cert_store_builder_build` to turn it into a `rustls_root_cert_store`.
/// This object is not safe for concurrent mutation.
pub struct rustls_root_cert_store_builder {
_private: [u8; 0],
}
impl crate::Castable for rustls_root_cert_store_builder {
type Ownership = crate::OwnershipBox;
type RustType = Option<RootCertStoreBuilder>;
}
pub(crate) struct RootCertStoreBuilder {
roots: RootCertStore,
}
impl rustls_root_cert_store_builder {
/// Create a `rustls_root_cert_store_builder`.
///
/// Caller owns the memory and may free it with `rustls_root_cert_store_free`, regardless of
/// whether `rustls_root_cert_store_builder_build` was called.
///
/// If you wish to abandon the builder without calling `rustls_root_cert_store_builder_build`,
/// it must be freed with `rustls_root_cert_store_builder_free`.
#[no_mangle]
pub extern "C" fn rustls_root_cert_store_builder_new() -> *mut rustls_root_cert_store_builder {
match ::std::panic::catch_unwind(|| {
let store = rustls::RootCertStore::empty();
to_boxed_mut_ptr(
Some(RootCertStoreBuilder {
roots: store,
}),
)
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Add one or more certificates to the root cert store builder using PEM
/// encoded data.
///
/// When `strict` is true an error will return a `CertificateParseError`
/// result. So will an attempt to parse data that has zero certificates.
///
/// When `strict` is false, unparseable root certificates will be ignored.
/// This may be useful on systems that have syntactically invalid root
/// certificates.
#[no_mangle]
pub extern "C" fn rustls_root_cert_store_builder_add_pem(
builder: *mut rustls_root_cert_store_builder,
pem: *const u8,
pem_len: size_t,
strict: bool,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let certs_pem: &[u8] = if pem.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe { slice::from_raw_parts(pem, pem_len as usize) }
};
let builder: &mut Option<RootCertStoreBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let builder = match builder {
None => return AlreadyUsed,
Some(b) => b,
};
let certs_der: Result<Vec<CertificateDer>, _> = rustls_pemfile::certs(
&mut Cursor::new(certs_pem),
)
.collect();
let certs_der = match certs_der {
Ok(vv) => vv,
Err(_) => return rustls_result::CertificateParseError,
};
let mut new_store = RootCertStore::empty();
let (parsed, rejected) = new_store.add_parsable_certificates(certs_der);
if strict && (rejected > 0 || parsed == 0) {
return rustls_result::CertificateParseError;
}
builder.roots.roots.append(&mut new_store.roots);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Add one or more certificates to the root cert store builder using PEM
/// encoded data read from the named file.
///
/// When `strict` is true an error will return a `CertificateParseError`
/// result. So will an attempt to parse data that has zero certificates.
///
/// When `strict` is false, unparseable root certificates will be ignored.
/// This may be useful on systems that have syntactically invalid root
/// certificates.
#[no_mangle]
pub extern "C" fn rustls_root_cert_store_builder_load_roots_from_file(
builder: *mut rustls_root_cert_store_builder,
filename: *const c_char,
strict: bool,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let builder: &mut Option<RootCertStoreBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let builder = match builder {
None => return AlreadyUsed,
Some(b) => b,
};
let filename: &CStr = unsafe {
if filename.is_null() {
return rustls_result::NullParameter;
}
CStr::from_ptr(filename)
};
let filename: &[u8] = filename.to_bytes();
let filename: &str = match std::str::from_utf8(filename) {
Ok(s) => s,
Err(_) => return rustls_result::Io,
};
let filename: &OsStr = OsStr::new(filename);
let mut cafile = match File::open(filename) {
Ok(f) => f,
Err(_) => return rustls_result::Io,
};
let mut bufreader = BufReader::new(&mut cafile);
let certs: Result<Vec<CertificateDer>, _> = rustls_pemfile::certs(
&mut bufreader,
)
.collect();
let certs = match certs {
Ok(certs) => certs,
Err(_) => return rustls_result::Io,
};
let mut roots = RootCertStore::empty();
let (parsed, rejected) = roots.add_parsable_certificates(certs);
if strict && (rejected > 0 || parsed == 0) {
return rustls_result::CertificateParseError;
}
builder.roots.roots.append(&mut roots.roots);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Create a new `rustls_root_cert_store` from the builder.
///
/// The builder is consumed and cannot be used again, but must still be freed.
///
/// The root cert store can be used in several `rustls_web_pki_client_cert_verifier_builder_new`
/// instances and must be freed by the application when no longer needed. See the documentation of
/// `rustls_root_cert_store_free` for details about lifetime.
#[no_mangle]
pub extern "C" fn rustls_root_cert_store_builder_build(
builder: *mut rustls_root_cert_store_builder,
root_cert_store_out: *mut *const rustls_root_cert_store,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let builder: &mut Option<RootCertStoreBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let builder = match builder.take() {
None => {
return crate::rustls_result::AlreadyUsed;
}
Some(x) => x,
};
set_arc_mut_ptr(root_cert_store_out, builder.roots);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Free a `rustls_root_cert_store_builder` previously returned from
/// `rustls_root_cert_store_builder_new`. Calling with NULL is fine. Must not be
/// called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_root_cert_store_builder_free(
builder: *mut rustls_root_cert_store_builder,
) {
match ::std::panic::catch_unwind(|| {
free_box(builder);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
/// A root certificate store.
/// <https://docs.rs/rustls/latest/rustls/struct.RootCertStore.html>
pub struct rustls_root_cert_store {
_private: [u8; 0],
}
impl crate::Castable for rustls_root_cert_store {
type Ownership = crate::OwnershipArc;
type RustType = RootCertStore;
}
impl rustls_root_cert_store {
/// Free a rustls_root_cert_store previously returned from rustls_root_cert_store_builder_build.
/// Calling with NULL is fine. Must not be called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_root_cert_store_free(
store: *const rustls_root_cert_store,
) {
match ::std::panic::catch_unwind(|| {
free_arc(store);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
/// A built client certificate verifier that can be provided to a `rustls_server_config_builder`
/// with `rustls_server_config_builder_set_client_verifier`.
pub struct rustls_client_cert_verifier {
_private: [u8; 0],
}
impl crate::Castable for rustls_client_cert_verifier {
type Ownership = crate::OwnershipBox;
type RustType = Arc<dyn ClientCertVerifier>;
}
impl rustls_client_cert_verifier {
/// Free a `rustls_client_cert_verifier` previously returned from
/// `rustls_client_cert_verifier_builder_build`. Calling with NULL is fine. Must not be
/// called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_client_cert_verifier_free(
verifier: *mut rustls_client_cert_verifier,
) {
match ::std::panic::catch_unwind(|| {
free_box(verifier);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
pub(crate) struct ClientCertVerifierBuilder {
roots: Arc<RootCertStore>,
root_hint_subjects: Vec<DistinguishedName>,
crls: Vec<CertificateRevocationListDer<'static>>,
revocation_depth: RevocationCheckDepth,
revocation_policy: UnknownStatusPolicy,
allow_unauthenticated: bool,
}
/// A client certificate verifier being constructed. A builder can be modified by,
/// e.g. `rustls_web_pki_client_cert_verifier_builder_add_crl`. Once you're
/// done configuring settings, call `rustls_web_pki_client_cert_verifier_builder_build`
/// to turn it into a `rustls_client_cert_verifier`. This object is not safe
/// for concurrent mutation.
///
/// See <https://docs.rs/rustls/latest/rustls/server/struct.ClientCertVerifierBuilder.html>
/// for more information.
pub struct rustls_web_pki_client_cert_verifier_builder {
_private: [u8; 0],
}
impl crate::Castable for rustls_web_pki_client_cert_verifier_builder {
type Ownership = crate::OwnershipBox;
type RustType = Option<ClientCertVerifierBuilder>;
}
impl rustls_web_pki_client_cert_verifier_builder {
/// Create a `rustls_web_pki_client_cert_verifier_builder`. Caller owns the memory and may
/// eventually call `rustls_web_pki_client_cert_verifier_builder_free` to free it, whether or
/// not `rustls_web_pki_client_cert_verifier_builder_build` was called.
///
/// Without further modification the builder will produce a client certificate verifier that
/// will require a client present a client certificate that chains to one of the trust anchors
/// in the provided `rustls_root_cert_store`. The root cert store must not be empty.
///
/// Revocation checking will not be performed unless
/// `rustls_web_pki_client_cert_verifier_builder_add_crl` is used to add certificate revocation
/// lists (CRLs) to the builder. If CRLs are added, revocation checking will be performed
/// for the entire certificate chain unless
/// `rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation` is used. Unknown
/// revocation status for certificates considered for revocation status will be treated as
/// an error unless `rustls_web_pki_client_cert_verifier_allow_unknown_revocation_status` is
/// used.
///
/// Unauthenticated clients will not be permitted unless
/// `rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated` is used.
///
/// This copies the contents of the `rustls_root_cert_store`. It does not take
/// ownership of the pointed-to data.
#[no_mangle]
pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_new(
store: *const rustls_root_cert_store,
) -> *mut rustls_web_pki_client_cert_verifier_builder {
match ::std::panic::catch_unwind(|| {
let store = match crate::clone_arc(store) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let builder = ClientCertVerifierBuilder {
root_hint_subjects: store.subjects(),
roots: store,
crls: Vec::default(),
revocation_depth: RevocationCheckDepth::Chain,
revocation_policy: UnknownStatusPolicy::Deny,
allow_unauthenticated: false,
};
to_boxed_mut_ptr(Some(builder))
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Add one or more certificate revocation lists (CRLs) to the client certificate verifier
/// builder by reading the CRL content from the provided buffer of PEM encoded content.
///
/// By default revocation checking will be performed on the entire certificate chain. To only
/// check the revocation status of the end entity certificate, use
/// `rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation`.
///
/// This function returns an error if the provided buffer is not valid PEM encoded content.
#[no_mangle]
pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_add_crl(
builder: *mut rustls_web_pki_client_cert_verifier_builder,
crl_pem: *const u8,
crl_pem_len: size_t,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let client_verifier_builder = match client_verifier_builder {
None => return AlreadyUsed,
Some(v) => v,
};
let crl_pem: &[u8] = if crl_pem.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe { slice::from_raw_parts(crl_pem, crl_pem_len as usize) }
};
let crls_der: Result<Vec<CertificateRevocationListDer>, _> = crls(
&mut Cursor::new(crl_pem),
)
.collect();
let crls_der = match crls_der {
Ok(vv) => vv,
Err(_) => return rustls_result::CertificateRevocationListParseError,
};
if crls_der.is_empty() {
return rustls_result::CertificateRevocationListParseError;
}
client_verifier_builder.crls.extend(crls_der);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// When CRLs are provided with `rustls_web_pki_client_cert_verifier_builder_add_crl`, only
/// check the revocation status of end entity certificates, ignoring any intermediate certificates
/// in the chain.
#[no_mangle]
pub extern "C" fn rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation(
builder: *mut rustls_web_pki_client_cert_verifier_builder,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let client_verifier_builder = match client_verifier_builder {
None => return AlreadyUsed,
Some(v) => v,
};
client_verifier_builder
.revocation_depth = RevocationCheckDepth::EndEntity;
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// When CRLs are provided with `rustls_web_pki_client_cert_verifier_builder_add_crl`, and it
/// isn't possible to determine the revocation status of a considered certificate, do not treat
/// it as an error condition.
///
/// Overrides the default behavior where unknown revocation status is considered an error.
#[no_mangle]
pub extern "C" fn rustls_web_pki_client_cert_verifier_allow_unknown_revocation_status(
builder: *mut rustls_web_pki_client_cert_verifier_builder,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let client_verifier_builder = match client_verifier_builder {
None => return AlreadyUsed,
Some(v) => v,
};
client_verifier_builder.revocation_policy = UnknownStatusPolicy::Allow;
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Allow unauthenticated anonymous clients in addition to those that present a client
/// certificate that chains to one of the verifier's configured trust anchors.
#[no_mangle]
pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated(
builder: *mut rustls_web_pki_client_cert_verifier_builder,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let client_verifier_builder = match client_verifier_builder {
None => return AlreadyUsed,
Some(v) => v,
};
client_verifier_builder.allow_unauthenticated = true;
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Clear the list of trust anchor hint subjects.
///
/// By default, the client cert verifier will use the subjects provided by the root cert
/// store configured for client authentication. Calling this function will remove these
/// hint subjects, indicating the client should make a free choice of which certificate
/// to send.
#[no_mangle]
pub extern "C" fn rustls_web_pki_client_cert_verifier_clear_root_hint_subjects(
builder: *mut rustls_web_pki_client_cert_verifier_builder,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let client_verifier_builder = match client_verifier_builder {
None => return AlreadyUsed,
Some(v) => v,
};
client_verifier_builder.root_hint_subjects.clear();
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Add additional distinguished names to the list of trust anchor hint subjects.
///
/// By default, the client cert verifier will use the subjects provided by the root cert
/// store configured for client authentication. Calling this function will add to these
/// existing hint subjects. Calling this function with an empty `store` will have no
/// effect, use `rustls_web_pki_client_cert_verifier_clear_root_hint_subjects` to clear
/// the subject hints.
#[no_mangle]
pub extern "C" fn rustls_web_pki_client_cert_verifier_add_root_hint_subjects(
builder: *mut rustls_web_pki_client_cert_verifier_builder,
store: *const rustls_root_cert_store,
) -> rustls_result {
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let client_verifier_builder = match client_verifier_builder {
None => return AlreadyUsed,
Some(v) => v,
};
let store = match crate::clone_arc(store) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
client_verifier_builder.root_hint_subjects = store.subjects();
rustls_result::Ok
}
/// Create a new client certificate verifier from the builder.
///
/// The builder is consumed and cannot be used again, but must still be freed.
///
/// The verifier can be used in several `rustls_server_config` instances and must be
/// freed by the application when no longer needed. See the documentation of
/// `rustls_web_pki_client_cert_verifier_builder_free` for details about lifetime.
#[no_mangle]
pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_build(
builder: *mut rustls_web_pki_client_cert_verifier_builder,
verifier_out: *mut *mut rustls_client_cert_verifier,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
if verifier_out.is_null() {
return NullParameter;
}
let client_verifier_builder: &mut Option<ClientCertVerifierBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let client_verifier_builder = match client_verifier_builder.take() {
None => {
return crate::rustls_result::AlreadyUsed;
}
Some(x) => x,
};
let mut builder = WebPkiClientVerifier::builder_with_provider(
client_verifier_builder.roots,
rustls::crypto::ring::default_provider().into(),
)
.with_crls(client_verifier_builder.crls);
match client_verifier_builder.revocation_depth {
RevocationCheckDepth::EndEntity => {
builder = builder.only_check_end_entity_revocation();
}
RevocationCheckDepth::Chain => {}
}
match client_verifier_builder.revocation_policy {
UnknownStatusPolicy::Allow => {
builder = builder.allow_unknown_revocation_status();
}
UnknownStatusPolicy::Deny => {}
}
if client_verifier_builder.allow_unauthenticated {
builder = builder.allow_unauthenticated();
}
if client_verifier_builder.root_hint_subjects.is_empty() {
builder = builder.clear_root_hint_subjects();
} else {
builder = builder
.add_root_hint_subjects(
client_verifier_builder.root_hint_subjects,
);
}
let verifier = match builder.build() {
Ok(v) => v,
Err(e) => return error::map_verifier_builder_error(e),
};
set_boxed_mut_ptr(verifier_out, verifier);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Free a `rustls_client_cert_verifier_builder` previously returned from
/// `rustls_client_cert_verifier_builder_new`. Calling with NULL is fine. Must not be
/// called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_free(
builder: *mut rustls_web_pki_client_cert_verifier_builder,
) {
match ::std::panic::catch_unwind(|| {
free_box(builder);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
/// A server certificate verifier being constructed. A builder can be modified by,
/// e.g. `rustls_web_pki_server_cert_verifier_builder_add_crl`. Once you're
/// done configuring settings, call `rustls_web_pki_server_cert_verifier_builder_build`
/// to turn it into a `rustls_server_cert_verifier`. This object is not safe
/// for concurrent mutation.
///
/// See <https://docs.rs/rustls/latest/rustls/client/struct.ServerCertVerifierBuilder.html>
/// for more information.
pub(crate) struct rustls_web_pki_server_cert_verifier_builder {
_private: [u8; 0],
}
impl crate::Castable for rustls_web_pki_server_cert_verifier_builder {
type Ownership = crate::OwnershipBox;
type RustType = Option<ServerCertVerifierBuilder>;
}
pub(crate) struct ServerCertVerifierBuilder {
roots: Arc<RootCertStore>,
crls: Vec<CertificateRevocationListDer<'static>>,
revocation_depth: RevocationCheckDepth,
revocation_policy: UnknownStatusPolicy,
}
impl ServerCertVerifierBuilder {
/// Create a `rustls_web_pki_server_cert_verifier_builder`. Caller owns the memory and may
/// free it with `rustls_web_pki_server_cert_verifier_builder_free`, regardless of whether
/// `rustls_web_pki_server_cert_verifier_builder_build` was called.
///
/// Without further modification the builder will produce a server certificate verifier that
/// will require a server present a certificate that chains to one of the trust anchors
/// in the provided `rustls_root_cert_store`. The root cert store must not be empty.
///
/// Revocation checking will not be performed unless
/// `rustls_web_pki_server_cert_verifier_builder_add_crl` is used to add certificate revocation
/// lists (CRLs) to the builder. If CRLs are added, revocation checking will be performed
/// for the entire certificate chain unless
/// `rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation` is used. Unknown
/// revocation status for certificates considered for revocation status will be treated as
/// an error unless `rustls_web_pki_server_cert_verifier_allow_unknown_revocation_status` is
/// used.
///
/// This copies the contents of the `rustls_root_cert_store`. It does not take
/// ownership of the pointed-to data.
#[no_mangle]
pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_new(
store: *const rustls_root_cert_store,
) -> *mut rustls_web_pki_server_cert_verifier_builder {
match ::std::panic::catch_unwind(|| {
let store = match crate::clone_arc(store) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let builder = ServerCertVerifierBuilder {
roots: store,
crls: Vec::default(),
revocation_depth: RevocationCheckDepth::Chain,
revocation_policy: UnknownStatusPolicy::Deny,
};
to_boxed_mut_ptr(Some(builder))
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Add one or more certificate revocation lists (CRLs) to the server certificate verifier
/// builder by reading the CRL content from the provided buffer of PEM encoded content.
///
/// By default revocation checking will be performed on the entire certificate chain. To only
/// check the revocation status of the end entity certificate, use
/// `rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation`.
///
/// This function returns an error if the provided buffer is not valid PEM encoded content.
#[no_mangle]
pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_add_crl(
builder: *mut rustls_web_pki_server_cert_verifier_builder,
crl_pem: *const u8,
crl_pem_len: size_t,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let server_verifier_builder: &mut Option<ServerCertVerifierBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let server_verifier_builder = match server_verifier_builder {
None => return AlreadyUsed,
Some(v) => v,
};
let crl_pem: &[u8] = if crl_pem.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe { slice::from_raw_parts(crl_pem, crl_pem_len as usize) }
};
let crls_der: Result<Vec<CertificateRevocationListDer>, _> = crls(
&mut Cursor::new(crl_pem),
)
.collect();
let crls_der = match crls_der {
Ok(vv) => vv,
Err(_) => return rustls_result::CertificateRevocationListParseError,
};
if crls_der.is_empty() {
return rustls_result::CertificateRevocationListParseError;
}
server_verifier_builder.crls.extend(crls_der);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// When CRLs are provided with `rustls_web_pki_server_cert_verifier_builder_add_crl`, only
/// check the revocation status of end entity certificates, ignoring any intermediate certificates
/// in the chain.
#[no_mangle]
pub extern "C" fn rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation(
builder: *mut rustls_web_pki_server_cert_verifier_builder,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let server_verifier_builder: &mut Option<ServerCertVerifierBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let server_verifier_builder = match server_verifier_builder {
None => return AlreadyUsed,
Some(v) => v,
};
server_verifier_builder
.revocation_depth = RevocationCheckDepth::EndEntity;
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// When CRLs are provided with `rustls_web_pki_server_cert_verifier_builder_add_crl`, and it
/// isn't possible to determine the revocation status of a considered certificate, do not treat
/// it as an error condition.
///
/// Overrides the default behavior where unknown revocation status is considered an error.
#[no_mangle]
pub extern "C" fn rustls_web_pki_server_cert_verifier_allow_unknown_revocation_status(
builder: *mut rustls_web_pki_server_cert_verifier_builder,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let server_verifier_builder: &mut Option<ServerCertVerifierBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let server_verifier_builder = match server_verifier_builder {
None => return AlreadyUsed,
Some(v) => v,
};
server_verifier_builder.revocation_policy = UnknownStatusPolicy::Allow;
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Create a new server certificate verifier from the builder.
///
/// The builder is consumed and cannot be used again, but must still be freed.
///
/// The verifier can be used in several `rustls_client_config` instances and must be
/// freed by the application when no longer needed. See the documentation of
/// `rustls_web_pki_server_cert_verifier_builder_free` for details about lifetime.
#[no_mangle]
pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_build(
builder: *mut rustls_web_pki_server_cert_verifier_builder,
verifier_out: *mut *mut rustls_server_cert_verifier,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
if verifier_out.is_null() {
return NullParameter;
}
let server_verifier_builder: &mut Option<ServerCertVerifierBuilder> = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let server_verifier_builder = match server_verifier_builder.take() {
None => {
return crate::rustls_result::AlreadyUsed;
}
Some(x) => x,
};
let mut builder = WebPkiServerVerifier::builder_with_provider(
server_verifier_builder.roots,
rustls::crypto::ring::default_provider().into(),
)
.with_crls(server_verifier_builder.crls);
match server_verifier_builder.revocation_depth {
RevocationCheckDepth::EndEntity => {
builder = builder.only_check_end_entity_revocation();
}
RevocationCheckDepth::Chain => {}
}
match server_verifier_builder.revocation_policy {
UnknownStatusPolicy::Allow => {
builder = builder.allow_unknown_revocation_status();
}
UnknownStatusPolicy::Deny => {}
}
let verifier = match builder.build() {
Ok(v) => v,
Err(e) => return error::map_verifier_builder_error(e),
};
set_boxed_mut_ptr(verifier_out, verifier);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Free a `rustls_server_cert_verifier_builder` previously returned from
/// `rustls_server_cert_verifier_builder_new`. Calling with NULL is fine. Must not be
/// called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_free(
builder: *mut rustls_web_pki_server_cert_verifier_builder,
) {
match ::std::panic::catch_unwind(|| {
free_box(builder);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
/// A built server certificate verifier that can be provided to a `rustls_client_config_builder`
/// with `rustls_client_config_builder_set_server_verifier`.
pub struct rustls_server_cert_verifier {
_private: [u8; 0],
}
impl crate::Castable for rustls_server_cert_verifier {
type Ownership = crate::OwnershipBox;
type RustType = Arc<dyn ServerCertVerifier>;
}
impl rustls_server_cert_verifier {
/// Free a `rustls_server_cert_verifier` previously returned from
/// `rustls_server_cert_verifier_builder_build`. Calling with NULL is fine. Must not be
/// called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_server_cert_verifier_free(
verifier: *mut rustls_server_cert_verifier,
) {
match ::std::panic::catch_unwind(|| {
free_box(verifier);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
}
pub mod client {
use std::ffi::CStr;
use std::fmt::{Debug, Formatter};
use std::slice;
use std::sync::Arc;
use libc::{c_char, size_t};
use pki_types::{CertificateDer, UnixTime};
use rustls::client::danger::{
HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier,
};
use rustls::client::ResolvesClientCert;
use rustls::crypto::ring::ALL_CIPHER_SUITES;
use rustls::{
sign::CertifiedKey, CertificateError, ClientConfig, ClientConnection,
DigitallySignedStruct, Error, ProtocolVersion, SignatureScheme,
SupportedCipherSuite, WantsVerifier,
};
use crate::cipher::{
rustls_certified_key, rustls_server_cert_verifier, rustls_supported_ciphersuite,
};
use crate::connection::{rustls_connection, Connection};
use crate::error::rustls_result::{InvalidParameter, NullParameter};
use crate::error::{self, rustls_result};
use crate::rslice::NulByte;
use crate::rslice::{rustls_slice_bytes, rustls_slice_slice_bytes, rustls_str};
use crate::{
arc_castable, box_castable, ffi_panic_boundary, free_arc, free_box,
set_boxed_mut_ptr, to_arc_const_ptr, to_boxed_mut_ptr, try_box_from_ptr,
try_clone_arc, try_mut_from_ptr, try_ref_from_ptr, try_slice, userdata_get,
};
/// A client config being constructed. A builder can be modified by,
/// e.g. rustls_client_config_builder_load_roots_from_file. Once you're
/// done configuring settings, call rustls_client_config_builder_build
/// to turn it into a *rustls_client_config. This object is not safe
/// for concurrent mutation. Under the hood, it corresponds to a
/// `Box<ClientConfig>`.
/// <https://docs.rs/rustls/latest/rustls/struct.ConfigBuilder.html>
pub struct rustls_client_config_builder {
_private: [u8; 0],
}
impl crate::Castable for rustls_client_config_builder {
type Ownership = crate::OwnershipBox;
type RustType = ClientConfigBuilder;
}
pub(crate) struct ClientConfigBuilder {
base: rustls::ConfigBuilder<ClientConfig, WantsVerifier>,
verifier: Arc<dyn ServerCertVerifier>,
alpn_protocols: Vec<Vec<u8>>,
enable_sni: bool,
cert_resolver: Option<Arc<dyn rustls::client::ResolvesClientCert>>,
}
/// A client config that is done being constructed and is now read-only.
/// Under the hood, this object corresponds to an `Arc<ClientConfig>`.
/// <https://docs.rs/rustls/latest/rustls/struct.ClientConfig.html>
pub struct rustls_client_config {
_private: [u8; 0],
}
impl crate::Castable for rustls_client_config {
type Ownership = crate::OwnershipArc;
type RustType = ClientConfig;
}
struct NoneVerifier;
#[automatically_derived]
impl ::core::fmt::Debug for NoneVerifier {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f, "NoneVerifier")
}
}
impl ServerCertVerifier for NoneVerifier {
fn verify_server_cert(
&self,
_end_entity: &CertificateDer,
_intermediates: &[CertificateDer],
_server_name: &pki_types::ServerName<'_>,
_ocsp_response: &[u8],
_now: UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
Err(rustls::Error::InvalidCertificate(CertificateError::BadSignature))
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &CertificateDer,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
Err(Error::InvalidCertificate(CertificateError::BadSignature))
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &CertificateDer,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
Err(Error::InvalidCertificate(CertificateError::BadSignature))
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
rustls::crypto::ring::default_provider()
.signature_verification_algorithms
.supported_schemes()
}
}
impl rustls_client_config_builder {
/// Create a rustls_client_config_builder. Caller owns the memory and must
/// eventually call rustls_client_config_builder_build, then free the
/// resulting rustls_client_config.
/// This uses rustls safe default values
/// for the cipher suites, key exchange groups and protocol versions.
/// This starts out with no trusted roots.
/// Caller must add roots with rustls_client_config_builder_load_roots_from_file
/// or provide a custom verifier.
#[no_mangle]
pub extern "C" fn rustls_client_config_builder_new() -> *mut rustls_client_config_builder {
match ::std::panic::catch_unwind(|| {
let base = ClientConfig::builder_with_provider(
rustls::crypto::ring::default_provider().into(),
)
.with_safe_default_protocol_versions()
.unwrap();
let builder = ClientConfigBuilder {
base,
verifier: Arc::new(NoneVerifier),
cert_resolver: None,
alpn_protocols: ::alloc::vec::Vec::new(),
enable_sni: true,
};
to_boxed_mut_ptr(builder)
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Create a rustls_client_config_builder. Caller owns the memory and must
/// eventually call rustls_client_config_builder_build, then free the
/// resulting rustls_client_config. Specify cipher suites in preference
/// order; the `cipher_suites` parameter must point to an array containing
/// `len` pointers to `rustls_supported_ciphersuite` previously obtained
/// from `rustls_all_ciphersuites_get_entry()`, or to a provided array,
/// RUSTLS_DEFAULT_CIPHER_SUITES or RUSTLS_ALL_CIPHER_SUITES. Set the TLS
/// protocol versions to use when negotiating a TLS session.
///
/// `tls_version` is the version of the protocol, as defined in rfc8446,
/// ch. 4.2.1 and end of ch. 5.1. Some values are defined in
/// `rustls_tls_version` for convenience, and the arrays
/// RUSTLS_DEFAULT_VERSIONS or RUSTLS_ALL_VERSIONS can be used directly.
///
/// `versions` will only be used during the call and the application retains
/// ownership. `len` is the number of consecutive `uint16_t` pointed to by `versions`.
#[no_mangle]
pub extern "C" fn rustls_client_config_builder_new_custom(
cipher_suites: *const *const rustls_supported_ciphersuite,
cipher_suites_len: size_t,
tls_versions: *const u16,
tls_versions_len: size_t,
builder_out: *mut *mut rustls_client_config_builder,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
if builder_out.is_null() {
return NullParameter;
}
let cipher_suites: &[*const rustls_supported_ciphersuite] = if cipher_suites
.is_null()
{
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe {
slice::from_raw_parts(cipher_suites, cipher_suites_len as usize)
}
};
let mut cs_vec: Vec<SupportedCipherSuite> = Vec::new();
for &cs in cipher_suites.iter() {
let cs = match crate::try_from(cs) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
match ALL_CIPHER_SUITES.iter().find(|&acs| cs.eq(acs)) {
Some(scs) => cs_vec.push(*scs),
None => return InvalidParameter,
}
}
let tls_versions: &[u16] = if tls_versions.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe {
slice::from_raw_parts(tls_versions, tls_versions_len as usize)
}
};
let mut versions = ::alloc::vec::Vec::new();
for version_number in tls_versions {
let proto = ProtocolVersion::from(*version_number);
if proto == rustls::version::TLS12.version {
versions.push(&rustls::version::TLS12);
} else if proto == rustls::version::TLS13.version {
versions.push(&rustls::version::TLS13);
}
}
let provider = rustls::crypto::CryptoProvider {
cipher_suites: cs_vec,
..rustls::crypto::ring::default_provider()
};
let result = rustls::ClientConfig::builder_with_provider(provider.into())
.with_protocol_versions(&versions);
let base = match result {
Ok(new) => new,
Err(_) => return rustls_result::InvalidParameter,
};
let config_builder = ClientConfigBuilder {
base,
verifier: Arc::new(NoneVerifier),
cert_resolver: None,
alpn_protocols: ::alloc::vec::Vec::new(),
enable_sni: true,
};
set_boxed_mut_ptr(builder_out, config_builder);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
/// Input to a custom certificate verifier callback. See
/// rustls_client_config_builder_dangerous_set_certificate_verifier().
///
/// server_name can contain a hostname, an IPv4 address in textual form, or an
/// IPv6 address in textual form.
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct rustls_verify_server_cert_params<'a> {
pub end_entity_cert_der: rustls_slice_bytes<'a>,
pub intermediate_certs_der: &'a rustls_slice_slice_bytes<'a>,
pub server_name: rustls_str<'a>,
pub ocsp_response: rustls_slice_bytes<'a>,
}
/// User-provided input to a custom certificate verifier callback. See
/// rustls_client_config_builder_dangerous_set_certificate_verifier().
#[allow(non_camel_case_types)]
pub type rustls_verify_server_cert_user_data = *mut libc::c_void;
#[allow(non_camel_case_types)]
pub type rustls_verify_server_cert_callback = Option<
unsafe extern "C" fn(
userdata: rustls_verify_server_cert_user_data,
params: *const rustls_verify_server_cert_params,
) -> u32,
>;
type VerifyCallback = unsafe extern "C" fn(
userdata: rustls_verify_server_cert_user_data,
params: *const rustls_verify_server_cert_params,
) -> u32;
struct Verifier {
callback: VerifyCallback,
}
/// Safety: Verifier is Send because we don't allocate or deallocate any of its
/// fields.
unsafe impl Send for Verifier {}
/// Safety: Verifier is Sync if the C code that passes us a callback that
/// obeys the concurrency safety requirements documented in
/// rustls_client_config_builder_dangerous_set_certificate_verifier.
unsafe impl Sync for Verifier {}
impl ServerCertVerifier for Verifier {
fn verify_server_cert(
&self,
end_entity: &CertificateDer,
intermediates: &[CertificateDer],
server_name: &pki_types::ServerName<'_>,
ocsp_response: &[u8],
_now: UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
let cb = self.callback;
let server_name = server_name.to_str();
let server_name: rustls_str = match server_name.as_ref().try_into() {
Ok(r) => r,
Err(NulByte {}) => {
return Err(rustls::Error::General("NUL byte in SNI".to_string()));
}
};
let intermediates: Vec<_> = intermediates
.iter()
.map(|cert| cert.as_ref())
.collect();
let intermediates = rustls_slice_slice_bytes {
inner: &intermediates,
};
let params = rustls_verify_server_cert_params {
end_entity_cert_der: end_entity.as_ref().into(),
intermediate_certs_der: &intermediates,
server_name,
ocsp_response: ocsp_response.into(),
};
let userdata = userdata_get()
.map_err(|_| {
rustls::Error::General(
"internal error with thread-local storage".to_string(),
)
})?;
let result: u32 = unsafe { cb(userdata, &params) };
match rustls_result::from(result) {
rustls_result::Ok => Ok(ServerCertVerified::assertion()),
r => Err(error::cert_result_to_error(r)),
}
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
rustls::crypto::verify_tls12_signature(
message,
cert,
dss,
&rustls::crypto::ring::default_provider()
.signature_verification_algorithms,
)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
rustls::crypto::verify_tls13_signature(
message,
cert,
dss,
&rustls::crypto::ring::default_provider()
.signature_verification_algorithms,
)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
rustls::crypto::ring::default_provider()
.signature_verification_algorithms
.supported_schemes()
}
}
impl Debug for Verifier {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Verifier").finish()
}
}
impl rustls_client_config_builder {
/// Set a custom server certificate verifier.
///
/// The callback must not capture any of the pointers in its
/// rustls_verify_server_cert_params.
/// If `userdata` has been set with rustls_connection_set_userdata, it
/// will be passed to the callback. Otherwise the userdata param passed to
/// the callback will be NULL.
///
/// The callback must be safe to call on any thread at any time, including
/// multiple concurrent calls. So, for instance, if the callback mutates
/// userdata (or other shared state), it must use synchronization primitives
/// to make such mutation safe.
///
/// The callback receives certificate chain information as raw bytes.
/// Currently this library offers no functions to parse the certificates,
/// so you'll need to bring your own certificate parsing library
/// if you need to parse them.
///
/// If the custom verifier accepts the certificate, it should return
/// RUSTLS_RESULT_OK. Otherwise, it may return any other rustls_result error.
/// Feel free to use an appropriate error from the RUSTLS_RESULT_CERT_*
/// section.
///
/// <https://docs.rs/rustls/latest/rustls/client/struct.DangerousClientConfig.html#method.set_certificate_verifier>
#[no_mangle]
pub extern "C" fn rustls_client_config_builder_dangerous_set_certificate_verifier(
config_builder: *mut rustls_client_config_builder,
callback: rustls_verify_server_cert_callback,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let config_builder = match crate::try_from_mut(config_builder) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let callback: VerifyCallback = match callback {
Some(cb) => cb,
None => return rustls_result::InvalidParameter,
};
let verifier: Verifier = Verifier { callback };
config_builder.verifier = Arc::new(verifier);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Configure the server certificate verifier.
///
/// This increases the reference count of `verifier` and does not take ownership.
#[no_mangle]
pub extern "C" fn rustls_client_config_builder_set_server_verifier(
builder: *mut rustls_client_config_builder,
verifier: *const rustls_server_cert_verifier,
) {
match ::std::panic::catch_unwind(|| {
let builder: &mut ClientConfigBuilder = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let verifier = match crate::try_from(verifier) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
builder.verifier = verifier.clone();
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Set the ALPN protocol list to the given protocols. `protocols` must point
/// to a buffer of `rustls_slice_bytes` (built by the caller) with `len`
/// elements. Each element of the buffer must be a rustls_slice_bytes whose
/// data field points to a single ALPN protocol ID. Standard ALPN protocol
/// IDs are defined at
/// <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>.
///
/// This function makes a copy of the data in `protocols` and does not retain
/// any pointers, so the caller can free the pointed-to memory after calling.
///
/// <https://docs.rs/rustls/latest/rustls/client/struct.ClientConfig.html#structfield.alpn_protocols>
#[no_mangle]
pub extern "C" fn rustls_client_config_builder_set_alpn_protocols(
builder: *mut rustls_client_config_builder,
protocols: *const rustls_slice_bytes,
len: size_t,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let config: &mut ClientConfigBuilder = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let protocols: &[rustls_slice_bytes] = if protocols.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe { slice::from_raw_parts(protocols, len as usize) }
};
let mut vv: Vec<Vec<u8>> = Vec::with_capacity(protocols.len());
for p in protocols {
let v: &[u8] = if p.data.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe { slice::from_raw_parts(p.data, p.len as usize) }
};
vv.push(v.to_vec());
}
config.alpn_protocols = vv;
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Enable or disable SNI.
/// <https://docs.rs/rustls/latest/rustls/struct.ClientConfig.html#structfield.enable_sni>
#[no_mangle]
pub extern "C" fn rustls_client_config_builder_set_enable_sni(
config: *mut rustls_client_config_builder,
enable: bool,
) {
match ::std::panic::catch_unwind(|| {
let config: &mut ClientConfigBuilder = match crate::try_from_mut(
config,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
config.enable_sni = enable;
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Provide the configuration a list of certificates where the connection
/// will select the first one that is compatible with the server's signature
/// verification capabilities. Clients that want to support both ECDSA and
/// RSA certificates will want the ECSDA to go first in the list.
///
/// The built configuration will keep a reference to all certified keys
/// provided. The client may `rustls_certified_key_free()` afterwards
/// without the configuration losing them. The same certified key may also
/// be used in multiple configs.
///
/// EXPERIMENTAL: installing a client authentication callback will replace any
/// configured certified keys and vice versa.
#[no_mangle]
pub extern "C" fn rustls_client_config_builder_set_certified_key(
builder: *mut rustls_client_config_builder,
certified_keys: *const *const rustls_certified_key,
certified_keys_len: size_t,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let config: &mut ClientConfigBuilder = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let keys_ptrs: &[*const rustls_certified_key] = if certified_keys
.is_null()
{
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe {
slice::from_raw_parts(
certified_keys,
certified_keys_len as usize,
)
}
};
let mut keys: Vec<Arc<CertifiedKey>> = Vec::new();
for &key_ptr in keys_ptrs {
let certified_key: Arc<CertifiedKey> = match crate::clone_arc(
key_ptr,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
keys.push(certified_key);
}
config
.cert_resolver = Some(
Arc::new(ResolvesClientCertFromChoices {
keys,
}),
);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
/// Always send the same client certificate.
struct ResolvesClientCertFromChoices {
keys: Vec<Arc<CertifiedKey>>,
}
#[automatically_derived]
impl ::core::fmt::Debug for ResolvesClientCertFromChoices {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field1_finish(
f,
"ResolvesClientCertFromChoices",
"keys",
&&self.keys,
)
}
}
impl ResolvesClientCert for ResolvesClientCertFromChoices {
fn resolve(
&self,
_acceptable_issuers: &[&[u8]],
sig_schemes: &[rustls::SignatureScheme],
) -> Option<Arc<rustls::sign::CertifiedKey>> {
for key in self.keys.iter() {
if key.key.choose_scheme(sig_schemes).is_some() {
return Some(key.clone());
}
}
None
}
fn has_certs(&self) -> bool {
!self.keys.is_empty()
}
}
impl rustls_client_config_builder {
/// Turn a *rustls_client_config_builder (mutable) into a const *rustls_client_config
/// (read-only).
#[no_mangle]
pub extern "C" fn rustls_client_config_builder_build(
builder: *mut rustls_client_config_builder,
) -> *const rustls_client_config {
match ::std::panic::catch_unwind(|| {
let builder: Box<ClientConfigBuilder> = match crate::try_box_from(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let config = builder
.base
.dangerous()
.with_custom_certificate_verifier(builder.verifier);
let mut config = match builder.cert_resolver {
Some(r) => config.with_client_cert_resolver(r),
None => config.with_no_client_auth(),
};
config.alpn_protocols = builder.alpn_protocols;
config.enable_sni = builder.enable_sni;
to_arc_const_ptr(config)
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// "Free" a client_config_builder without building it into a rustls_client_config.
/// Normally builders are built into rustls_client_config via `rustls_client_config_builder_build`
/// and may not be free'd or otherwise used afterwards.
/// Use free only when the building of a config has to be aborted before a config
/// was created.
#[no_mangle]
pub extern "C" fn rustls_client_config_builder_free(
config: *mut rustls_client_config_builder,
) {
match ::std::panic::catch_unwind(|| {
free_box(config);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
impl rustls_client_config {
/// "Free" a rustls_client_config previously returned from
/// rustls_client_config_builder_build. Since rustls_client_config is actually an
/// atomically reference-counted pointer, extant client connections may still
/// hold an internal reference to the Rust object. However, C code must
/// consider this pointer unusable after "free"ing it.
/// Calling with NULL is fine. Must not be called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_client_config_free(
config: *const rustls_client_config,
) {
match ::std::panic::catch_unwind(|| {
free_arc(config);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Create a new rustls_connection containing a client connection and return
/// it in the output parameter `out`. If this returns an error code, the
/// memory pointed to by `conn_out` remains unchanged. If this returns a
/// non-error, the memory pointed to by `conn_out` is modified to point at a
/// valid rustls_connection. The caller now owns the rustls_connection and must
/// call `rustls_connection_free` when done with it.
///
/// The server_name parameter can contain a hostname or an IP address in
/// textual form (IPv4 or IPv6). This function will return an error if it
/// cannot be parsed as one of those types.
#[no_mangle]
pub extern "C" fn rustls_client_connection_new(
config: *const rustls_client_config,
server_name: *const c_char,
conn_out: *mut *mut rustls_connection,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
if conn_out.is_null() {
return NullParameter;
}
let server_name: &CStr = unsafe {
if server_name.is_null() {
return NullParameter;
}
CStr::from_ptr(server_name)
};
let config: Arc<ClientConfig> = match crate::clone_arc(config) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let server_name: &str = match server_name.to_str() {
Ok(s) => s,
Err(std::str::Utf8Error { .. }) => {
return rustls_result::InvalidDnsNameError;
}
};
let server_name: pki_types::ServerName = match server_name.try_into() {
Ok(sn) => sn,
Err(_) => return rustls_result::InvalidDnsNameError,
};
let client = ClientConnection::new(config, server_name).unwrap();
let c = Connection::from_client(client);
set_boxed_mut_ptr(conn_out, c);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
}
pub mod connection {
use std::io::{ErrorKind, Read, Write};
use std::{ffi::c_void, ptr::null};
use std::{ptr::null_mut, slice};
use libc::{size_t, EINVAL, EIO};
use pki_types::CertificateDer;
use rustls::crypto::ring::ALL_CIPHER_SUITES;
use rustls::{ClientConnection, ServerConnection, SupportedCipherSuite};
use crate::io::{
rustls_write_vectored_callback, CallbackReader, CallbackWriter, ReadCallback,
VectoredCallbackWriter, VectoredWriteCallback, WriteCallback,
};
use crate::log::{ensure_log_registered, rustls_log_callback};
use crate::{
box_castable, cipher::{rustls_certificate, rustls_supported_ciphersuite},
error::{map_error, rustls_io_result, rustls_result},
ffi_panic_boundary, free_box, io::{rustls_read_callback, rustls_write_callback},
try_callback, try_mut_from_ptr, try_ref_from_ptr, try_slice, userdata_push,
};
use rustls_result::NullParameter;
pub(crate) struct Connection {
conn: rustls::Connection,
userdata: *mut c_void,
log_callback: rustls_log_callback,
}
impl Connection {
pub(crate) fn from_client(conn: ClientConnection) -> Self {
Connection {
conn: conn.into(),
userdata: null_mut(),
log_callback: None,
}
}
pub(crate) fn from_server(conn: ServerConnection) -> Self {
Connection {
conn: conn.into(),
userdata: null_mut(),
log_callback: None,
}
}
#[allow(dead_code)]
pub(crate) fn as_client(&self) -> Option<&ClientConnection> {
match &self.conn {
rustls::Connection::Client(c) => Some(c),
_ => None,
}
}
pub(crate) fn as_server(&self) -> Option<&ServerConnection> {
match &self.conn {
rustls::Connection::Server(s) => Some(s),
_ => None,
}
}
#[allow(dead_code)]
pub(crate) fn as_client_mut(&mut self) -> Option<&mut ClientConnection> {
match &mut self.conn {
rustls::Connection::Client(c) => Some(c),
_ => None,
}
}
#[allow(dead_code)]
pub(crate) fn as_server_mut(&mut self) -> Option<&mut ServerConnection> {
match &mut self.conn {
rustls::Connection::Server(s) => Some(s),
_ => None,
}
}
}
impl std::ops::Deref for Connection {
type Target = rustls::Connection;
fn deref(&self) -> &Self::Target {
&self.conn
}
}
impl std::ops::DerefMut for Connection {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.conn
}
}
pub struct rustls_connection {
_private: [u8; 0],
}
impl crate::Castable for rustls_connection {
type Ownership = crate::OwnershipBox;
type RustType = Connection;
}
impl rustls_connection {
/// Set the userdata pointer associated with this connection. This will be passed
/// to any callbacks invoked by the connection, if you've set up callbacks in the config.
/// The pointed-to data must outlive the connection.
#[no_mangle]
pub extern "C" fn rustls_connection_set_userdata(
conn: *mut rustls_connection,
userdata: *mut c_void,
) {
let conn: &mut Connection = match crate::try_from_mut(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
conn.userdata = userdata;
}
/// Set the logging callback for this connection. The log callback will be invoked
/// with the userdata parameter previously set by rustls_connection_set_userdata, or
/// NULL if no userdata was set.
#[no_mangle]
pub extern "C" fn rustls_connection_set_log_callback(
conn: *mut rustls_connection,
cb: rustls_log_callback,
) {
let conn: &mut Connection = match crate::try_from_mut(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
ensure_log_registered();
conn.log_callback = cb;
}
/// Read some TLS bytes from the network into internal buffers. The actual network
/// I/O is performed by `callback`, which you provide. Rustls will invoke your
/// callback with a suitable buffer to store the read bytes into. You don't have
/// to fill it up, just fill with as many bytes as you get in one syscall.
/// The `userdata` parameter is passed through directly to `callback`. Note that
/// this is distinct from the `userdata` parameter set with
/// `rustls_connection_set_userdata`.
/// Returns 0 for success, or an errno value on error. Passes through return values
/// from callback. See rustls_read_callback for more details.
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.read_tls>
#[no_mangle]
pub extern "C" fn rustls_connection_read_tls(
conn: *mut rustls_connection,
callback: rustls_read_callback,
userdata: *mut c_void,
out_n: *mut size_t,
) -> rustls_io_result {
match ::std::panic::catch_unwind(|| {
let conn: &mut Connection = match crate::try_from_mut(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
if out_n.is_null() {
return rustls_io_result(EINVAL);
}
let callback: ReadCallback = match callback {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let mut reader = CallbackReader {
callback,
userdata,
};
let n_read: usize = match conn.read_tls(&mut reader) {
Ok(n) => n,
Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)),
};
unsafe {
*out_n = n_read;
}
rustls_io_result(0)
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Write some TLS bytes to the network. The actual network I/O is performed by
/// `callback`, which you provide. Rustls will invoke your callback with a
/// suitable buffer containing TLS bytes to send. You don't have to write them
/// all, just as many as you can in one syscall.
/// The `userdata` parameter is passed through directly to `callback`. Note that
/// this is distinct from the `userdata` parameter set with
/// `rustls_connection_set_userdata`.
/// Returns 0 for success, or an errno value on error. Passes through return values
/// from callback. See rustls_write_callback for more details.
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.write_tls>
#[no_mangle]
pub extern "C" fn rustls_connection_write_tls(
conn: *mut rustls_connection,
callback: rustls_write_callback,
userdata: *mut c_void,
out_n: *mut size_t,
) -> rustls_io_result {
match ::std::panic::catch_unwind(|| {
let conn: &mut Connection = match crate::try_from_mut(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
if out_n.is_null() {
return rustls_io_result(EINVAL);
}
let callback: WriteCallback = match callback {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let mut writer = CallbackWriter {
callback,
userdata,
};
let n_written: usize = match conn.write_tls(&mut writer) {
Ok(n) => n,
Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)),
};
unsafe {
*out_n = n_written;
}
rustls_io_result(0)
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Write all available TLS bytes to the network. The actual network I/O is performed by
/// `callback`, which you provide. Rustls will invoke your callback with an array
/// of rustls_slice_bytes, each containing a buffer with TLS bytes to send.
/// You don't have to write them all, just as many as you are willing.
/// The `userdata` parameter is passed through directly to `callback`. Note that
/// this is distinct from the `userdata` parameter set with
/// `rustls_connection_set_userdata`.
/// Returns 0 for success, or an errno value on error. Passes through return values
/// from callback. See rustls_write_callback for more details.
/// <https://docs.rs/rustls/latest/rustls/struct.Writer.html#method.write_vectored>
#[no_mangle]
pub extern "C" fn rustls_connection_write_tls_vectored(
conn: *mut rustls_connection,
callback: rustls_write_vectored_callback,
userdata: *mut c_void,
out_n: *mut size_t,
) -> rustls_io_result {
match ::std::panic::catch_unwind(|| {
let conn: &mut Connection = match crate::try_from_mut(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
if out_n.is_null() {
return rustls_io_result(EINVAL);
}
let callback: VectoredWriteCallback = match callback {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let mut writer = VectoredCallbackWriter {
callback,
userdata,
};
let n_written: usize = match conn.write_tls(&mut writer) {
Ok(n) => n,
Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)),
};
unsafe {
*out_n = n_written;
}
rustls_io_result(0)
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Decrypt any available ciphertext from the internal buffer and put it
/// into the internal plaintext buffer, potentially making bytes available
/// for rustls_connection_read().
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.process_new_packets>
#[no_mangle]
pub extern "C" fn rustls_connection_process_new_packets(
conn: *mut rustls_connection,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let conn: &mut Connection = match crate::try_from_mut(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let guard = match userdata_push(conn.userdata, conn.log_callback) {
Ok(g) => g,
Err(_) => return rustls_result::Panic,
};
let result = match conn.process_new_packets() {
Ok(_) => rustls_result::Ok,
Err(e) => map_error(e),
};
match guard.try_drop() {
Ok(()) => result,
Err(_) => rustls_result::Panic,
}
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// <https://docs.rs/rustls/latest/rustls/struct.CommonState.html#method.wants_read>
#[no_mangle]
pub extern "C" fn rustls_connection_wants_read(
conn: *const rustls_connection,
) -> bool {
match ::std::panic::catch_unwind(|| {
let conn: &Connection = match crate::try_from(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
conn.wants_read()
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// <https://docs.rs/rustls/latest/rustls/struct.CommonState.html#method.wants_write>
#[no_mangle]
pub extern "C" fn rustls_connection_wants_write(
conn: *const rustls_connection,
) -> bool {
match ::std::panic::catch_unwind(|| {
let conn: &Connection = match crate::try_from(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
conn.wants_write()
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// <https://docs.rs/rustls/latest/rustls/struct.CommonState.html#method.is_handshaking>
#[no_mangle]
pub extern "C" fn rustls_connection_is_handshaking(
conn: *const rustls_connection,
) -> bool {
match ::std::panic::catch_unwind(|| {
let conn: &Connection = match crate::try_from(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
conn.is_handshaking()
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Sets a limit on the internal buffers used to buffer unsent plaintext (prior
/// to completing the TLS handshake) and unsent TLS records. By default, there
/// is no limit. The limit can be set at any time, even if the current buffer
/// use is higher.
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.set_buffer_limit>
#[no_mangle]
pub extern "C" fn rustls_connection_set_buffer_limit(
conn: *mut rustls_connection,
n: usize,
) {
match ::std::panic::catch_unwind(|| {
let conn: &mut Connection = match crate::try_from_mut(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
conn.set_buffer_limit(Some(n));
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Queues a close_notify fatal alert to be sent in the next write_tls call.
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.send_close_notify>
#[no_mangle]
pub extern "C" fn rustls_connection_send_close_notify(
conn: *mut rustls_connection,
) {
match ::std::panic::catch_unwind(|| {
let conn: &mut Connection = match crate::try_from_mut(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
conn.send_close_notify();
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Return the i-th certificate provided by the peer.
/// Index 0 is the end entity certificate. Higher indexes are certificates
/// in the chain. Requesting an index higher than what is available returns
/// NULL.
/// The returned pointer is valid until the next mutating function call
/// affecting the connection. A mutating function call is one where the
/// first argument has type `struct rustls_connection *` (as opposed to
/// `const struct rustls_connection *`).
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.peer_certificates>
#[no_mangle]
pub extern "C" fn rustls_connection_get_peer_certificate<'a>(
conn: *const rustls_connection,
i: size_t,
) -> *const rustls_certificate<'a> {
match ::std::panic::catch_unwind(|| {
let conn: &Connection = match crate::try_from(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
match conn.peer_certificates().and_then(|c| c.get(i)) {
Some(cert) => cert as *const CertificateDer as *const _,
None => null(),
}
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Get the ALPN protocol that was negotiated, if any. Stores a pointer to a
/// borrowed buffer of bytes, and that buffer's len, in the output parameters.
/// The borrow lives as long as the connection.
/// If the connection is still handshaking, or no ALPN protocol was negotiated,
/// stores NULL and 0 in the output parameters.
/// The provided pointer is valid until the next mutating function call
/// affecting the connection. A mutating function call is one where the
/// first argument has type `struct rustls_connection *` (as opposed to
/// `const struct rustls_connection *`).
/// <https://www.iana.org/assignments/tls-parameters/>
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.alpn_protocol>
#[no_mangle]
pub extern "C" fn rustls_connection_get_alpn_protocol(
conn: *const rustls_connection,
protocol_out: *mut *const u8,
protocol_out_len: *mut usize,
) {
match ::std::panic::catch_unwind(|| {
let conn: &Connection = match crate::try_from(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
if protocol_out.is_null() || protocol_out_len.is_null() {
return;
}
match conn.alpn_protocol() {
Some(p) => {
unsafe {
*protocol_out = p.as_ptr();
*protocol_out_len = p.len();
}
}
None => {
unsafe {
*protocol_out = null();
*protocol_out_len = 0;
}
}
}
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Return the TLS protocol version that has been negotiated. Before this
/// has been decided during the handshake, this will return 0. Otherwise,
/// the u16 version number as defined in the relevant RFC is returned.
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.protocol_version>
/// <https://docs.rs/rustls/latest/rustls/internal/msgs/enums/enum.ProtocolVersion.html>
#[no_mangle]
pub extern "C" fn rustls_connection_get_protocol_version(
conn: *const rustls_connection,
) -> u16 {
match ::std::panic::catch_unwind(|| {
let conn: &Connection = match crate::try_from(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
match conn.protocol_version() {
Some(p) => u16::from(p),
_ => 0,
}
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Retrieves the cipher suite agreed with the peer.
/// This returns NULL until the ciphersuite is agreed.
/// The returned pointer lives as long as the program.
/// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.negotiated_cipher_suite>
#[no_mangle]
pub extern "C" fn rustls_connection_get_negotiated_ciphersuite(
conn: *const rustls_connection,
) -> *const rustls_supported_ciphersuite {
match ::std::panic::catch_unwind(|| {
let conn: &Connection = match crate::try_from(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let negotiated = match conn.negotiated_cipher_suite() {
Some(cs) => cs,
None => return null(),
};
for cs in ALL_CIPHER_SUITES {
let cs: &'static SupportedCipherSuite = cs;
if negotiated == *cs {
return cs as *const SupportedCipherSuite as *const _;
}
}
null()
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Write up to `count` plaintext bytes from `buf` into the `rustls_connection`.
/// This will increase the number of output bytes available to
/// `rustls_connection_write_tls`.
/// On success, store the number of bytes actually written in *out_n
/// (this may be less than `count`).
/// <https://docs.rs/rustls/latest/rustls/struct.Writer.html#method.write>
#[no_mangle]
pub extern "C" fn rustls_connection_write(
conn: *mut rustls_connection,
buf: *const u8,
count: size_t,
out_n: *mut size_t,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let conn: &mut Connection = match crate::try_from_mut(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let write_buf: &[u8] = if buf.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe { slice::from_raw_parts(buf, count as usize) }
};
if out_n.is_null() {
return NullParameter;
}
let n_written: usize = match conn.writer().write(write_buf) {
Ok(n) => n,
Err(_) => return rustls_result::Io,
};
unsafe {
*out_n = n_written;
}
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Read up to `count` plaintext bytes from the `rustls_connection` into `buf`.
/// On success, store the number of bytes read in *out_n (this may be less
/// than `count`). A success with *out_n set to 0 means "all bytes currently
/// available have been read, but more bytes may become available after
/// subsequent calls to rustls_connection_read_tls and
/// rustls_connection_process_new_packets."
///
/// Subtle note: Even though this function only writes to `buf` and does not
/// read from it, the memory in `buf` must be initialized before the call (for
/// Rust-internal reasons). Initializing a buffer once and then using it
/// multiple times without zeroizing before each call is fine.
/// <https://docs.rs/rustls/latest/rustls/struct.Reader.html#method.read>
#[no_mangle]
pub extern "C" fn rustls_connection_read(
conn: *mut rustls_connection,
buf: *mut u8,
count: size_t,
out_n: *mut size_t,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let conn: &mut Connection = match crate::try_from_mut(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
if buf.is_null() {
return NullParameter;
}
if out_n.is_null() {
return NullParameter;
}
let read_buf: &mut [u8] = unsafe {
slice::from_raw_parts_mut(buf, count)
};
let n_read: usize = match conn.reader().read(read_buf) {
Ok(n) => n,
Err(e) if e.kind() == ErrorKind::UnexpectedEof => {
return rustls_result::UnexpectedEof;
}
Err(e) if e.kind() == ErrorKind::WouldBlock => {
return rustls_result::PlaintextEmpty;
}
Err(_) => return rustls_result::Io,
};
unsafe {
*out_n = n_read;
}
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Free a rustls_connection. Calling with NULL is fine.
/// Must not be called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_connection_free(conn: *mut rustls_connection) {
match ::std::panic::catch_unwind(|| {
free_box(conn);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
}
pub mod enums {
#[repr(C)]
#[allow(dead_code)]
/// Definitions of known TLS protocol versions.
pub enum rustls_tls_version {
Sslv2 = 0x0200,
Sslv3 = 0x0300,
Tlsv1_0 = 0x0301,
Tlsv1_1 = 0x0302,
Tlsv1_2 = 0x0303,
Tlsv1_3 = 0x0304,
}
/// Rustls' list of supported protocol versions. The length of the array is
/// given by `RUSTLS_ALL_VERSIONS_LEN`.
#[no_mangle]
pub static RUSTLS_ALL_VERSIONS: [u16; 2] = [
rustls_tls_version::Tlsv1_3 as u16,
rustls_tls_version::Tlsv1_2 as u16,
];
/// The length of the array `RUSTLS_ALL_VERSIONS`.
#[no_mangle]
pub static RUSTLS_ALL_VERSIONS_LEN: usize = RUSTLS_ALL_VERSIONS.len();
/// Rustls' default list of protocol versions. The length of the array is
/// given by `RUSTLS_DEFAULT_VERSIONS_LEN`.
#[no_mangle]
pub static RUSTLS_DEFAULT_VERSIONS: [u16; 2] = [
rustls_tls_version::Tlsv1_3 as u16,
rustls_tls_version::Tlsv1_2 as u16,
];
/// The length of the array `RUSTLS_DEFAULT_VERSIONS`.
#[no_mangle]
pub static RUSTLS_DEFAULT_VERSIONS_LEN: usize = RUSTLS_DEFAULT_VERSIONS.len();
}
mod error {
use std::cmp::min;
use std::fmt::Display;
use std::sync::Arc;
use crate::ffi_panic_boundary;
use libc::{c_char, c_uint, size_t};
use rustls::server::VerifierBuilderError;
use rustls::{CertRevocationListError, CertificateError, Error, InvalidMessage};
/// A return value for a function that may return either success (0) or a
/// non-zero value representing an error. The values should match socket
/// error numbers for your operating system - for example, the integers for
/// ETIMEDOUT, EAGAIN, or similar.
#[repr(transparent)]
pub struct rustls_io_result(pub libc::c_int);
#[automatically_derived]
impl ::core::fmt::Debug for rustls_io_result {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(
f,
"rustls_io_result",
&&self.0,
)
}
}
#[automatically_derived]
impl ::core::clone::Clone for rustls_io_result {
#[inline]
fn clone(&self) -> rustls_io_result {
let _: ::core::clone::AssertParamIsClone<libc::c_int>;
*self
}
}
#[automatically_derived]
impl ::core::marker::Copy for rustls_io_result {}
#[automatically_derived]
impl ::core::marker::StructuralPartialEq for rustls_io_result {}
#[automatically_derived]
impl ::core::cmp::PartialEq for rustls_io_result {
#[inline]
fn eq(&self, other: &rustls_io_result) -> bool {
self.0 == other.0
}
}
#[automatically_derived]
impl ::core::cmp::Eq for rustls_io_result {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {
let _: ::core::cmp::AssertParamIsEq<libc::c_int>;
}
}
#[allow(dead_code)]
#[repr(u32)]
pub enum rustls_result {
Ok = 7000,
Io = 7001,
NullParameter = 7002,
InvalidDnsNameError = 7003,
Panic = 7004,
CertificateParseError = 7005,
PrivateKeyParseError = 7006,
InsufficientSize = 7007,
NotFound = 7008,
InvalidParameter = 7009,
UnexpectedEof = 7010,
PlaintextEmpty = 7011,
AcceptorNotReady = 7012,
AlreadyUsed = 7013,
CertificateRevocationListParseError = 7014,
NoCertificatesPresented = 7101,
DecryptError = 7102,
FailedToGetCurrentTime = 7103,
FailedToGetRandomBytes = 7113,
HandshakeNotComplete = 7104,
PeerSentOversizedRecord = 7105,
NoApplicationProtocol = 7106,
BadMaxFragmentSize = 7114,
UnsupportedNameType = 7115,
EncryptError = 7116,
CertEncodingBad = 7121,
CertExpired = 7122,
CertNotYetValid = 7123,
CertRevoked = 7124,
CertUnhandledCriticalExtension = 7125,
CertUnknownIssuer = 7126,
CertBadSignature = 7127,
CertNotValidForName = 7128,
CertInvalidPurpose = 7129,
CertApplicationVerificationFailure = 7130,
CertOtherError = 7131,
MessageHandshakePayloadTooLarge = 7133,
MessageInvalidCcs = 7134,
MessageInvalidContentType = 7135,
MessageInvalidCertStatusType = 7136,
MessageInvalidCertRequest = 7137,
MessageInvalidDhParams = 7138,
MessageInvalidEmptyPayload = 7139,
MessageInvalidKeyUpdate = 7140,
MessageInvalidServerName = 7141,
MessageTooLarge = 7142,
MessageTooShort = 7143,
MessageMissingData = 7144,
MessageMissingKeyExchange = 7145,
MessageNoSignatureSchemes = 7146,
MessageTrailingData = 7147,
MessageUnexpectedMessage = 7148,
MessageUnknownProtocolVersion = 7149,
MessageUnsupportedCompression = 7150,
MessageUnsupportedCurveType = 7151,
MessageUnsupportedKeyExchangeAlgorithm = 7152,
MessageInvalidOther = 7153,
PeerIncompatibleError = 7107,
PeerMisbehavedError = 7108,
InappropriateMessage = 7109,
InappropriateHandshakeMessage = 7110,
General = 7112,
AlertCloseNotify = 7200,
AlertUnexpectedMessage = 7201,
AlertBadRecordMac = 7202,
AlertDecryptionFailed = 7203,
AlertRecordOverflow = 7204,
AlertDecompressionFailure = 7205,
AlertHandshakeFailure = 7206,
AlertNoCertificate = 7207,
AlertBadCertificate = 7208,
AlertUnsupportedCertificate = 7209,
AlertCertificateRevoked = 7210,
AlertCertificateExpired = 7211,
AlertCertificateUnknown = 7212,
AlertIllegalParameter = 7213,
AlertUnknownCA = 7214,
AlertAccessDenied = 7215,
AlertDecodeError = 7216,
AlertDecryptError = 7217,
AlertExportRestriction = 7218,
AlertProtocolVersion = 7219,
AlertInsufficientSecurity = 7220,
AlertInternalError = 7221,
AlertInappropriateFallback = 7222,
AlertUserCanceled = 7223,
AlertNoRenegotiation = 7224,
AlertMissingExtension = 7225,
AlertUnsupportedExtension = 7226,
AlertCertificateUnobtainable = 7227,
AlertUnrecognisedName = 7228,
AlertBadCertificateStatusResponse = 7229,
AlertBadCertificateHashValue = 7230,
AlertUnknownPSKIdentity = 7231,
AlertCertificateRequired = 7232,
AlertNoApplicationProtocol = 7233,
AlertUnknown = 7234,
CertRevocationListBadSignature = 7400,
CertRevocationListInvalidCrlNumber = 7401,
CertRevocationListInvalidRevokedCertSerialNumber = 7402,
CertRevocationListIssuerInvalidForCrl = 7403,
CertRevocationListOtherError = 7404,
CertRevocationListParseError = 7405,
CertRevocationListUnsupportedCrlVersion = 7406,
CertRevocationListUnsupportedCriticalExtension = 7407,
CertRevocationListUnsupportedDeltaCrl = 7408,
CertRevocationListUnsupportedIndirectCrl = 7409,
CertRevocationListUnsupportedRevocationReason = 7410,
ClientCertVerifierBuilderNoRootAnchors = 7500,
}
#[automatically_derived]
#[allow(dead_code)]
impl ::core::fmt::Debug for rustls_result {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(
f,
match self {
rustls_result::Ok => "Ok",
rustls_result::Io => "Io",
rustls_result::NullParameter => "NullParameter",
rustls_result::InvalidDnsNameError => "InvalidDnsNameError",
rustls_result::Panic => "Panic",
rustls_result::CertificateParseError => "CertificateParseError",
rustls_result::PrivateKeyParseError => "PrivateKeyParseError",
rustls_result::InsufficientSize => "InsufficientSize",
rustls_result::NotFound => "NotFound",
rustls_result::InvalidParameter => "InvalidParameter",
rustls_result::UnexpectedEof => "UnexpectedEof",
rustls_result::PlaintextEmpty => "PlaintextEmpty",
rustls_result::AcceptorNotReady => "AcceptorNotReady",
rustls_result::AlreadyUsed => "AlreadyUsed",
rustls_result::CertificateRevocationListParseError => {
"CertificateRevocationListParseError"
}
rustls_result::NoCertificatesPresented => "NoCertificatesPresented",
rustls_result::DecryptError => "DecryptError",
rustls_result::FailedToGetCurrentTime => "FailedToGetCurrentTime",
rustls_result::FailedToGetRandomBytes => "FailedToGetRandomBytes",
rustls_result::HandshakeNotComplete => "HandshakeNotComplete",
rustls_result::PeerSentOversizedRecord => "PeerSentOversizedRecord",
rustls_result::NoApplicationProtocol => "NoApplicationProtocol",
rustls_result::BadMaxFragmentSize => "BadMaxFragmentSize",
rustls_result::UnsupportedNameType => "UnsupportedNameType",
rustls_result::EncryptError => "EncryptError",
rustls_result::CertEncodingBad => "CertEncodingBad",
rustls_result::CertExpired => "CertExpired",
rustls_result::CertNotYetValid => "CertNotYetValid",
rustls_result::CertRevoked => "CertRevoked",
rustls_result::CertUnhandledCriticalExtension => {
"CertUnhandledCriticalExtension"
}
rustls_result::CertUnknownIssuer => "CertUnknownIssuer",
rustls_result::CertBadSignature => "CertBadSignature",
rustls_result::CertNotValidForName => "CertNotValidForName",
rustls_result::CertInvalidPurpose => "CertInvalidPurpose",
rustls_result::CertApplicationVerificationFailure => {
"CertApplicationVerificationFailure"
}
rustls_result::CertOtherError => "CertOtherError",
rustls_result::MessageHandshakePayloadTooLarge => {
"MessageHandshakePayloadTooLarge"
}
rustls_result::MessageInvalidCcs => "MessageInvalidCcs",
rustls_result::MessageInvalidContentType => {
"MessageInvalidContentType"
}
rustls_result::MessageInvalidCertStatusType => {
"MessageInvalidCertStatusType"
}
rustls_result::MessageInvalidCertRequest => {
"MessageInvalidCertRequest"
}
rustls_result::MessageInvalidDhParams => "MessageInvalidDhParams",
rustls_result::MessageInvalidEmptyPayload => {
"MessageInvalidEmptyPayload"
}
rustls_result::MessageInvalidKeyUpdate => "MessageInvalidKeyUpdate",
rustls_result::MessageInvalidServerName => "MessageInvalidServerName",
rustls_result::MessageTooLarge => "MessageTooLarge",
rustls_result::MessageTooShort => "MessageTooShort",
rustls_result::MessageMissingData => "MessageMissingData",
rustls_result::MessageMissingKeyExchange => {
"MessageMissingKeyExchange"
}
rustls_result::MessageNoSignatureSchemes => {
"MessageNoSignatureSchemes"
}
rustls_result::MessageTrailingData => "MessageTrailingData",
rustls_result::MessageUnexpectedMessage => "MessageUnexpectedMessage",
rustls_result::MessageUnknownProtocolVersion => {
"MessageUnknownProtocolVersion"
}
rustls_result::MessageUnsupportedCompression => {
"MessageUnsupportedCompression"
}
rustls_result::MessageUnsupportedCurveType => {
"MessageUnsupportedCurveType"
}
rustls_result::MessageUnsupportedKeyExchangeAlgorithm => {
"MessageUnsupportedKeyExchangeAlgorithm"
}
rustls_result::MessageInvalidOther => "MessageInvalidOther",
rustls_result::PeerIncompatibleError => "PeerIncompatibleError",
rustls_result::PeerMisbehavedError => "PeerMisbehavedError",
rustls_result::InappropriateMessage => "InappropriateMessage",
rustls_result::InappropriateHandshakeMessage => {
"InappropriateHandshakeMessage"
}
rustls_result::General => "General",
rustls_result::AlertCloseNotify => "AlertCloseNotify",
rustls_result::AlertUnexpectedMessage => "AlertUnexpectedMessage",
rustls_result::AlertBadRecordMac => "AlertBadRecordMac",
rustls_result::AlertDecryptionFailed => "AlertDecryptionFailed",
rustls_result::AlertRecordOverflow => "AlertRecordOverflow",
rustls_result::AlertDecompressionFailure => {
"AlertDecompressionFailure"
}
rustls_result::AlertHandshakeFailure => "AlertHandshakeFailure",
rustls_result::AlertNoCertificate => "AlertNoCertificate",
rustls_result::AlertBadCertificate => "AlertBadCertificate",
rustls_result::AlertUnsupportedCertificate => {
"AlertUnsupportedCertificate"
}
rustls_result::AlertCertificateRevoked => "AlertCertificateRevoked",
rustls_result::AlertCertificateExpired => "AlertCertificateExpired",
rustls_result::AlertCertificateUnknown => "AlertCertificateUnknown",
rustls_result::AlertIllegalParameter => "AlertIllegalParameter",
rustls_result::AlertUnknownCA => "AlertUnknownCA",
rustls_result::AlertAccessDenied => "AlertAccessDenied",
rustls_result::AlertDecodeError => "AlertDecodeError",
rustls_result::AlertDecryptError => "AlertDecryptError",
rustls_result::AlertExportRestriction => "AlertExportRestriction",
rustls_result::AlertProtocolVersion => "AlertProtocolVersion",
rustls_result::AlertInsufficientSecurity => {
"AlertInsufficientSecurity"
}
rustls_result::AlertInternalError => "AlertInternalError",
rustls_result::AlertInappropriateFallback => {
"AlertInappropriateFallback"
}
rustls_result::AlertUserCanceled => "AlertUserCanceled",
rustls_result::AlertNoRenegotiation => "AlertNoRenegotiation",
rustls_result::AlertMissingExtension => "AlertMissingExtension",
rustls_result::AlertUnsupportedExtension => {
"AlertUnsupportedExtension"
}
rustls_result::AlertCertificateUnobtainable => {
"AlertCertificateUnobtainable"
}
rustls_result::AlertUnrecognisedName => "AlertUnrecognisedName",
rustls_result::AlertBadCertificateStatusResponse => {
"AlertBadCertificateStatusResponse"
}
rustls_result::AlertBadCertificateHashValue => {
"AlertBadCertificateHashValue"
}
rustls_result::AlertUnknownPSKIdentity => "AlertUnknownPSKIdentity",
rustls_result::AlertCertificateRequired => "AlertCertificateRequired",
rustls_result::AlertNoApplicationProtocol => {
"AlertNoApplicationProtocol"
}
rustls_result::AlertUnknown => "AlertUnknown",
rustls_result::CertRevocationListBadSignature => {
"CertRevocationListBadSignature"
}
rustls_result::CertRevocationListInvalidCrlNumber => {
"CertRevocationListInvalidCrlNumber"
}
rustls_result::CertRevocationListInvalidRevokedCertSerialNumber => {
"CertRevocationListInvalidRevokedCertSerialNumber"
}
rustls_result::CertRevocationListIssuerInvalidForCrl => {
"CertRevocationListIssuerInvalidForCrl"
}
rustls_result::CertRevocationListOtherError => {
"CertRevocationListOtherError"
}
rustls_result::CertRevocationListParseError => {
"CertRevocationListParseError"
}
rustls_result::CertRevocationListUnsupportedCrlVersion => {
"CertRevocationListUnsupportedCrlVersion"
}
rustls_result::CertRevocationListUnsupportedCriticalExtension => {
"CertRevocationListUnsupportedCriticalExtension"
}
rustls_result::CertRevocationListUnsupportedDeltaCrl => {
"CertRevocationListUnsupportedDeltaCrl"
}
rustls_result::CertRevocationListUnsupportedIndirectCrl => {
"CertRevocationListUnsupportedIndirectCrl"
}
rustls_result::CertRevocationListUnsupportedRevocationReason => {
"CertRevocationListUnsupportedRevocationReason"
}
rustls_result::ClientCertVerifierBuilderNoRootAnchors => {
"ClientCertVerifierBuilderNoRootAnchors"
}
},
)
}
}
#[automatically_derived]
#[allow(dead_code)]
impl ::core::clone::Clone for rustls_result {
#[inline]
fn clone(&self) -> rustls_result {
*self
}
}
#[automatically_derived]
#[allow(dead_code)]
impl ::core::marker::Copy for rustls_result {}
#[automatically_derived]
#[allow(dead_code)]
impl ::core::marker::StructuralPartialEq for rustls_result {}
#[automatically_derived]
#[allow(dead_code)]
impl ::core::cmp::PartialEq for rustls_result {
#[inline]
fn eq(&self, other: &rustls_result) -> bool {
let __self_tag = ::core::intrinsics::discriminant_value(self);
let __arg1_tag = ::core::intrinsics::discriminant_value(other);
__self_tag == __arg1_tag
}
}
#[automatically_derived]
#[allow(dead_code)]
impl ::core::cmp::Eq for rustls_result {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {}
}
impl From<u32> for rustls_result {
fn from(x: u32) -> Self {
match x {
7000 => rustls_result::Ok,
7001 => rustls_result::Io,
7002 => rustls_result::NullParameter,
7003 => rustls_result::InvalidDnsNameError,
7004 => rustls_result::Panic,
7005 => rustls_result::CertificateParseError,
7006 => rustls_result::PrivateKeyParseError,
7007 => rustls_result::InsufficientSize,
7008 => rustls_result::NotFound,
7009 => rustls_result::InvalidParameter,
7010 => rustls_result::UnexpectedEof,
7011 => rustls_result::PlaintextEmpty,
7012 => rustls_result::AcceptorNotReady,
7013 => rustls_result::AlreadyUsed,
7014 => rustls_result::CertificateRevocationListParseError,
7101 => rustls_result::NoCertificatesPresented,
7102 => rustls_result::DecryptError,
7103 => rustls_result::FailedToGetCurrentTime,
7113 => rustls_result::FailedToGetRandomBytes,
7104 => rustls_result::HandshakeNotComplete,
7105 => rustls_result::PeerSentOversizedRecord,
7106 => rustls_result::NoApplicationProtocol,
7114 => rustls_result::BadMaxFragmentSize,
7115 => rustls_result::UnsupportedNameType,
7116 => rustls_result::EncryptError,
7121 => rustls_result::CertEncodingBad,
7122 => rustls_result::CertExpired,
7123 => rustls_result::CertNotYetValid,
7124 => rustls_result::CertRevoked,
7125 => rustls_result::CertUnhandledCriticalExtension,
7126 => rustls_result::CertUnknownIssuer,
7127 => rustls_result::CertBadSignature,
7128 => rustls_result::CertNotValidForName,
7129 => rustls_result::CertInvalidPurpose,
7130 => rustls_result::CertApplicationVerificationFailure,
7131 => rustls_result::CertOtherError,
7133 => rustls_result::MessageHandshakePayloadTooLarge,
7134 => rustls_result::MessageInvalidCcs,
7135 => rustls_result::MessageInvalidContentType,
7136 => rustls_result::MessageInvalidCertStatusType,
7137 => rustls_result::MessageInvalidCertRequest,
7138 => rustls_result::MessageInvalidDhParams,
7139 => rustls_result::MessageInvalidEmptyPayload,
7140 => rustls_result::MessageInvalidKeyUpdate,
7141 => rustls_result::MessageInvalidServerName,
7142 => rustls_result::MessageTooLarge,
7143 => rustls_result::MessageTooShort,
7144 => rustls_result::MessageMissingData,
7145 => rustls_result::MessageMissingKeyExchange,
7146 => rustls_result::MessageNoSignatureSchemes,
7147 => rustls_result::MessageTrailingData,
7148 => rustls_result::MessageUnexpectedMessage,
7149 => rustls_result::MessageUnknownProtocolVersion,
7150 => rustls_result::MessageUnsupportedCompression,
7151 => rustls_result::MessageUnsupportedCurveType,
7152 => rustls_result::MessageUnsupportedKeyExchangeAlgorithm,
7153 => rustls_result::MessageInvalidOther,
7107 => rustls_result::PeerIncompatibleError,
7108 => rustls_result::PeerMisbehavedError,
7109 => rustls_result::InappropriateMessage,
7110 => rustls_result::InappropriateHandshakeMessage,
7112 => rustls_result::General,
7200 => rustls_result::AlertCloseNotify,
7201 => rustls_result::AlertUnexpectedMessage,
7202 => rustls_result::AlertBadRecordMac,
7203 => rustls_result::AlertDecryptionFailed,
7204 => rustls_result::AlertRecordOverflow,
7205 => rustls_result::AlertDecompressionFailure,
7206 => rustls_result::AlertHandshakeFailure,
7207 => rustls_result::AlertNoCertificate,
7208 => rustls_result::AlertBadCertificate,
7209 => rustls_result::AlertUnsupportedCertificate,
7210 => rustls_result::AlertCertificateRevoked,
7211 => rustls_result::AlertCertificateExpired,
7212 => rustls_result::AlertCertificateUnknown,
7213 => rustls_result::AlertIllegalParameter,
7214 => rustls_result::AlertUnknownCA,
7215 => rustls_result::AlertAccessDenied,
7216 => rustls_result::AlertDecodeError,
7217 => rustls_result::AlertDecryptError,
7218 => rustls_result::AlertExportRestriction,
7219 => rustls_result::AlertProtocolVersion,
7220 => rustls_result::AlertInsufficientSecurity,
7221 => rustls_result::AlertInternalError,
7222 => rustls_result::AlertInappropriateFallback,
7223 => rustls_result::AlertUserCanceled,
7224 => rustls_result::AlertNoRenegotiation,
7225 => rustls_result::AlertMissingExtension,
7226 => rustls_result::AlertUnsupportedExtension,
7227 => rustls_result::AlertCertificateUnobtainable,
7228 => rustls_result::AlertUnrecognisedName,
7229 => rustls_result::AlertBadCertificateStatusResponse,
7230 => rustls_result::AlertBadCertificateHashValue,
7231 => rustls_result::AlertUnknownPSKIdentity,
7232 => rustls_result::AlertCertificateRequired,
7233 => rustls_result::AlertNoApplicationProtocol,
7234 => rustls_result::AlertUnknown,
7400 => rustls_result::CertRevocationListBadSignature,
7401 => rustls_result::CertRevocationListInvalidCrlNumber,
7402 => rustls_result::CertRevocationListInvalidRevokedCertSerialNumber,
7403 => rustls_result::CertRevocationListIssuerInvalidForCrl,
7404 => rustls_result::CertRevocationListOtherError,
7405 => rustls_result::CertRevocationListParseError,
7406 => rustls_result::CertRevocationListUnsupportedCrlVersion,
7407 => rustls_result::CertRevocationListUnsupportedCriticalExtension,
7408 => rustls_result::CertRevocationListUnsupportedDeltaCrl,
7409 => rustls_result::CertRevocationListUnsupportedIndirectCrl,
7410 => rustls_result::CertRevocationListUnsupportedRevocationReason,
7500 => rustls_result::ClientCertVerifierBuilderNoRootAnchors,
_ => rustls_result::InvalidParameter,
}
}
}
impl rustls_result {
/// After a rustls function returns an error, you may call
/// this to get a pointer to a buffer containing a detailed error
/// message. The contents of the error buffer will be out_n bytes long,
/// UTF-8 encoded, and not NUL-terminated.
#[no_mangle]
pub extern "C" fn rustls_error(
result: c_uint,
buf: *mut c_char,
len: size_t,
out_n: *mut size_t,
) {
match ::std::panic::catch_unwind(|| {
if buf.is_null() {
return;
}
if out_n.is_null() {
return;
}
let error_str = rustls_result::from(result).to_string();
let out_len: usize = min(len - 1, error_str.len());
unsafe {
std::ptr::copy_nonoverlapping(
error_str.as_ptr() as *mut c_char,
buf,
out_len,
);
*out_n = out_len;
}
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
#[no_mangle]
pub extern "C" fn rustls_result_is_cert_error(result: c_uint) -> bool {
use rustls_result::*;
match rustls_result::from(result) {
CertEncodingBad
| CertExpired
| CertNotYetValid
| CertRevoked
| CertUnhandledCriticalExtension
| CertUnknownIssuer
| CertBadSignature
| CertNotValidForName
| CertInvalidPurpose
| CertApplicationVerificationFailure
| CertOtherError => true,
_ => false,
}
}
}
/// For cert-related rustls_results, turn them into a rustls::Error. For other
/// inputs, including Ok, return rustls::Error::General.
pub(crate) fn cert_result_to_error(result: rustls_result) -> rustls::Error {
use rustls::Error::*;
use rustls::OtherError;
use rustls_result::*;
match result {
CertEncodingBad => InvalidCertificate(CertificateError::BadEncoding),
CertExpired => InvalidCertificate(CertificateError::Expired),
CertNotYetValid => InvalidCertificate(CertificateError::NotValidYet),
CertRevoked => InvalidCertificate(CertificateError::Revoked),
CertUnhandledCriticalExtension => {
InvalidCertificate(CertificateError::UnhandledCriticalExtension)
}
CertUnknownIssuer => InvalidCertificate(CertificateError::UnknownIssuer),
CertBadSignature => InvalidCertificate(CertificateError::BadSignature),
CertNotValidForName => InvalidCertificate(CertificateError::NotValidForName),
CertInvalidPurpose => InvalidCertificate(CertificateError::InvalidPurpose),
CertApplicationVerificationFailure => {
InvalidCertificate(CertificateError::ApplicationVerificationFailure)
}
CertOtherError => {
InvalidCertificate(
CertificateError::Other(OtherError(Arc::from(Box::from("")))),
)
}
_ => rustls::Error::General("".into()),
}
}
pub(crate) fn map_error(input: rustls::Error) -> rustls_result {
use rustls::AlertDescription as alert;
use rustls_result::*;
match input {
Error::InappropriateMessage { .. } => InappropriateMessage,
Error::InappropriateHandshakeMessage { .. } => InappropriateHandshakeMessage,
Error::NoCertificatesPresented => NoCertificatesPresented,
Error::DecryptError => DecryptError,
Error::PeerIncompatible(_) => PeerIncompatibleError,
Error::PeerMisbehaved(_) => PeerMisbehavedError,
Error::UnsupportedNameType => UnsupportedNameType,
Error::EncryptError => EncryptError,
Error::InvalidMessage(e) => {
match e {
InvalidMessage::HandshakePayloadTooLarge => {
MessageHandshakePayloadTooLarge
}
InvalidMessage::InvalidCcs => MessageInvalidCcs,
InvalidMessage::InvalidContentType => MessageInvalidContentType,
InvalidMessage::InvalidCertificateStatusType => {
MessageInvalidCertStatusType
}
InvalidMessage::InvalidCertRequest => MessageInvalidCertRequest,
InvalidMessage::InvalidDhParams => MessageInvalidDhParams,
InvalidMessage::InvalidEmptyPayload => MessageInvalidEmptyPayload,
InvalidMessage::InvalidKeyUpdate => MessageInvalidKeyUpdate,
InvalidMessage::InvalidServerName => MessageInvalidServerName,
InvalidMessage::MessageTooLarge => MessageTooLarge,
InvalidMessage::MessageTooShort => MessageTooShort,
InvalidMessage::MissingData(_) => MessageMissingData,
InvalidMessage::MissingKeyExchange => MessageMissingKeyExchange,
InvalidMessage::NoSignatureSchemes => MessageNoSignatureSchemes,
InvalidMessage::TrailingData(_) => MessageTrailingData,
InvalidMessage::UnexpectedMessage(_) => MessageUnexpectedMessage,
InvalidMessage::UnknownProtocolVersion => {
MessageUnknownProtocolVersion
}
InvalidMessage::UnsupportedCompression => {
MessageUnsupportedCompression
}
InvalidMessage::UnsupportedCurveType => MessageUnsupportedCurveType,
InvalidMessage::UnsupportedKeyExchangeAlgorithm(_) => {
MessageUnsupportedCompression
}
_ => MessageInvalidOther,
}
}
Error::FailedToGetCurrentTime => FailedToGetCurrentTime,
Error::FailedToGetRandomBytes => FailedToGetRandomBytes,
Error::HandshakeNotComplete => HandshakeNotComplete,
Error::PeerSentOversizedRecord => PeerSentOversizedRecord,
Error::NoApplicationProtocol => NoApplicationProtocol,
Error::BadMaxFragmentSize => BadMaxFragmentSize,
Error::InvalidCertificate(e) => {
match e {
CertificateError::BadEncoding => CertEncodingBad,
CertificateError::Expired => CertExpired,
CertificateError::NotValidYet => CertNotYetValid,
CertificateError::Revoked => CertRevoked,
CertificateError::UnhandledCriticalExtension => {
CertUnhandledCriticalExtension
}
CertificateError::UnknownIssuer => CertUnknownIssuer,
CertificateError::BadSignature => CertBadSignature,
CertificateError::NotValidForName => CertNotValidForName,
CertificateError::InvalidPurpose => CertInvalidPurpose,
CertificateError::ApplicationVerificationFailure => {
CertApplicationVerificationFailure
}
_ => CertOtherError,
}
}
Error::General(_) => General,
Error::AlertReceived(e) => {
match e {
alert::CloseNotify => AlertCloseNotify,
alert::UnexpectedMessage => AlertUnexpectedMessage,
alert::BadRecordMac => AlertBadRecordMac,
alert::DecryptionFailed => AlertDecryptionFailed,
alert::RecordOverflow => AlertRecordOverflow,
alert::DecompressionFailure => AlertDecompressionFailure,
alert::HandshakeFailure => AlertHandshakeFailure,
alert::NoCertificate => AlertNoCertificate,
alert::BadCertificate => AlertBadCertificate,
alert::UnsupportedCertificate => AlertUnsupportedCertificate,
alert::CertificateRevoked => AlertCertificateRevoked,
alert::CertificateExpired => AlertCertificateExpired,
alert::CertificateUnknown => AlertCertificateUnknown,
alert::IllegalParameter => AlertIllegalParameter,
alert::UnknownCA => AlertUnknownCA,
alert::AccessDenied => AlertAccessDenied,
alert::DecodeError => AlertDecodeError,
alert::DecryptError => AlertDecryptError,
alert::ExportRestriction => AlertExportRestriction,
alert::ProtocolVersion => AlertProtocolVersion,
alert::InsufficientSecurity => AlertInsufficientSecurity,
alert::InternalError => AlertInternalError,
alert::InappropriateFallback => AlertInappropriateFallback,
alert::UserCanceled => AlertUserCanceled,
alert::NoRenegotiation => AlertNoRenegotiation,
alert::MissingExtension => AlertMissingExtension,
alert::UnsupportedExtension => AlertUnsupportedExtension,
alert::CertificateUnobtainable => AlertCertificateUnobtainable,
alert::UnrecognisedName => AlertUnrecognisedName,
alert::BadCertificateStatusResponse => {
AlertBadCertificateStatusResponse
}
alert::BadCertificateHashValue => AlertBadCertificateHashValue,
alert::UnknownPSKIdentity => AlertUnknownPSKIdentity,
alert::CertificateRequired => AlertCertificateRequired,
alert::NoApplicationProtocol => AlertNoApplicationProtocol,
alert::Unknown(_) => AlertUnknown,
_ => AlertUnknown,
}
}
Error::InvalidCertRevocationList(e) => map_crl_error(e),
_ => General,
}
}
pub(crate) fn map_crl_error(err: CertRevocationListError) -> rustls_result {
use rustls_result::*;
match err {
CertRevocationListError::BadSignature => CertRevocationListBadSignature,
CertRevocationListError::InvalidCrlNumber => {
CertRevocationListInvalidCrlNumber
}
CertRevocationListError::InvalidRevokedCertSerialNumber => {
CertRevocationListInvalidRevokedCertSerialNumber
}
CertRevocationListError::IssuerInvalidForCrl => {
CertRevocationListIssuerInvalidForCrl
}
CertRevocationListError::Other(_) => CertRevocationListOtherError,
CertRevocationListError::ParseError => CertRevocationListParseError,
CertRevocationListError::UnsupportedCrlVersion => {
CertRevocationListUnsupportedCrlVersion
}
CertRevocationListError::UnsupportedCriticalExtension => {
CertRevocationListUnsupportedCriticalExtension
}
CertRevocationListError::UnsupportedDeltaCrl => {
CertRevocationListUnsupportedDeltaCrl
}
CertRevocationListError::UnsupportedIndirectCrl => {
CertRevocationListUnsupportedIndirectCrl
}
CertRevocationListError::UnsupportedRevocationReason => {
CertRevocationListUnsupportedRevocationReason
}
_ => CertRevocationListOtherError,
}
}
pub(crate) fn map_verifier_builder_error(
err: VerifierBuilderError,
) -> rustls_result {
match err {
VerifierBuilderError::NoRootAnchors => {
rustls_result::ClientCertVerifierBuilderNoRootAnchors
}
VerifierBuilderError::InvalidCrl(crl_err) => map_crl_error(crl_err),
_ => rustls_result::General,
}
}
impl Display for rustls_result {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use rustls::AlertDescription as alert;
use rustls_result::*;
match self {
rustls_result::Ok => f.write_fmt(format_args!("OK")),
Io => f.write_fmt(format_args!("I/O error")),
NullParameter => f.write_fmt(format_args!("a parameter was NULL")),
InvalidDnsNameError => {
f.write_fmt(
format_args!(
"server name was malformed (not a valid hostname or IP address)",
),
)
}
Panic => f.write_fmt(format_args!("a Rust component panicked")),
CertificateParseError => {
f.write_fmt(format_args!("error parsing certificate"))
}
PrivateKeyParseError => {
f.write_fmt(format_args!("error parsing private key"))
}
InsufficientSize => {
f.write_fmt(format_args!("provided buffer is of insufficient size"))
}
NotFound => f.write_fmt(format_args!("the item was not found")),
InvalidParameter => {
f.write_fmt(format_args!("a parameter had an invalid value"))
}
UnexpectedEof => {
f.write_fmt(
format_args!(
"peer closed TCP connection without first closing TLS connection",
),
)
}
PlaintextEmpty => {
f.write_fmt(
format_args!(
"no plaintext available; call rustls_connection_read_tls again",
),
)
}
AcceptorNotReady => {
f.write_fmt(
format_args!(
"rustls_acceptor not ready yet; read more TLS bytes into it",
),
)
}
AlreadyUsed => {
f.write_fmt(
format_args!(
"tried to use a rustls struct after it had been converted to another struct",
),
)
}
CertificateRevocationListParseError => {
f.write_fmt(
format_args!("error parsing certificate revocation list (CRL)"),
)
}
CertEncodingBad => {
Error::InvalidCertificate(CertificateError::BadEncoding).fmt(f)
}
CertExpired => {
Error::InvalidCertificate(CertificateError::Expired).fmt(f)
}
CertNotYetValid => {
Error::InvalidCertificate(CertificateError::NotValidYet).fmt(f)
}
CertRevoked => {
Error::InvalidCertificate(CertificateError::Revoked).fmt(f)
}
CertUnhandledCriticalExtension => {
Error::InvalidCertificate(
CertificateError::UnhandledCriticalExtension,
)
.fmt(f)
}
CertUnknownIssuer => {
Error::InvalidCertificate(CertificateError::UnknownIssuer).fmt(f)
}
CertBadSignature => {
Error::InvalidCertificate(CertificateError::BadSignature).fmt(f)
}
CertNotValidForName => {
Error::InvalidCertificate(CertificateError::NotValidForName).fmt(f)
}
CertInvalidPurpose => {
Error::InvalidCertificate(CertificateError::InvalidPurpose).fmt(f)
}
CertApplicationVerificationFailure => {
Error::InvalidCertificate(
CertificateError::ApplicationVerificationFailure,
)
.fmt(f)
}
CertOtherError => f.write_fmt(format_args!("unknown certificate error")),
InappropriateMessage => {
f.write_fmt(format_args!("received unexpected message"))
}
InappropriateHandshakeMessage => {
f.write_fmt(format_args!("received unexpected handshake message"))
}
MessageHandshakePayloadTooLarge => {
Error::InvalidMessage(InvalidMessage::HandshakePayloadTooLarge)
.fmt(f)
}
MessageInvalidContentType => {
Error::InvalidMessage(InvalidMessage::InvalidContentType).fmt(f)
}
MessageInvalidServerName => {
Error::InvalidMessage(InvalidMessage::InvalidServerName).fmt(f)
}
MessageTooLarge => {
Error::InvalidMessage(InvalidMessage::MessageTooLarge).fmt(f)
}
MessageTooShort => {
Error::InvalidMessage(InvalidMessage::MessageTooShort).fmt(f)
}
MessageUnknownProtocolVersion => {
Error::InvalidMessage(InvalidMessage::UnknownProtocolVersion).fmt(f)
}
MessageUnsupportedCompression => {
Error::InvalidMessage(InvalidMessage::UnsupportedCompression).fmt(f)
}
MessageInvalidEmptyPayload => {
Error::InvalidMessage(InvalidMessage::InvalidEmptyPayload).fmt(f)
}
MessageInvalidCertStatusType => {
Error::InvalidMessage(InvalidMessage::InvalidCertificateStatusType)
.fmt(f)
}
MessageInvalidKeyUpdate => {
Error::InvalidMessage(InvalidMessage::InvalidKeyUpdate).fmt(f)
}
MessageUnsupportedCurveType => {
Error::InvalidMessage(InvalidMessage::UnsupportedCurveType).fmt(f)
}
MessageMissingData => {
f.write_fmt(
format_args!(
"missing data for the named handshake payload value",
),
)
}
MessageTrailingData => {
f.write_fmt(
format_args!(
"trailing data found for the named handshake payload value",
),
)
}
MessageUnexpectedMessage => {
f.write_fmt(format_args!("peer sent unexpected message type"))
}
MessageUnsupportedKeyExchangeAlgorithm => {
f.write_fmt(
format_args!("peer sent an unsupported key exchange algorithm"),
)
}
MessageMissingKeyExchange => {
f.write_fmt(
format_args!(
"peer did not advertise supported key exchange groups",
),
)
}
MessageNoSignatureSchemes => {
f.write_fmt(
format_args!("peer sent an empty list of signature schemes"),
)
}
MessageInvalidDhParams => {
f.write_fmt(
format_args!(
"peer\'s Diffie-Hellman (DH) parameters could not be decoded",
),
)
}
MessageInvalidCertRequest => {
f.write_fmt(format_args!("invalid certificate request context"))
}
MessageInvalidCcs => {
f.write_fmt(format_args!("invalid change cipher spec (CCS) payload"))
}
MessageInvalidOther => f.write_fmt(format_args!("invalid message")),
PeerIncompatibleError => {
f.write_fmt(format_args!("peer is incompatible"))
}
PeerMisbehavedError => f.write_fmt(format_args!("peer misbehaved")),
General => f.write_fmt(format_args!("general error")),
NoCertificatesPresented => Error::NoCertificatesPresented.fmt(f),
DecryptError => Error::DecryptError.fmt(f),
FailedToGetCurrentTime => Error::FailedToGetCurrentTime.fmt(f),
FailedToGetRandomBytes => Error::FailedToGetRandomBytes.fmt(f),
HandshakeNotComplete => Error::HandshakeNotComplete.fmt(f),
PeerSentOversizedRecord => Error::PeerSentOversizedRecord.fmt(f),
NoApplicationProtocol => Error::NoApplicationProtocol.fmt(f),
BadMaxFragmentSize => Error::BadMaxFragmentSize.fmt(f),
UnsupportedNameType => Error::UnsupportedNameType.fmt(f),
EncryptError => Error::EncryptError.fmt(f),
AlertCloseNotify => Error::AlertReceived(alert::CloseNotify).fmt(f),
AlertUnexpectedMessage => {
Error::AlertReceived(alert::UnexpectedMessage).fmt(f)
}
AlertBadRecordMac => Error::AlertReceived(alert::BadRecordMac).fmt(f),
AlertDecryptionFailed => {
Error::AlertReceived(alert::DecryptionFailed).fmt(f)
}
AlertRecordOverflow => Error::AlertReceived(alert::RecordOverflow).fmt(f),
AlertDecompressionFailure => {
Error::AlertReceived(alert::DecompressionFailure).fmt(f)
}
AlertHandshakeFailure => {
Error::AlertReceived(alert::HandshakeFailure).fmt(f)
}
AlertNoCertificate => Error::AlertReceived(alert::NoCertificate).fmt(f),
AlertBadCertificate => Error::AlertReceived(alert::BadCertificate).fmt(f),
AlertUnsupportedCertificate => {
Error::AlertReceived(alert::UnsupportedCertificate).fmt(f)
}
AlertCertificateRevoked => {
Error::AlertReceived(alert::CertificateRevoked).fmt(f)
}
AlertCertificateExpired => {
Error::AlertReceived(alert::CertificateExpired).fmt(f)
}
AlertCertificateUnknown => {
Error::AlertReceived(alert::CertificateUnknown).fmt(f)
}
AlertIllegalParameter => {
Error::AlertReceived(alert::IllegalParameter).fmt(f)
}
AlertUnknownCA => Error::AlertReceived(alert::UnknownCA).fmt(f),
AlertAccessDenied => Error::AlertReceived(alert::AccessDenied).fmt(f),
AlertDecodeError => Error::AlertReceived(alert::DecodeError).fmt(f),
AlertDecryptError => Error::AlertReceived(alert::DecryptError).fmt(f),
AlertExportRestriction => {
Error::AlertReceived(alert::ExportRestriction).fmt(f)
}
AlertProtocolVersion => {
Error::AlertReceived(alert::ProtocolVersion).fmt(f)
}
AlertInsufficientSecurity => {
Error::AlertReceived(alert::InsufficientSecurity).fmt(f)
}
AlertInternalError => Error::AlertReceived(alert::InternalError).fmt(f),
AlertInappropriateFallback => {
Error::AlertReceived(alert::InappropriateFallback).fmt(f)
}
AlertUserCanceled => Error::AlertReceived(alert::UserCanceled).fmt(f),
AlertNoRenegotiation => {
Error::AlertReceived(alert::NoRenegotiation).fmt(f)
}
AlertMissingExtension => {
Error::AlertReceived(alert::MissingExtension).fmt(f)
}
AlertUnsupportedExtension => {
Error::AlertReceived(alert::UnsupportedExtension).fmt(f)
}
AlertCertificateUnobtainable => {
Error::AlertReceived(alert::CertificateUnobtainable).fmt(f)
}
AlertUnrecognisedName => {
Error::AlertReceived(alert::UnrecognisedName).fmt(f)
}
AlertBadCertificateStatusResponse => {
Error::AlertReceived(alert::BadCertificateStatusResponse).fmt(f)
}
AlertBadCertificateHashValue => {
Error::AlertReceived(alert::BadCertificateHashValue).fmt(f)
}
AlertUnknownPSKIdentity => {
Error::AlertReceived(alert::UnknownPSKIdentity).fmt(f)
}
AlertCertificateRequired => {
Error::AlertReceived(alert::CertificateRequired).fmt(f)
}
AlertNoApplicationProtocol => {
Error::AlertReceived(alert::NoApplicationProtocol).fmt(f)
}
AlertUnknown => Error::AlertReceived(alert::Unknown(0)).fmt(f),
CertRevocationListBadSignature => {
Error::InvalidCertRevocationList(
CertRevocationListError::BadSignature,
)
.fmt(f)
}
CertRevocationListInvalidCrlNumber => {
Error::InvalidCertRevocationList(
CertRevocationListError::InvalidCrlNumber,
)
.fmt(f)
}
CertRevocationListInvalidRevokedCertSerialNumber => {
Error::InvalidCertRevocationList(
CertRevocationListError::InvalidRevokedCertSerialNumber,
)
.fmt(f)
}
CertRevocationListIssuerInvalidForCrl => {
Error::InvalidCertRevocationList(
CertRevocationListError::IssuerInvalidForCrl,
)
.fmt(f)
}
CertRevocationListOtherError => {
f.write_fmt(
format_args!("unknown certificate revocation list (CRL) error"),
)
}
CertRevocationListParseError => {
Error::InvalidCertRevocationList(CertRevocationListError::ParseError)
.fmt(f)
}
CertRevocationListUnsupportedCrlVersion => {
Error::InvalidCertRevocationList(
CertRevocationListError::UnsupportedCrlVersion,
)
.fmt(f)
}
CertRevocationListUnsupportedCriticalExtension => {
Error::InvalidCertRevocationList(
CertRevocationListError::UnsupportedCriticalExtension,
)
.fmt(f)
}
CertRevocationListUnsupportedDeltaCrl => {
Error::InvalidCertRevocationList(
CertRevocationListError::UnsupportedDeltaCrl,
)
.fmt(f)
}
CertRevocationListUnsupportedIndirectCrl => {
Error::InvalidCertRevocationList(
CertRevocationListError::UnsupportedIndirectCrl,
)
.fmt(f)
}
CertRevocationListUnsupportedRevocationReason => {
Error::InvalidCertRevocationList(
CertRevocationListError::UnsupportedRevocationReason,
)
.fmt(f)
}
ClientCertVerifierBuilderNoRootAnchors => {
f.write_fmt(format_args!("no root trust anchors provided"))
}
}
}
}
}
pub mod io {
use std::io::{Error, IoSlice, Read, Result, Write};
use libc::{c_void, size_t};
use crate::error::rustls_io_result;
/// A callback for rustls_connection_read_tls.
/// An implementation of this callback should attempt to read up to n bytes from the
/// network, storing them in `buf`. If any bytes were stored, the implementation should
/// set out_n to the number of bytes stored and return 0. If there was an error,
/// the implementation should return a nonzero rustls_io_result, which will be
/// passed through to the caller. On POSIX systems, returning `errno` is convenient.
/// On other systems, any appropriate error code works.
/// It's best to make one read attempt to the network per call. Additional reads will
/// be triggered by subsequent calls to one of the `_read_tls` methods.
/// `userdata` is set to the value provided to `rustls_connection_set_userdata`. In most
/// cases that should be a struct that contains, at a minimum, a file descriptor.
/// The buf and out_n pointers are borrowed and should not be retained across calls.
pub type rustls_read_callback = Option<
unsafe extern "C" fn(
userdata: *mut c_void,
buf: *mut u8,
n: size_t,
out_n: *mut size_t,
) -> rustls_io_result,
>;
pub(crate) type ReadCallback = unsafe extern "C" fn(
userdata: *mut c_void,
buf: *mut u8,
n: size_t,
out_n: *mut size_t,
) -> rustls_io_result;
pub(crate) struct CallbackReader {
pub callback: ReadCallback,
pub userdata: *mut c_void,
}
impl Read for CallbackReader {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let mut out_n: usize = 0;
let cb = self.callback;
let result = unsafe {
cb(self.userdata, buf.as_mut_ptr(), buf.len(), &mut out_n)
};
match result.0 {
0 => Ok(out_n),
e => Err(Error::from_raw_os_error(e)),
}
}
}
/// A callback for rustls_connection_write_tls or rustls_accepted_alert_write_tls.
/// An implementation of this callback should attempt to write the `n` bytes in buf
/// to the network. If any bytes were written, the implementation should
/// set out_n to the number of bytes stored and return 0. If there was an error,
/// the implementation should return a nonzero rustls_io_result, which will be
/// passed through to the caller. On POSIX systems, returning `errno` is convenient.
/// On other systems, any appropriate error code works.
/// It's best to make one write attempt to the network per call. Additional writes will
/// be triggered by subsequent calls to rustls_connection_write_tls.
/// `userdata` is set to the value provided to `rustls_connection_set_userdata`. In most
/// cases that should be a struct that contains, at a minimum, a file descriptor.
/// The buf and out_n pointers are borrowed and should not be retained across calls.
pub type rustls_write_callback = Option<
unsafe extern "C" fn(
userdata: *mut c_void,
buf: *const u8,
n: size_t,
out_n: *mut size_t,
) -> rustls_io_result,
>;
pub(crate) type WriteCallback = unsafe extern "C" fn(
userdata: *mut c_void,
buf: *const u8,
n: size_t,
out_n: *mut size_t,
) -> rustls_io_result;
pub(crate) struct CallbackWriter {
pub callback: WriteCallback,
pub userdata: *mut c_void,
}
impl Write for CallbackWriter {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let mut out_n: usize = 0;
let cb = self.callback;
let result = unsafe {
cb(self.userdata, buf.as_ptr(), buf.len(), &mut out_n)
};
match result.0 {
0 => Ok(out_n),
e => Err(Error::from_raw_os_error(e)),
}
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
/// An alias for `struct iovec` from uio.h (on Unix) or `WSABUF` on Windows. You should cast
/// `const struct rustls_iovec *` to `const struct iovec *` on Unix, or `const *LPWSABUF`
/// on Windows. See [`std::io::IoSlice`] for details on interoperability with platform
/// specific vectored IO.
pub struct rustls_iovec {
_private: [u8; 0],
}
/// A callback for rustls_connection_write_tls_vectored.
/// An implementation of this callback should attempt to write the bytes in
/// the given `count` iovecs to the network. If any bytes were written,
/// the implementation should set out_n to the number of bytes written and return 0.
/// If there was an error, the implementation should return a nonzero rustls_io_result,
/// which will be passed through to the caller. On POSIX systems, returning `errno` is convenient.
/// On other systems, any appropriate error code works.
/// It's best to make one write attempt to the network per call. Additional write will
/// be triggered by subsequent calls to one of the `_write_tls` methods.
/// `userdata` is set to the value provided to `rustls_*_session_set_userdata`. In most
/// cases that should be a struct that contains, at a minimum, a file descriptor.
/// The buf and out_n pointers are borrowed and should not be retained across calls.
pub type rustls_write_vectored_callback = Option<
unsafe extern "C" fn(
userdata: *mut c_void,
iov: *const rustls_iovec,
count: size_t,
out_n: *mut size_t,
) -> rustls_io_result,
>;
pub(crate) type VectoredWriteCallback = unsafe extern "C" fn(
userdata: *mut c_void,
iov: *const rustls_iovec,
count: size_t,
out_n: *mut size_t,
) -> rustls_io_result;
pub(crate) struct VectoredCallbackWriter {
pub callback: VectoredWriteCallback,
pub userdata: *mut c_void,
}
impl Write for VectoredCallbackWriter {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.write_vectored(&[IoSlice::new(buf)])
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
let mut out_n: usize = 0;
let cb = self.callback;
let result = unsafe {
cb(
self.userdata,
bufs.as_ptr() as *const rustls_iovec,
bufs.len(),
&mut out_n,
)
};
match result.0 {
0 => Ok(out_n),
e => Err(Error::from_raw_os_error(e)),
}
}
}
}
pub mod log {
use libc::c_void;
use log::Level;
use crate::{log_callback_get, rslice::rustls_str};
struct Logger {}
impl log::Log for Logger {
fn enabled(&self, _metadata: &log::Metadata<'_>) -> bool {
true
}
fn log(&self, record: &log::Record<'_>) {
if let Ok((Some(cb), userdata)) = log_callback_get() {
let message = {
let res = ::alloc::fmt::format(
format_args!("{0} {1}", record.target(), record.args()),
);
res
};
if let Ok(message) = message.as_str().try_into() {
unsafe {
cb(
userdata,
&rustls_log_params {
level: record.level() as rustls_log_level,
message,
},
);
}
}
}
}
fn flush(&self) {}
}
#[cfg(not(feature = "no_log_capture"))]
pub(crate) fn ensure_log_registered() {
log::set_logger(&Logger {}).ok();
log::set_max_level(log::LevelFilter::Debug)
}
pub type rustls_log_level = usize;
/// Return a rustls_str containing the stringified version of a log level.
#[no_mangle]
pub extern "C" fn rustls_log_level_str(
level: rustls_log_level,
) -> rustls_str<'static> {
let s = match level {
1 => Level::Error.as_str(),
2 => Level::Warn.as_str(),
3 => Level::Info.as_str(),
4 => Level::Debug.as_str(),
5 => Level::Trace.as_str(),
_ => "INVALID",
};
rustls_str::from_str_unchecked(s)
}
#[repr(C)]
pub struct rustls_log_params<'a> {
pub level: rustls_log_level,
pub message: rustls_str<'a>,
}
#[allow(non_camel_case_types)]
pub type rustls_log_callback = Option<
unsafe extern "C" fn(userdata: *mut c_void, params: *const rustls_log_params),
>;
}
mod panic {
use libc::EINVAL;
use crate::error::{rustls_io_result, rustls_result};
use crate::rslice::{rustls_slice_bytes, rustls_str};
use std::ptr::{null, null_mut};
pub(crate) trait PanicOrDefault {
fn value() -> Self;
}
pub(crate) trait NullParameterOrDefault {
fn value() -> Self;
}
pub(crate) trait Defaultable: Default {}
impl Defaultable for u16 {}
impl Defaultable for usize {}
impl Defaultable for bool {}
impl Defaultable for () {}
impl<T> Defaultable for Option<T> {}
impl<'a> Defaultable for rustls_slice_bytes<'a> {}
impl<T: Defaultable> PanicOrDefault for T {
fn value() -> Self {
Default::default()
}
}
impl<T> PanicOrDefault for *mut T {
fn value() -> Self {
null_mut()
}
}
impl<T> PanicOrDefault for *const T {
fn value() -> Self {
null()
}
}
impl PanicOrDefault for rustls_result {
fn value() -> Self {
rustls_result::Panic
}
}
impl<'a> PanicOrDefault for rustls_str<'a> {
fn value() -> Self {
rustls_str::from_str_unchecked("")
}
}
impl PanicOrDefault for rustls_io_result {
fn value() -> Self {
rustls_io_result(EINVAL)
}
}
impl<T: Defaultable> NullParameterOrDefault for T {
fn value() -> Self {
Default::default()
}
}
impl<T> NullParameterOrDefault for *mut T {
fn value() -> Self {
null_mut()
}
}
impl<T> NullParameterOrDefault for *const T {
fn value() -> Self {
null()
}
}
impl NullParameterOrDefault for rustls_result {
fn value() -> Self {
rustls_result::NullParameter
}
}
impl NullParameterOrDefault for rustls_io_result {
fn value() -> Self {
rustls_io_result(EINVAL)
}
}
impl<'a> NullParameterOrDefault for rustls_str<'a> {
fn value() -> Self {
rustls_str::from_str_unchecked("")
}
}
}
pub mod rslice {
use libc::{c_char, size_t};
use std::fmt;
use std::marker::PhantomData;
use std::ptr::null;
use std::slice;
use std::str;
/// A read-only view on a Rust byte slice.
///
/// This is used to pass data from rustls-ffi to callback functions provided
/// by the user of the API.
/// `len` indicates the number of bytes than can be safely read.
///
/// The memory exposed is available as specified by the function
/// using this in its signature. For instance, when this is a parameter to a
/// callback, the lifetime will usually be the duration of the callback.
/// Functions that receive one of these must not dereference the data pointer
/// beyond the allowed lifetime.
#[repr(C)]
pub struct rustls_slice_bytes<'a> {
pub data: *const u8,
pub len: size_t,
phantom: PhantomData<&'a [u8]>,
}
impl<'a> Default for rustls_slice_bytes<'a> {
fn default() -> rustls_slice_bytes<'a> {
Self {
data: &[0u8; 0] as *const u8,
len: 0,
phantom: PhantomData,
}
}
}
impl<'a> From<&'a [u8]> for rustls_slice_bytes<'a> {
fn from(s: &[u8]) -> Self {
rustls_slice_bytes {
data: s.as_ptr(),
len: s.len(),
phantom: PhantomData,
}
}
}
/// A read-only view of a slice of Rust byte slices.
///
/// This is used to pass data from rustls-ffi to callback functions provided
/// by the user of the API. Because Vec and slice are not `#[repr(C)]`, we
/// provide access via a pointer to an opaque struct and an accessor method
/// that acts on that struct to get entries of type `rustls_slice_bytes`.
/// Internally, the pointee is a `&[&[u8]]`.
///
/// The memory exposed is available as specified by the function
/// using this in its signature. For instance, when this is a parameter to a
/// callback, the lifetime will usually be the duration of the callback.
/// Functions that receive one of these must not call its methods beyond the
/// allowed lifetime.
pub struct rustls_slice_slice_bytes<'a> {
pub(crate) inner: &'a [&'a [u8]],
}
/// Return the length of the outer slice. If the input pointer is NULL,
/// returns 0.
#[no_mangle]
pub extern "C" fn rustls_slice_slice_bytes_len(
input: *const rustls_slice_slice_bytes,
) -> size_t {
match unsafe { input.as_ref() } {
Some(c) => c.inner.len(),
None => 0,
}
}
/// Retrieve the nth element from the input slice of slices. If the input
/// pointer is NULL, or n is greater than the length of the
/// rustls_slice_slice_bytes, returns rustls_slice_bytes{NULL, 0}.
#[no_mangle]
pub extern "C" fn rustls_slice_slice_bytes_get(
input: *const rustls_slice_slice_bytes,
n: size_t,
) -> rustls_slice_bytes {
let input: &rustls_slice_slice_bytes = {
match unsafe { input.as_ref() } {
Some(c) => c,
None => {
return rustls_slice_bytes {
data: null(),
len: 0,
phantom: PhantomData,
};
}
}
};
match input.inner.get(n) {
Some(rsb) => (*rsb).into(),
None => {
rustls_slice_bytes {
data: null(),
len: 0,
phantom: PhantomData,
}
}
}
}
/// A read-only view on a Rust `&str`. The contents are guaranteed to be valid
/// UTF-8. As an additional guarantee on top of Rust's normal UTF-8 guarantee,
/// a `rustls_str` is guaranteed not to contain internal NUL bytes, so it is
/// safe to interpolate into a C string or compare using strncmp. Keep in mind
/// that it is not NUL-terminated.
///
/// The memory exposed is available as specified by the function
/// using this in its signature. For instance, when this is a parameter to a
/// callback, the lifetime will usually be the duration of the callback.
/// Functions that receive one of these must not dereference the data pointer
/// beyond the allowed lifetime.
#[repr(C)]
pub struct rustls_str<'a> {
pub data: *const c_char,
pub len: size_t,
phantom: PhantomData<&'a str>,
}
/// NulByte represents an error converting `&str` to `rustls_str` when the &str
/// contains a NUL.
pub struct NulByte {}
#[automatically_derived]
impl ::core::fmt::Debug for NulByte {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f, "NulByte")
}
}
impl<'a> TryFrom<&'a str> for rustls_str<'a> {
type Error = NulByte;
fn try_from(s: &str) -> Result<Self, Self::Error> {
if s.contains('\0') {
return Err(NulByte {});
}
Ok(rustls_str {
data: s.as_ptr() as *const c_char,
len: s.len(),
phantom: PhantomData,
})
}
}
impl<'a> Default for rustls_str<'a> {
fn default() -> rustls_str<'static> {
Self::from_str_unchecked("")
}
}
/// rustls_str represents a string that can be passed to C code. The string
/// should not have any internal NUL bytes and is not NUL terminated. C code
/// should not create rustls_str objects, they should only be created in Rust
/// code.
impl<'a> rustls_str<'a> {
pub fn from_str_unchecked(s: &'static str) -> rustls_str<'static> {
rustls_str {
data: s.as_ptr() as *const _,
len: s.len(),
phantom: PhantomData,
}
}
/// Change a rustls_str's lifetime to 'static. This doesn't actually change
/// how long the pointed-to data lives, but is necessary when returning a
/// rustls_str (as opposed to passing it into a callback), because Rust
/// can't figure out the "real" lifetime.
///
/// # Safety
///
/// The caller is responsible for requiring (usually via
/// documentation) that nothing uses the resulting rustls_str past its
/// actual validity period. The validity period is somewhat ill-defined
/// at present, but the Stacked Borrows experiment provides one definition,
/// by which a shared reference is valid until a mutable reference (to
/// the object or a parent object) is created.
pub unsafe fn into_static(self) -> rustls_str<'static> {
std::mem::transmute(self)
}
}
impl<'a> fmt::Debug for rustls_str<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let raw = unsafe { slice::from_raw_parts(self.data as *const u8, self.len) };
let s = str::from_utf8(raw).unwrap_or("%!(ERROR)");
f.debug_struct("rustls_str")
.field("data", &s)
.field("len", &self.len)
.finish()
}
}
/// A read-only view of a slice of multiple Rust `&str`'s (that is, multiple
/// strings). Like `rustls_str`, this guarantees that each string contains
/// UTF-8 and no NUL bytes. Strings are not NUL-terminated.
///
/// This is used to pass data from rustls-ffi to callback functions provided
/// by the user of the API. Because Vec and slice are not `#[repr(C)]`, we
/// can't provide a straightforward `data` and `len` structure. Instead, we
/// provide access via a pointer to an opaque struct and accessor methods.
/// Internally, the pointee is a `&[&str]`.
///
/// The memory exposed is available as specified by the function
/// using this in its signature. For instance, when this is a parameter to a
/// callback, the lifetime will usually be the duration of the callback.
/// Functions that receive one of these must not call its methods beyond the
/// allowed lifetime.
pub struct rustls_slice_str<'a> {
pub(crate) inner: &'a [&'a str],
}
/// Return the length of the outer slice. If the input pointer is NULL,
/// returns 0.
#[no_mangle]
pub extern "C" fn rustls_slice_str_len(input: *const rustls_slice_str) -> size_t {
unsafe {
match input.as_ref() {
Some(c) => c.inner.len(),
None => 0,
}
}
}
/// Retrieve the nth element from the input slice of `&str`s. If the input
/// pointer is NULL, or n is greater than the length of the
/// rustls_slice_str, returns rustls_str{NULL, 0}.
#[no_mangle]
pub extern "C" fn rustls_slice_str_get(
input: *const rustls_slice_str,
n: size_t,
) -> rustls_str {
let input: &rustls_slice_str = unsafe {
match input.as_ref() {
Some(c) => c,
None => {
return rustls_str {
data: null(),
len: 0,
phantom: PhantomData,
};
}
}
};
input
.inner
.get(n)
.and_then(|&s| s.try_into().ok())
.unwrap_or(rustls_str {
data: null(),
len: 0,
phantom: PhantomData,
})
}
/// A read-only view on a Rust slice of 16-bit integers in platform endianness.
///
/// This is used to pass data from rustls-ffi to callback functions provided
/// by the user of the API.
/// `len` indicates the number of bytes than can be safely read.
///
/// The memory exposed is available as specified by the function
/// using this in its signature. For instance, when this is a parameter to a
/// callback, the lifetime will usually be the duration of the callback.
/// Functions that receive one of these must not dereference the data pointer
/// beyond the allowed lifetime.
#[repr(C)]
pub struct rustls_slice_u16<'a> {
pub data: *const u16,
pub len: size_t,
phantom: PhantomData<&'a [u16]>,
}
impl<'a> From<&'a [u16]> for rustls_slice_u16<'a> {
fn from(s: &[u16]) -> Self {
rustls_slice_u16 {
data: s.as_ptr(),
len: s.len(),
phantom: PhantomData,
}
}
}
}
pub mod server {
use std::ffi::c_void;
use std::fmt::{Debug, Formatter};
use std::ptr::null;
use std::slice;
use std::sync::Arc;
use libc::size_t;
use rustls::crypto::ring::ALL_CIPHER_SUITES;
use rustls::server::danger::ClientCertVerifier;
use rustls::server::{
ClientHello, ResolvesServerCert, ServerConfig, ServerConnection,
StoresServerSessions, WebPkiClientVerifier,
};
use rustls::sign::CertifiedKey;
use rustls::{ProtocolVersion, SignatureScheme, SupportedCipherSuite, WantsVerifier};
use crate::cipher::{
rustls_certified_key, rustls_client_cert_verifier, rustls_supported_ciphersuite,
};
use crate::connection::{rustls_connection, Connection};
use crate::error::rustls_result::{InvalidParameter, NullParameter};
use crate::error::{map_error, rustls_result};
use crate::rslice::{
rustls_slice_bytes, rustls_slice_slice_bytes, rustls_slice_u16, rustls_str,
};
use crate::session::{
rustls_session_store_get_callback, rustls_session_store_put_callback,
SessionStoreBroker, SessionStoreGetCallback, SessionStorePutCallback,
};
use crate::{
arc_castable, box_castable, ffi_panic_boundary, free_arc, free_box,
set_boxed_mut_ptr, to_arc_const_ptr, to_boxed_mut_ptr, try_box_from_ptr,
try_clone_arc, try_mut_from_ptr, try_ref_from_ptr, try_slice, userdata_get,
Castable, OwnershipRef,
};
/// A server config being constructed. A builder can be modified by,
/// e.g. rustls_server_config_builder_load_native_roots. Once you're
/// done configuring settings, call rustls_server_config_builder_build
/// to turn it into a *const rustls_server_config. This object is not safe
/// for concurrent mutation.
/// <https://docs.rs/rustls/latest/rustls/struct.ConfigBuilder.html>
pub struct rustls_server_config_builder {
_private: [u8; 0],
}
impl crate::Castable for rustls_server_config_builder {
type Ownership = crate::OwnershipBox;
type RustType = ServerConfigBuilder;
}
pub(crate) struct ServerConfigBuilder {
base: rustls::ConfigBuilder<ServerConfig, WantsVerifier>,
verifier: Arc<dyn ClientCertVerifier>,
cert_resolver: Option<Arc<dyn ResolvesServerCert>>,
session_storage: Option<Arc<dyn StoresServerSessions + Send + Sync>>,
alpn_protocols: Vec<Vec<u8>>,
ignore_client_order: Option<bool>,
}
/// A server config that is done being constructed and is now read-only.
/// Under the hood, this object corresponds to an `Arc<ServerConfig>`.
/// <https://docs.rs/rustls/latest/rustls/struct.ServerConfig.html>
pub struct rustls_server_config {
_private: [u8; 0],
}
impl crate::Castable for rustls_server_config {
type Ownership = crate::OwnershipArc;
type RustType = ServerConfig;
}
impl rustls_server_config_builder {
/// Create a rustls_server_config_builder. Caller owns the memory and must
/// eventually call rustls_server_config_builder_build, then free the
/// resulting rustls_server_config. This uses rustls safe default values
/// for the cipher suites, key exchange groups and protocol versions.
#[no_mangle]
pub extern "C" fn rustls_server_config_builder_new() -> *mut rustls_server_config_builder {
match ::std::panic::catch_unwind(|| {
let base = ServerConfig::builder_with_provider(
rustls::crypto::ring::default_provider().into(),
)
.with_safe_default_protocol_versions()
.unwrap();
let builder = ServerConfigBuilder {
base,
verifier: WebPkiClientVerifier::no_client_auth(),
cert_resolver: None,
session_storage: None,
alpn_protocols: ::alloc::vec::Vec::new(),
ignore_client_order: None,
};
to_boxed_mut_ptr(builder)
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Create a rustls_server_config_builder. Caller owns the memory and must
/// eventually call rustls_server_config_builder_build, then free the
/// resulting rustls_server_config. Specify cipher suites in preference
/// order; the `cipher_suites` parameter must point to an array containing
/// `len` pointers to `rustls_supported_ciphersuite` previously obtained
/// from `rustls_all_ciphersuites_get_entry()`. Set the TLS protocol
/// versions to use when negotiating a TLS session.
///
/// `tls_version` is the version of the protocol, as defined in rfc8446,
/// ch. 4.2.1 and end of ch. 5.1. Some values are defined in
/// `rustls_tls_version` for convenience.
///
/// `versions` will only be used during the call and the application retains
/// ownership. `len` is the number of consecutive `uint16_t` pointed to by `versions`.
#[no_mangle]
pub extern "C" fn rustls_server_config_builder_new_custom(
cipher_suites: *const *const rustls_supported_ciphersuite,
cipher_suites_len: size_t,
tls_versions: *const u16,
tls_versions_len: size_t,
builder_out: *mut *mut rustls_server_config_builder,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
if builder_out.is_null() {
return NullParameter;
}
let cipher_suites: &[*const rustls_supported_ciphersuite] = if cipher_suites
.is_null()
{
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe {
slice::from_raw_parts(cipher_suites, cipher_suites_len as usize)
}
};
let mut cs_vec: Vec<SupportedCipherSuite> = Vec::new();
for &cs in cipher_suites.iter() {
let cs = match crate::try_from(cs) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
match ALL_CIPHER_SUITES.iter().find(|&acs| cs.eq(acs)) {
Some(scs) => cs_vec.push(*scs),
None => return InvalidParameter,
}
}
let tls_versions: &[u16] = if tls_versions.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe {
slice::from_raw_parts(tls_versions, tls_versions_len as usize)
}
};
let mut versions = ::alloc::vec::Vec::new();
for version_number in tls_versions {
let proto = ProtocolVersion::from(*version_number);
if proto == rustls::version::TLS12.version {
versions.push(&rustls::version::TLS12);
} else if proto == rustls::version::TLS13.version {
versions.push(&rustls::version::TLS13);
}
}
let provider = rustls::crypto::CryptoProvider {
cipher_suites: cs_vec,
..rustls::crypto::ring::default_provider()
};
let result = rustls::ServerConfig::builder_with_provider(provider.into())
.with_protocol_versions(&versions);
let base = match result {
Ok(new) => new,
Err(_) => return rustls_result::InvalidParameter,
};
let builder = ServerConfigBuilder {
base,
verifier: WebPkiClientVerifier::no_client_auth(),
cert_resolver: None,
session_storage: None,
alpn_protocols: ::alloc::vec::Vec::new(),
ignore_client_order: None,
};
set_boxed_mut_ptr(builder_out, builder);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Create a rustls_server_config_builder for TLS sessions that may verify client
/// certificates. This increases the refcount of `verifier` and doesn't take ownership.
#[no_mangle]
pub extern "C" fn rustls_server_config_builder_set_client_verifier(
builder: *mut rustls_server_config_builder,
verifier: *const rustls_client_cert_verifier,
) {
match ::std::panic::catch_unwind(|| {
let builder: &mut ServerConfigBuilder = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let verifier = match crate::try_from(verifier) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
builder.verifier = verifier.clone();
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// "Free" a server_config_builder without building it into a rustls_server_config.
/// Normally builders are built into rustls_server_configs via `rustls_server_config_builder_build`
/// and may not be free'd or otherwise used afterwards.
/// Use free only when the building of a config has to be aborted before a config
/// was created.
#[no_mangle]
pub extern "C" fn rustls_server_config_builder_free(
config: *mut rustls_server_config_builder,
) {
match ::std::panic::catch_unwind(|| {
free_box(config);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// With `ignore` != 0, the server will ignore the client ordering of cipher
/// suites, aka preference, during handshake and respect its own ordering
/// as configured.
/// <https://docs.rs/rustls/latest/rustls/struct.ServerConfig.html#structfield.ignore_client_order>
#[no_mangle]
pub extern "C" fn rustls_server_config_builder_set_ignore_client_order(
builder: *mut rustls_server_config_builder,
ignore: bool,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let config: &mut ServerConfigBuilder = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
config.ignore_client_order = Some(ignore);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Set the ALPN protocol list to the given protocols. `protocols` must point
/// to a buffer of `rustls_slice_bytes` (built by the caller) with `len`
/// elements. Each element of the buffer must point to a slice of bytes that
/// contains a single ALPN protocol from
/// <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>.
///
/// This function makes a copy of the data in `protocols` and does not retain
/// any pointers, so the caller can free the pointed-to memory after calling.
///
/// <https://docs.rs/rustls/latest/rustls/server/struct.ServerConfig.html#structfield.alpn_protocols>
#[no_mangle]
pub extern "C" fn rustls_server_config_builder_set_alpn_protocols(
builder: *mut rustls_server_config_builder,
protocols: *const rustls_slice_bytes,
len: size_t,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let config: &mut ServerConfigBuilder = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let protocols: &[rustls_slice_bytes] = if protocols.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe { slice::from_raw_parts(protocols, len as usize) }
};
let mut vv: Vec<Vec<u8>> = Vec::new();
for p in protocols {
let v: &[u8] = if p.data.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe { slice::from_raw_parts(p.data, p.len as usize) }
};
vv.push(v.to_vec());
}
config.alpn_protocols = vv;
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Provide the configuration a list of certificates where the connection
/// will select the first one that is compatible with the client's signature
/// verification capabilities. Servers that want to support both ECDSA and
/// RSA certificates will want the ECSDA to go first in the list.
///
/// The built configuration will keep a reference to all certified keys
/// provided. The client may `rustls_certified_key_free()` afterwards
/// without the configuration losing them. The same certified key may also
/// be used in multiple configs.
///
/// EXPERIMENTAL: installing a client_hello callback will replace any
/// configured certified keys and vice versa.
#[no_mangle]
pub extern "C" fn rustls_server_config_builder_set_certified_keys(
builder: *mut rustls_server_config_builder,
certified_keys: *const *const rustls_certified_key,
certified_keys_len: size_t,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let builder: &mut ServerConfigBuilder = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let keys_ptrs: &[*const rustls_certified_key] = if certified_keys
.is_null()
{
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe {
slice::from_raw_parts(
certified_keys,
certified_keys_len as usize,
)
}
};
let mut keys: Vec<Arc<CertifiedKey>> = Vec::new();
for &key_ptr in keys_ptrs {
let certified_key: Arc<CertifiedKey> = match crate::clone_arc(
key_ptr,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
keys.push(certified_key);
}
builder
.cert_resolver = Some(
Arc::new(ResolvesServerCertFromChoices::new(&keys)),
);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Turn a *rustls_server_config_builder (mutable) into a const *rustls_server_config
/// (read-only).
#[no_mangle]
pub extern "C" fn rustls_server_config_builder_build(
builder: *mut rustls_server_config_builder,
) -> *const rustls_server_config {
match ::std::panic::catch_unwind(|| {
let builder = match crate::try_box_from(builder) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let base = builder.base.with_client_cert_verifier(builder.verifier);
let mut config = if let Some(r) = builder.cert_resolver {
base.with_cert_resolver(r)
} else {
return null();
};
if let Some(ss) = builder.session_storage {
config.session_storage = ss;
}
config.alpn_protocols = builder.alpn_protocols;
if let Some(ignore_client_order) = builder.ignore_client_order {
config.ignore_client_order = ignore_client_order;
}
to_arc_const_ptr(config)
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
impl rustls_server_config {
/// "Free" a rustls_server_config previously returned from
/// rustls_server_config_builder_build. Since rustls_server_config is actually an
/// atomically reference-counted pointer, extant server connections may still
/// hold an internal reference to the Rust object. However, C code must
/// consider this pointer unusable after "free"ing it.
/// Calling with NULL is fine. Must not be called twice with the same value.
#[no_mangle]
pub extern "C" fn rustls_server_config_free(
config: *const rustls_server_config,
) {
match ::std::panic::catch_unwind(|| {
free_arc(config);
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Create a new rustls_connection containing a server connection, and return it
/// in the output parameter `out`. If this returns an error code, the memory
/// pointed to by `conn_out` remains unchanged. If this returns a non-error,
/// the memory pointed to by `conn_out` is modified to point
/// at a valid rustls_connection. The caller now owns the rustls_connection
/// and must call `rustls_connection_free` when done with it.
#[no_mangle]
pub extern "C" fn rustls_server_connection_new(
config: *const rustls_server_config,
conn_out: *mut *mut rustls_connection,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
if conn_out.is_null() {
return NullParameter;
}
let config: Arc<ServerConfig> = match crate::clone_arc(config) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let server_connection = match ServerConnection::new(config) {
Ok(sc) => sc,
Err(e) => return map_error(e),
};
let c = Connection::from_server(server_connection);
set_boxed_mut_ptr(conn_out, c);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
/// Copy the server name from the server name indication (SNI) extension to `buf` which can
/// hold up to `count` bytes, and the length of that server name in `out_n`. The string is
/// stored in UTF-8 with no terminating NUL byte.
/// Returns RUSTLS_RESULT_INSUFFICIENT_SIZE if the SNI hostname is longer than `count`.
/// Returns Ok with *out_n == 0 if there is no SNI hostname available on this connection
/// because it hasn't been processed yet, or because the client did not send SNI.
/// <https://docs.rs/rustls/latest/rustls/server/struct.ServerConnection.html#method.server_name>
#[no_mangle]
pub extern "C" fn rustls_server_connection_get_server_name(
conn: *const rustls_connection,
buf: *mut u8,
count: size_t,
out_n: *mut size_t,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let conn: &Connection = match crate::try_from(conn) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
if buf.is_null() {
return NullParameter;
}
if out_n.is_null() {
return NullParameter;
}
let server_connection = match conn.as_server() {
Some(s) => s,
_ => return rustls_result::InvalidParameter,
};
let sni_hostname = match server_connection.server_name() {
Some(sni_hostname) => sni_hostname,
None => {
unsafe {
*out_n = 0;
}
return rustls_result::Ok;
}
};
let len: usize = sni_hostname.len();
if len > count {
unsafe { *out_n = 0 }
return rustls_result::InsufficientSize;
}
unsafe {
std::ptr::copy_nonoverlapping(sni_hostname.as_ptr(), buf, len);
*out_n = len;
}
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
/// Choose the server certificate to be used for a connection based on certificate
/// type. Will pick the first CertfiedKey available that is suitable for
/// the SignatureSchemes supported by the client.
struct ResolvesServerCertFromChoices {
choices: Vec<Arc<CertifiedKey>>,
}
#[automatically_derived]
impl ::core::fmt::Debug for ResolvesServerCertFromChoices {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field1_finish(
f,
"ResolvesServerCertFromChoices",
"choices",
&&self.choices,
)
}
}
impl ResolvesServerCertFromChoices {
pub fn new(choices: &[Arc<CertifiedKey>]) -> Self {
ResolvesServerCertFromChoices {
choices: Vec::from(choices),
}
}
}
impl ResolvesServerCert for ResolvesServerCertFromChoices {
fn resolve(&self, client_hello: ClientHello) -> Option<Arc<CertifiedKey>> {
for key in self.choices.iter() {
if key.key.choose_scheme(client_hello.signature_schemes()).is_some() {
return Some(key.clone());
}
}
None
}
}
/// The TLS Client Hello information provided to a ClientHelloCallback function.
/// `server_name` is the value of the ServerNameIndication extension provided
/// by the client. If the client did not send an SNI, the length of this
/// `rustls_string` will be 0. The signature_schemes field carries the values
/// supplied by the client or, if the client did not send this TLS extension,
/// the default schemes in the rustls library. See:
/// <https://docs.rs/rustls/latest/rustls/internal/msgs/enums/enum.SignatureScheme.html>.
/// `alpn` carries the list of ALPN protocol names that the client proposed to
/// the server. Again, the length of this list will be 0 if none were supplied.
///
/// All this data, when passed to a callback function, is only accessible during
/// the call and may not be modified. Users of this API must copy any values that
/// they want to access when the callback returned.
///
/// EXPERIMENTAL: this feature of rustls-ffi is likely to change in the future, as
/// the rustls library is re-evaluating their current approach to client hello handling.
#[repr(C)]
pub struct rustls_client_hello<'a> {
server_name: rustls_str<'a>,
signature_schemes: rustls_slice_u16<'a>,
alpn: *const rustls_slice_slice_bytes<'a>,
}
impl<'a> Castable for rustls_client_hello<'a> {
type Ownership = OwnershipRef;
type RustType = rustls_client_hello<'a>;
}
/// Any context information the callback will receive when invoked.
pub type rustls_client_hello_userdata = *mut c_void;
/// Prototype of a callback that can be installed by the application at the
/// `rustls_server_config`. This callback will be invoked by a `rustls_connection`
/// once the TLS client hello message has been received.
/// `userdata` will be set based on rustls_connection_set_userdata.
/// `hello` gives the value of the available client announcements, as interpreted
/// by rustls. See the definition of `rustls_client_hello` for details.
///
/// NOTE:
/// - the passed in `hello` and all its values are only available during the
/// callback invocations.
/// - the passed callback function must be safe to call multiple times concurrently
/// with the same userdata, unless there is only a single config and connection
/// where it is installed.
///
/// EXPERIMENTAL: this feature of rustls-ffi is likely to change in the future, as
/// the rustls library is re-evaluating their current approach to client hello handling.
pub type rustls_client_hello_callback = Option<
unsafe extern "C" fn(
userdata: rustls_client_hello_userdata,
hello: *const rustls_client_hello,
) -> *const rustls_certified_key,
>;
type ClientHelloCallback = unsafe extern "C" fn(
userdata: rustls_client_hello_userdata,
hello: *const rustls_client_hello,
) -> *const rustls_certified_key;
struct ClientHelloResolver {
/// Implementation of rustls::ResolvesServerCert that passes values
/// from the supplied ClientHello to the callback function.
pub callback: ClientHelloCallback,
}
impl ClientHelloResolver {
pub fn new(callback: ClientHelloCallback) -> ClientHelloResolver {
ClientHelloResolver { callback }
}
}
impl ResolvesServerCert for ClientHelloResolver {
fn resolve(&self, client_hello: ClientHello) -> Option<Arc<CertifiedKey>> {
let server_name = client_hello.server_name().unwrap_or_default();
let server_name: rustls_str = match server_name.try_into() {
Ok(r) => r,
Err(_) => return None,
};
let mapped_sigs: Vec<u16> = client_hello
.signature_schemes()
.iter()
.map(|s| u16::from(*s))
.collect();
let alpn = match client_hello.alpn() {
Some(iter) => iter.collect(),
None => ::alloc::vec::Vec::new(),
};
let alpn = rustls_slice_slice_bytes {
inner: &alpn,
};
let signature_schemes: rustls_slice_u16 = (&*mapped_sigs).into();
let hello = rustls_client_hello {
server_name,
signature_schemes,
alpn: &alpn,
};
let cb = self.callback;
let userdata = match userdata_get() {
Ok(u) => u,
Err(_) => return None,
};
let key_ptr: *const rustls_certified_key = unsafe { cb(userdata, &hello) };
let certified_key: &CertifiedKey = match crate::try_from(key_ptr) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
Some(Arc::new(certified_key.clone()))
}
}
/// This struct can be considered thread safe, as long
/// as the registered callbacks are thread safe. This is
/// documented as a requirement in the API.
unsafe impl Sync for ClientHelloResolver {}
unsafe impl Send for ClientHelloResolver {}
impl Debug for ClientHelloResolver {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ClientHelloResolver").finish()
}
}
impl rustls_server_config_builder {
/// Register a callback to be invoked when a connection created from this config
/// sees a TLS ClientHello message. If `userdata` has been set with
/// rustls_connection_set_userdata, it will be passed to the callback.
/// Otherwise the userdata param passed to the callback will be NULL.
///
/// Any existing `ResolvesServerCert` implementation currently installed in the
/// `rustls_server_config` will be replaced. This also means registering twice
/// will overwrite the first registration. It is not permitted to pass a NULL
/// value for `callback`.
///
/// EXPERIMENTAL: this feature of rustls-ffi is likely to change in the future, as
/// the rustls library is re-evaluating their current approach to client hello handling.
/// Installing a client_hello callback will replace any configured certified keys
/// and vice versa. Same holds true for the set_certified_keys variant.
#[no_mangle]
pub extern "C" fn rustls_server_config_builder_set_hello_callback(
builder: *mut rustls_server_config_builder,
callback: rustls_client_hello_callback,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let callback: ClientHelloCallback = match callback {
Some(cb) => cb,
None => return rustls_result::NullParameter,
};
let builder: &mut ServerConfigBuilder = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
builder
.cert_resolver = Some(Arc::new(ClientHelloResolver::new(callback)));
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
fn sigschemes(input: &[u16]) -> Vec<SignatureScheme> {
input.iter().copied().map(Into::into).collect()
}
/// Select a `rustls_certified_key` from the list that matches the cryptographic
/// parameters of a TLS client hello. Note that this does not do any SNI matching.
/// The input certificates should already have been filtered to ones matching the
/// SNI from the client hello.
///
/// This is intended for servers that are configured with several keys for the
/// same domain name(s), for example ECDSA and RSA types. The presented keys are
/// inspected in the order given and keys first in the list are given preference,
/// all else being equal. However rustls is free to choose whichever it considers
/// to be the best key with its knowledge about security issues and possible future
/// extensions of the protocol.
///
/// Return RUSTLS_RESULT_OK if a key was selected and RUSTLS_RESULT_NOT_FOUND
/// if none was suitable.
#[no_mangle]
pub extern "C" fn rustls_client_hello_select_certified_key(
hello: *const rustls_client_hello,
certified_keys: *const *const rustls_certified_key,
certified_keys_len: size_t,
out_key: *mut *const rustls_certified_key,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let hello = match crate::try_from(hello) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
let schemes: Vec<SignatureScheme> = sigschemes(
if hello.signature_schemes.data.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe {
slice::from_raw_parts(
hello.signature_schemes.data,
hello.signature_schemes.len as usize,
)
}
},
);
if out_key.is_null() {
return NullParameter;
}
let keys_ptrs: &[*const rustls_certified_key] = if certified_keys.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe {
slice::from_raw_parts(certified_keys, certified_keys_len as usize)
}
};
for &key_ptr in keys_ptrs {
let key_ref: &CertifiedKey = match crate::try_from(key_ptr) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
if key_ref.key.choose_scheme(&schemes).is_some() {
unsafe {
*out_key = key_ptr;
}
return rustls_result::Ok;
}
}
rustls_result::NotFound
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
impl rustls_server_config_builder {
/// Register callbacks for persistence of TLS session IDs and secrets. Both
/// keys and values are highly sensitive data, containing enough information
/// to break the security of the connections involved.
///
/// If `userdata` has been set with rustls_connection_set_userdata, it
/// will be passed to the callbacks. Otherwise the userdata param passed to
/// the callbacks will be NULL.
#[no_mangle]
pub extern "C" fn rustls_server_config_builder_set_persistence(
builder: *mut rustls_server_config_builder,
get_cb: rustls_session_store_get_callback,
put_cb: rustls_session_store_put_callback,
) -> rustls_result {
match ::std::panic::catch_unwind(|| {
let get_cb: SessionStoreGetCallback = match get_cb {
Some(cb) => cb,
None => return rustls_result::NullParameter,
};
let put_cb: SessionStorePutCallback = match put_cb {
Some(cb) => cb,
None => return rustls_result::NullParameter,
};
let builder: &mut ServerConfigBuilder = match crate::try_from_mut(
builder,
) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
};
builder
.session_storage = Some(
Arc::new(SessionStoreBroker::new(get_cb, put_cb)),
);
rustls_result::Ok
}) {
Ok(ret) => ret,
Err(_) => return crate::PanicOrDefault::value(),
}
}
}
}
pub mod session {
use crate::error::rustls_result;
use crate::rslice::rustls_slice_bytes;
use crate::userdata_get;
use libc::{c_int, c_void, size_t};
use std::fmt::{Debug, Formatter};
/// Any context information the callback will receive when invoked.
pub type rustls_session_store_userdata = *mut c_void;
/// Prototype of a callback that can be installed by the application at the
/// `rustls_server_config` or `rustls_client_config`. This callback will be
/// invoked by a TLS session when looking up the data for a TLS session id.
/// `userdata` will be supplied based on rustls_{client,server}_session_set_userdata.
///
/// The `buf` points to `count` consecutive bytes where the
/// callback is expected to copy the result to. The number of copied bytes
/// needs to be written to `out_n`. The callback should not read any
/// data from `buf`.
///
/// If the value to copy is larger than `count`, the callback should never
/// do a partial copy but instead remove the value from its store and
/// act as if it was never found.
///
/// The callback should return RUSTLS_RESULT_OK to indicate that a value was
/// retrieved and written in its entirety into `buf`, or RUSTLS_RESULT_NOT_FOUND
/// if no session was retrieved.
///
/// When `remove_after` is != 0, the returned data needs to be removed
/// from the store.
///
/// NOTE: the passed in `key` and `buf` are only available during the
/// callback invocation.
/// NOTE: callbacks used in several sessions via a common config
/// must be implemented thread-safe.
pub type rustls_session_store_get_callback = Option<
unsafe extern "C" fn(
userdata: rustls_session_store_userdata,
key: *const rustls_slice_bytes,
remove_after: c_int,
buf: *mut u8,
count: size_t,
out_n: *mut size_t,
) -> u32,
>;
pub(crate) type SessionStoreGetCallback = unsafe extern "C" fn(
userdata: rustls_session_store_userdata,
key: *const rustls_slice_bytes,
remove_after: c_int,
buf: *mut u8,
count: size_t,
out_n: *mut size_t,
) -> u32;
/// Prototype of a callback that can be installed by the application at the
/// `rustls_server_config` or `rustls_client_config`. This callback will be
/// invoked by a TLS session when a TLS session has been created and an id
/// for later use is handed to the client/has been received from the server.
/// `userdata` will be supplied based on rustls_{client,server}_session_set_userdata.
///
/// The callback should return RUSTLS_RESULT_OK to indicate that a value was
/// successfully stored, or RUSTLS_RESULT_IO on failure.
///
/// NOTE: the passed in `key` and `val` are only available during the
/// callback invocation.
/// NOTE: callbacks used in several sessions via a common config
/// must be implemented thread-safe.
pub type rustls_session_store_put_callback = Option<
unsafe extern "C" fn(
userdata: rustls_session_store_userdata,
key: *const rustls_slice_bytes,
val: *const rustls_slice_bytes,
) -> u32,
>;
pub(crate) type SessionStorePutCallback = unsafe extern "C" fn(
userdata: rustls_session_store_userdata,
key: *const rustls_slice_bytes,
val: *const rustls_slice_bytes,
) -> u32;
pub(crate) struct SessionStoreBroker {
pub get_cb: SessionStoreGetCallback,
pub put_cb: SessionStorePutCallback,
}
impl SessionStoreBroker {
pub fn new(
get_cb: SessionStoreGetCallback,
put_cb: SessionStorePutCallback,
) -> Self {
SessionStoreBroker {
get_cb,
put_cb,
}
}
fn retrieve(&self, key: &[u8], remove: bool) -> Option<Vec<u8>> {
let key: rustls_slice_bytes = key.into();
let userdata = userdata_get().ok()?;
let mut data: Vec<u8> = ::alloc::vec::from_elem(0, 65 * 1024);
let mut out_n: size_t = 0;
let cb = self.get_cb;
let result = unsafe {
cb(
userdata,
&key,
remove as c_int,
data.as_mut_ptr(),
data.len(),
&mut out_n,
)
};
match rustls_result::from(result) {
rustls_result::Ok => {
unsafe { data.set_len(out_n) };
Some(data)
}
_ => None,
}
}
fn store(&self, key: Vec<u8>, value: Vec<u8>) -> bool {
let key: rustls_slice_bytes = key.as_slice().into();
let value: rustls_slice_bytes = value.as_slice().into();
let cb = self.put_cb;
let userdata = match userdata_get() {
Ok(u) => u,
Err(_) => return false,
};
let result = unsafe { cb(userdata, &key, &value) };
result == rustls_result::Ok as u32
}
}
impl rustls::server::StoresServerSessions for SessionStoreBroker {
fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool {
self.store(key, value)
}
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
self.retrieve(key, false)
}
fn take(&self, key: &[u8]) -> Option<Vec<u8>> {
self.retrieve(key, true)
}
fn can_cache(&self) -> bool {
true
}
}
impl Debug for SessionStoreBroker {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SessionStoreBroker").finish()
}
}
/// This struct can be considered thread safe, as long
/// as the registered callbacks are thread safe. This is
/// documented as a requirement in the API.
unsafe impl Sync for SessionStoreBroker {}
unsafe impl Send for SessionStoreBroker {}
}
pub use error::rustls_result;
pub use error::*;
use crate::log::rustls_log_callback;
use crate::panic::PanicOrDefault;
const RUSTLS_FFI_VERSION: &str = "rustls-ffi/0.13.0/rustls/0.23.4";
#[allow(clippy::thread_local_initializer_can_be_made_const)]
pub(crate) const USERDATA: ::std::thread::LocalKey<RefCell<Vec<Userdata>>> = {
#[inline]
fn __init() -> RefCell<Vec<Userdata>> {
RefCell::new(Vec::new())
}
#[inline]
unsafe fn __getit(
init: ::std::option::Option<&mut ::std::option::Option<RefCell<Vec<Userdata>>>>,
) -> ::std::option::Option<&'static RefCell<Vec<Userdata>>> {
#[thread_local]
static __KEY: ::std::thread::local_impl::Key<RefCell<Vec<Userdata>>> = ::std::thread::local_impl::Key::<
RefCell<Vec<Userdata>>,
>::new();
unsafe {
__KEY
.get(move || {
if let ::std::option::Option::Some(init) = init {
if let ::std::option::Option::Some(value) = init.take() {
return value;
}
if true {
{
::core::panicking::panic_fmt(
format_args!(
"internal error: entered unreachable code: {0}",
format_args!("missing default value"),
),
);
};
}
}
__init()
})
}
}
unsafe { ::std::thread::LocalKey::new(__getit) }
};
pub(crate) struct Userdata {
userdata: *mut c_void,
log_callback: rustls_log_callback,
}
/// UserdataGuard pops an entry off the USERDATA stack, restoring the
/// thread-local state to its value previous to the creation of the UserdataGuard.
/// Invariants: As long as a UserdataGuard is live:
/// - The stack of userdata items for this thread must have at least one item.
/// - The top item on that stack must be the one this guard was built with.
/// - The `data` field must not be None.
/// If any of these invariants fails, try_drop will return an error.
pub(crate) struct UserdataGuard {
data: Option<Userdata>,
}
impl UserdataGuard {
fn new(u: *mut c_void) -> Self {
UserdataGuard {
data: Some(Userdata {
userdata: u,
log_callback: None,
}),
}
}
/// Even though we have a Drop impl on this guard, when possible it's
/// best to call try_drop explicitly. That way any failures of internal
/// variants can be signaled to the user immediately by returning
/// rustls_result::Panic.
fn try_drop(mut self) -> Result<(), UserdataError> {
self.try_pop()
}
fn try_pop(&mut self) -> Result<(), UserdataError> {
let expected_data = self
.data
.as_ref()
.ok_or(UserdataError::AlreadyPopped)?
.userdata;
USERDATA
.try_with(|userdata| {
userdata
.try_borrow_mut()
.map_or_else(
|_| Err(UserdataError::AlreadyBorrowed),
|mut v| {
let u = v.pop().ok_or(UserdataError::EmptyStack)?;
self.data = None;
if u.userdata == expected_data {
Ok(())
} else {
Err(UserdataError::WrongData)
}
},
)
})
.unwrap_or(Err(UserdataError::AccessError))
}
}
impl Drop for UserdataGuard {
fn drop(&mut self) {
self.try_pop().ok();
}
}
pub(crate) enum UserdataError {
/// try_pop was called twice.
AlreadyPopped,
/// The RefCell is borrowed somewhere else.
AlreadyBorrowed,
/// The stack of userdata items was already empty.
EmptyStack,
/// The LocalKey was destroyed before this call.
/// See <https://doc.rust-lang.org/std/thread/struct.LocalKey.html#method.try_with>
AccessError,
/// Unexpected pointer when popping.
WrongData,
}
#[automatically_derived]
impl ::core::clone::Clone for UserdataError {
#[inline]
fn clone(&self) -> UserdataError {
match self {
UserdataError::AlreadyPopped => UserdataError::AlreadyPopped,
UserdataError::AlreadyBorrowed => UserdataError::AlreadyBorrowed,
UserdataError::EmptyStack => UserdataError::EmptyStack,
UserdataError::AccessError => UserdataError::AccessError,
UserdataError::WrongData => UserdataError::WrongData,
}
}
}
#[automatically_derived]
impl ::core::fmt::Debug for UserdataError {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(
f,
match self {
UserdataError::AlreadyPopped => "AlreadyPopped",
UserdataError::AlreadyBorrowed => "AlreadyBorrowed",
UserdataError::EmptyStack => "EmptyStack",
UserdataError::AccessError => "AccessError",
UserdataError::WrongData => "WrongData",
},
)
}
}
#[must_use = "If you drop the guard, userdata will be immediately cleared"]
pub(crate) fn userdata_push(
u: *mut c_void,
cb: rustls_log_callback,
) -> Result<UserdataGuard, UserdataError> {
USERDATA
.try_with(|userdata| {
userdata
.try_borrow_mut()
.map_or_else(
|_| Err(UserdataError::AlreadyBorrowed),
|mut v| {
v.push(Userdata {
userdata: u,
log_callback: cb,
});
Ok(())
},
)
})
.unwrap_or(Err(UserdataError::AccessError))?;
Ok(UserdataGuard::new(u))
}
pub(crate) fn userdata_get() -> Result<*mut c_void, UserdataError> {
USERDATA
.try_with(|userdata| {
userdata
.try_borrow_mut()
.map_or_else(
|_| Err(UserdataError::AlreadyBorrowed),
|v| match v.last() {
Some(u) => Ok(u.userdata),
None => Err(UserdataError::EmptyStack),
},
)
})
.unwrap_or(Err(UserdataError::AccessError))
}
pub(crate) fn log_callback_get() -> Result<
(rustls_log_callback, *mut c_void),
UserdataError,
> {
USERDATA
.try_with(|userdata| {
userdata
.try_borrow_mut()
.map_or_else(
|_| Err(UserdataError::AlreadyBorrowed),
|v| match v.last() {
Some(u) => Ok((u.log_callback, u.userdata)),
None => Err(UserdataError::EmptyStack),
},
)
})
.unwrap_or(Err(UserdataError::AccessError))
}
/// Used to mark that pointer to a [`Castable`]'s underlying `Castable::RustType` is provided
/// to C code as a pointer to a `Box<Castable::RustType>`.
pub(crate) struct OwnershipBox;
/// Used to mark that a pointer to a [`Castable`]'s underlying `Castable::RustType` is provided
/// to C code as a pointer to an `Arc<Castable::RustType>`.
pub(crate) struct OwnershipArc;
/// Used to mark that a pointer to a [`Castable`]'s underlying `Castable::RustType` is provided
/// to C code as a pointer to a reference, `&Castable::RustType`.
pub(crate) struct OwnershipRef;
/// A trait for marking the type of a pointer to a [`Castable`]'s underlying `Castable::RustType`
/// that is provided to C code, either a [`OwnershipBox`] when it is a pointer to a `Box<_>`,
/// a [`OwnershipArc`] when it is a pointer to an `Arc<_>`, or a [`OwnershipRef`] when it is a
/// pointer to a `&_`.
#[allow(dead_code)]
trait OwnershipMarker {}
impl OwnershipMarker for OwnershipBox {}
impl OwnershipMarker for OwnershipArc {}
impl OwnershipMarker for OwnershipRef {}
/// `Castable` represents the relationship between a snake case type (like [`client::rustls_client_config`])
/// and the corresponding Rust type (like [`rustls::ClientConfig`]), specified as the associated type
/// `RustType`. Each `Castable` also has an associated type `Ownership` specifying one of the
/// [`OwnershipMarker`] types, [`OwnershipBox`], [`OwnershipArc`] or [`OwnershipRef`].
///
/// An implementation of `Castable` that uses [`OwnershipBox`] indicates that when we give C code
/// a pointer to the relevant `RustType` `T`, that it is actually a `Box<T>`. An
/// implementation of `Castable` that uses [`OwnershipArc`] means that when we give C code a
/// pointer to the relevant type, that it is actually an `Arc<T>`. Lastly an implementation of
/// `Castable` that uses [`OwnershipRef`] means that when we give C code a pointer to the relevant
/// type, that it is actually a `&T`.
///
/// By using an associated type on `Castable` to communicate this we can use the type system to
/// guarantee that a single type can't implement `Castable` for more than one [`OwnershipMarker`],
/// since this would be a conflicting trait implementation and rejected by the compiler.
///
/// This trait allows us to avoid using `as` in most places, and ensures that when we cast, we're
/// preserving const-ness, and casting between the correct types. Implementing this is required in
/// order to use `try_ref_from_ptr!` or `try_mut_from_ptr!` and several other helpful cast-related
/// conversion helpers.
///
/// You can define a new `Castable` type using the `box_castable!`, `arc_castable!` or
/// `ref_castable!` macros depending on the ownership marker you desire. See each macro's
/// documentation for more information.
pub(crate) trait Castable {
/// Indicates whether to use `Box` or `Arc` when giving a pointer to C code for the underlying
/// `RustType`.
type Ownership: OwnershipMarker;
/// The underlying Rust type that we are casting to and from.
type RustType;
}
pub(crate) use box_castable;
pub(crate) use arc_castable;
pub(crate) use ref_castable;
pub(crate) use castable;
/// Convert a const pointer to a [`Castable`] to a const pointer to its underlying
/// [`Castable::RustType`].
///
/// This can be used regardless of the [`Castable::Ownership`] as we can make const pointers for
/// `Box`, `Arc` and ref types.
pub(crate) fn cast_const_ptr<C>(ptr: *const C) -> *const C::RustType
where
C: Castable,
{
ptr as *const _
}
/// Convert a [`Castable`]'s underlying [`Castable::RustType`] to a constant pointer
/// to an `Arc` over the rust type. Can only be used when the `Castable` has specified a cast type
/// equal to [`OwnershipArc`].
pub(crate) fn to_arc_const_ptr<C>(src: C::RustType) -> *const C
where
C: Castable<Ownership = OwnershipArc>,
{
Arc::into_raw(Arc::new(src)) as *const _
}
/// Given a const pointer to a [`Castable`] representing an `Arc`, clone the `Arc` and return
/// the corresponding Rust type.
///
/// The caller still owns its copy of the `Arc`. In other words, the reference count of the
/// `Arc` will be incremented by 1 by the end of this function.
///
/// To achieve that, we need to `mem::forget` the `Arc` we get back from `into_raw`, because
/// `into_raw` _does_ take back ownership. If we called `into_raw` without `mem::forget`, at the
/// end of the function that Arc would be dropped and the reference count would be decremented,
/// potentially to 0, causing memory to be freed.
///
/// Does nothing, returning `None`, when passed a `NULL` pointer. Can only be used when the
/// `Castable` has specified a cast type equal to [`OwnershipArc`].
///
/// ## Unsafety:
///
/// If non-null, `ptr` must be a pointer that resulted from previously calling `Arc::into_raw`,
/// e.g. from using [`to_arc_const_ptr`].
pub(crate) fn clone_arc<C>(ptr: *const C) -> Option<Arc<C::RustType>>
where
C: Castable<Ownership = OwnershipArc>,
{
if ptr.is_null() {
return None;
}
let rs_typed = cast_const_ptr::<C>(ptr);
let r = unsafe { Arc::from_raw(rs_typed) };
let val = Arc::clone(&r);
mem::forget(r);
Some(val)
}
/// Convert a mutable pointer to a [`Castable`] to an optional `Box` over the underlying rust type.
///
/// Does nothing, returning `None`, when passed `NULL`. Can only be used when the `Castable` has
/// specified a cast type equal to [`OwnershipBox`].
///
/// ## Unsafety:
///
/// If non-null, `ptr` must be a pointer that resulted from previously calling `Box::into_raw`,
/// e.g. from using [`to_boxed_mut_ptr`].
pub(crate) fn to_box<C>(ptr: *mut C) -> Option<Box<C::RustType>>
where
C: Castable<Ownership = OwnershipBox>,
{
if ptr.is_null() {
return None;
}
let rs_typed = cast_mut_ptr(ptr);
unsafe { Some(Box::from_raw(rs_typed)) }
}
/// Free a constant pointer to a [`Castable`]'s underlying [`Castable::RustType`] by
/// reconstituting an `Arc` from the raw pointer and dropping it.
///
/// For types represented with an `Arc` on the Rust side, we offer a `_free()`
/// method to the C side that decrements the refcount and ultimately drops
/// the `Arc` if the refcount reaches 0. By contrast with `to_arc`, we call
/// `Arc::from_raw` on the input pointer, but we _don't_ clone it, because we
/// want the refcount to be lower by one when we reach the end of the function.
///
/// Does nothing, returning `None`, when passed `NULL`. Can only be used when the `Castable` has
/// specified a cast type equal to [`OwnershipArc`].
pub(crate) fn free_arc<C>(ptr: *const C)
where
C: Castable<Ownership = OwnershipArc>,
{
if ptr.is_null() {
return;
}
let rs_typed = cast_const_ptr(ptr);
drop(unsafe { Arc::from_raw(rs_typed) });
}
/// Convert a mutable pointer to a [`Castable`] to an optional `Box` over the underlying
/// [`Castable::RustType`], and immediately let it fall out of scope to be freed.
///
/// Can only be used when the `Castable` has specified a cast type equal to [`OwnershipBox`].
///
/// ## Unsafety:
///
/// If non-null, `ptr` must be a pointer that resulted from previously calling `Box::into_raw`,
/// e.g. from using [`to_boxed_mut_ptr`].
pub(crate) fn free_box<C>(ptr: *mut C)
where
C: Castable<Ownership = OwnershipBox>,
{
to_box(ptr);
}
/// Convert a mutable pointer to a [`Castable`] to a mutable pointer to its underlying
/// [`Castable::RustType`].
///
/// Can only be used when the `Castable` has specified a cast source equal to `BoxCastPtrMarker`.
pub(crate) fn cast_mut_ptr<C>(ptr: *mut C) -> *mut C::RustType
where
C: Castable<Ownership = OwnershipBox>,
{
ptr as *mut _
}
/// Converts a [`Castable`]'s underlying [`Castable::RustType`] to a mutable pointer
/// to a `Box` over the rust type.
///
/// Can only be used when the `Castable` has specified a cast type equal to [`OwnershipBox`].
pub(crate) fn to_boxed_mut_ptr<C>(src: C::RustType) -> *mut C
where
C: Castable<Ownership = OwnershipBox>,
{
Box::into_raw(Box::new(src)) as *mut _
}
/// Converts a [`Castable`]'s underlying [`Castable::RustType`] to a mutable pointer
/// to a `Box` over the rust type and sets the `dst` out pointer to the resulting mutable `Box`
/// pointer. See [`to_boxed_mut_ptr`] for more information.
///
/// ## Unsafety:
///
/// `dst` must not be `NULL`.
pub(crate) fn set_boxed_mut_ptr<C>(dst: *mut *mut C, src: C::RustType)
where
C: Castable<Ownership = OwnershipBox>,
{
unsafe {
*dst = to_boxed_mut_ptr(src);
}
}
/// Converts a [`Castable`]'s underlying [`Castable::RustType`] to a const pointer
/// to an `Arc` over the rust type and sets the `dst` out pointer to the resulting const `Arc`
/// pointer. See [`to_arc_const_ptr`] for more information.
///
/// ## Unsafety:
///
/// `dst` must not be `NULL`.
pub(crate) fn set_arc_mut_ptr<C>(dst: *mut *const C, src: C::RustType)
where
C: Castable<Ownership = OwnershipArc>,
{
unsafe {
*dst = to_arc_const_ptr(src);
}
}
/// Converts a mutable pointer to a [`Castable`] to an optional ref to the underlying
/// [`Castable::RustType`]. See [`cast_mut_ptr`] for more information.
///
/// Does nothing, returning `None`, when passed `NULL`. Can only be used when the `Castable` has
/// specified a cast type equal to [`OwnershipBox`].
pub(crate) fn try_from_mut<'a, C>(from: *mut C) -> Option<&'a mut C::RustType>
where
C: Castable<Ownership = OwnershipBox>,
{
unsafe { cast_mut_ptr(from).as_mut() }
}
pub(crate) use try_mut_from_ptr;
/// Converts a const pointer to a [`Castable`] to an optional ref to the underlying
/// [`Castable::RustType`]. See [`cast_const_ptr`] for more information.
///
/// Does nothing, returning `None` when passed `NULL`. Can be used with `Castable`'s that
/// specify a cast type of [`OwnershipArc`] as well as `Castable`'s that specify
/// a cast type of [`OwnershipBox`].
pub(crate) fn try_from<'a, C, O>(from: *const C) -> Option<&'a C::RustType>
where
C: Castable<Ownership = O>,
{
unsafe { cast_const_ptr(from).as_ref() }
}
pub(crate) use try_ref_from_ptr;
pub(crate) use try_clone_arc;
/// Convert a mutable pointer to a [`Castable`] to an optional `Box` over the underlying
/// [`Castable::RustType`].
///
/// Does nothing, returning `None`, when passed `NULL`. Can only be used with `Castable`'s that
/// specify a cast type of [`OwnershipBox`].
pub(crate) fn try_box_from<C>(from: *mut C) -> Option<Box<C::RustType>>
where
C: Castable<Ownership = OwnershipBox>,
{
to_box(from)
}
pub(crate) use try_box_from_ptr;
pub(crate) use try_slice;
pub(crate) use try_callback;
pub(crate) use try_take;
/// Returns a static string containing the rustls-ffi version as well as the
/// rustls version. The string is alive for the lifetime of the program and does
/// not need to be freed.
#[no_mangle]
pub extern "C" fn rustls_version() -> rustls_str<'static> {
rustls_str::from_str_unchecked(RUSTLS_FFI_VERSION)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment