diff options
author | Jacob Hoffman-Andrews <github@hoffman-andrews.com> | 2021-05-20 17:49:17 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-20 17:49:17 -0700 |
commit | 7d3b1e58e30e8c3dc6b2845947e8229fc68a1e83 (patch) | |
tree | 8873b6f9697ab0b3f2016fc31913da6113342263 | |
parent | 2ff80553c8cf0ba2ee6d78f1119867c2318c50bf (diff) |
Consolidate two session types into one connection type (#99)v0.6.0
This reduces duplication in the API and in the implementation,
and aligns naming with what's in rustls master (connection
instead of session).
Fixes #37
-rw-r--r-- | CHANGELOG.md | 21 | ||||
-rw-r--r-- | src/client.rs | 329 | ||||
-rw-r--r-- | src/connection.rs | 422 | ||||
-rw-r--r-- | src/crustls.h | 464 | ||||
-rw-r--r-- | src/main.c | 48 | ||||
-rw-r--r-- | src/server.rs | 337 |
6 files changed, 663 insertions, 958 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 3801036..c8ec1a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,14 +12,19 @@ ### Changed - - rustls_client_session_read_tls, rustls_client_session_write_tls, - rustls_server_session_read_tls, and rustls_server_session_write_tls - take a callback rather than copying bytes into a buffer. This can simplify - user code significantly and in particular makes it harder for user code - to accidentally drop bytes from the buffer. This introduces a new - rustls_io_error type that is an alias for c_int. It wraps a value from - `errno`. Both the updated read/write functions and the callbacks they - receive return rustls_io_error. + - The separate rustls_client_session and rustls_server_session types have + been merged into a single rustls_connection type. Merging these reduces + duplication in both the API and the implementation, and better reflects + how the underlying rustls library works. The name change, from session + to connection, reflects an [upcoming change in the rustls library](rename). + - The read_tls and write_tls methods now take a callback rather than + copying bytes into a buffer. This can simplify user code significantly + and in particular makes it harder for user code to accidentally drop + bytes from the buffer. This introduces a new rustls_io_error type that + is an alias for c_int. It wraps a value from `errno`. Both the updated + read/write functions and the callbacks they receive return rustls_io_error. + +[rename]: https://github.com/ctz/rustls/commit/9ee16c4c5970eebf2f88704b9e9eaca37aefbea5 ## 0.5.0 - 2021-04-29 diff --git a/src/client.rs b/src/client.rs index 35838ae..34185c0 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,31 +1,28 @@ -use std::ffi::{c_void, OsStr}; +use std::ffi::OsStr; use std::fs::File; use std::io::BufReader; -use std::io::{Read, Write}; -use std::ptr::{null, null_mut}; use std::slice; use std::sync::Arc; use std::{convert::TryInto, ffi::CStr}; use libc::{c_char, size_t}; use rustls::{ - Certificate, ClientConfig, ClientSession, RootCertStore, ServerCertVerified, Session, TLSError, + Certificate, ClientConfig, ClientSession, RootCertStore, ServerCertVerified, TLSError, }; use webpki::DNSNameRef; -use crate::connection; -use crate::error::{self, map_error, result_to_tlserror, rustls_io_result, rustls_result}; -use crate::io::{rustls_read_callback, rustls_write_callback, ReadCallback, WriteCallback}; +use crate::connection::{rustls_connection, Connection}; +use crate::error::{self, result_to_tlserror, rustls_result}; +use crate::rslice::NulByte; use crate::rslice::{rustls_slice_bytes, rustls_slice_slice_bytes, rustls_str}; use crate::session::{ rustls_session_store_get_callback, rustls_session_store_put_callback, SessionStoreBroker, SessionStoreGetCallback, SessionStorePutCallback, }; use crate::{ - arc_with_incref_from_raw, ffi_panic_boundary, is_close_notify, try_callback, try_mut_from_ptr, - try_mut_slice, try_ref_from_ptr, try_slice, userdata_get, userdata_push, CastPtr, + arc_with_incref_from_raw, ffi_panic_boundary, try_mut_from_ptr, try_ref_from_ptr, try_slice, + userdata_get, CastPtr, }; -use crate::{cipher::rustls_certificate, rslice::NulByte}; use rustls_result::NullParameter; /// A client config being constructed. A builder can be modified by, @@ -60,19 +57,6 @@ impl CastPtr for rustls_client_config { type RustType = ClientConfig; } -pub struct rustls_client_session { - _private: [u8; 0], -} - -pub(crate) struct Sess { - session: ClientSession, - userdata: *mut c_void, -} - -impl CastPtr for rustls_client_session { - type RustType = Sess; -} - /// 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 starts out with no trusted roots. @@ -214,7 +198,7 @@ impl rustls::ServerCertVerifier for Verifier { /// /// The callback must not capture any of the pointers in its /// rustls_verify_server_cert_params. -/// If `userdata` has been set with rustls_client_session_set_userdata, it +/// 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. /// @@ -355,7 +339,7 @@ pub extern "C" fn rustls_client_config_builder_set_enable_sni( /// "Free" a client_config previously returned from /// rustls_client_config_builder_build. Since client_config is actually an -/// atomically reference-counted pointer, extant client_sessions may still +/// 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. @@ -370,16 +354,17 @@ pub extern "C" fn rustls_client_config_free(config: *const rustls_client_config) } } -/// Create a new rustls::ClientSession, and return it in the output parameter `out`. -/// If this returns an error code, the memory pointed to by `session_out` remains unchanged. -/// If this returns a non-error, the memory pointed to by `session_out` is modified to point -/// at a valid ClientSession. The caller now owns the ClientSession and must call -/// `rustls_client_session_free` when done with it. +/// 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 `session_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_client_connection_free` when done with it. #[no_mangle] -pub extern "C" fn rustls_client_session_new( +pub extern "C" fn rustls_client_connection_new( config: *const rustls_client_config, hostname: *const c_char, - session_out: *mut *mut rustls_client_session, + conn_out: *mut *mut rustls_connection, ) -> rustls_result { ffi_panic_boundary! { let hostname: &CStr = unsafe { @@ -406,295 +391,21 @@ pub extern "C" fn rustls_client_session_new( // We've succeeded. Put the client on the heap, and transfer ownership // to the caller. After this point, we must return CRUSTLS_OK so the // caller knows it is responsible for this memory. - let c = Sess { - session: ClientSession::new(&config, name_ref), - userdata: null_mut(), - }; + let c = Connection::from_client( ClientSession::new(&config, name_ref)); unsafe { - *session_out = Box::into_raw(Box::new(c)) as *mut _; + *conn_out = Box::into_raw(Box::new(c)) as *mut _; } return rustls_result::Ok; } } -/// Set the userdata pointer associated with this session. This will be passed -/// to any callbacks invoked by the session, if you've set up callbacks in the config. -/// The pointed-to data must outlive the session. -#[no_mangle] -pub extern "C" fn rustls_client_session_set_userdata( - session: *mut rustls_client_session, - userdata: *mut c_void, -) { - let session: &mut Sess = try_mut_from_ptr!(session); - session.userdata = userdata; -} - -#[no_mangle] -pub extern "C" fn rustls_client_session_wants_read(session: *const rustls_client_session) -> bool { - ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); - session.session.wants_read() - } -} - -#[no_mangle] -pub extern "C" fn rustls_client_session_wants_write(session: *const rustls_client_session) -> bool { - ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); - session.session.wants_write() - } -} - -#[no_mangle] -pub extern "C" fn rustls_client_session_is_handshaking( - session: *const rustls_client_session, -) -> bool { - ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); - session.session.is_handshaking() - } -} - -/// 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/0.19.1/rustls/trait.Session.html#tymethod.get_protocol_version -/// https://docs.rs/rustls/0.19.1/rustls/internal/msgs/enums/enum.ProtocolVersion.html -#[no_mangle] -pub extern "C" fn rustls_client_session_get_protocol_version( - session: *const rustls_client_session, -) -> u16 { - ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); - match session.session.get_protocol_version() { - Some(v) => v.get_u16(), - None => 0 - } - } -} - -/// 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 session. -/// If the session is still handshaking, or no ALPN protocol was negotiated, -/// stores NULL and 0 in the output parameters. -/// https://www.iana.org/assignments/tls-parameters/ -/// https://docs.rs/rustls/0.19.1/rustls/trait.Session.html#tymethod.get_alpn_protocol -#[no_mangle] -pub extern "C" fn rustls_client_session_get_alpn_protocol( - session: *const rustls_client_session, - protocol_out: *mut *const u8, - protocol_out_len: *mut usize, -) { - ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); - let protocol_out = try_mut_from_ptr!(protocol_out); - let protocol_out_len = try_mut_from_ptr!(protocol_out_len); - match session.session.get_alpn_protocol() { - Some(p) => { - *protocol_out = p.as_ptr(); - *protocol_out_len = p.len(); - }, - None => { - *protocol_out = null(); - *protocol_out_len = 0; - } - } - } -} - -/// Return the i-th certificate provided by the server. -/// Index 0 is the end entity certificate. Higher indexes are certificates -/// in the chain. Requesting an index higher than what is available returns -/// NULL. -#[no_mangle] -pub extern "C" fn rustls_client_session_get_peer_certificate( - session: *const rustls_client_session, - i: size_t, -) -> *const rustls_certificate { - ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); - match session.session.get_peer_certificates() { - Some(v) => match v.get(i) { - Some(cert) => cert as *const Certificate as *const _, - None => null() - }, - None => null() - } - } -} - -#[no_mangle] -pub extern "C" fn rustls_client_session_process_new_packets( - session: *mut rustls_client_session, -) -> rustls_result { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - let guard = match userdata_push(session.userdata) { - Ok(g) => g, - Err(_) => return rustls_result::Panic, - }; - let result = match session.session.process_new_packets() { - Ok(()) => rustls_result::Ok, - Err(e) => map_error(e), - }; - match guard.try_drop() { - Ok(()) => result, - Err(_) => return rustls_result::Panic, - } - } -} - -/// Queues a close_notify fatal alert to be sent in the next write_tls call. -/// https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.send_close_notify -#[no_mangle] -pub extern "C" fn rustls_client_session_send_close_notify(session: *mut rustls_client_session) { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - session.session.send_close_notify() - } -} - -/// Free a client_session previously returned from rustls_client_session_new. -/// Calling with NULL is fine. Must not be called twice with the same value. -#[no_mangle] -pub extern "C" fn rustls_client_session_free(session: *mut rustls_client_session) { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - // Convert the pointer to a Box and drop it. - unsafe { Box::from_raw(session); } - } -} - -/// Write up to `count` plaintext bytes from `buf` into the ClientSession. -/// This will increase the number of output bytes available to -/// `rustls_client_session_write_tls`. -/// On success, store the number of bytes actually written in *out_n -/// (this may be less than `count`). -/// https://docs.rs/rustls/0.19.0/rustls/struct.ClientSession.html#method.write -#[no_mangle] -pub extern "C" fn rustls_client_session_write( - session: *mut rustls_client_session, - buf: *const u8, - count: size_t, - out_n: *mut size_t, -) -> rustls_result { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - let write_buf: &[u8] = try_slice!(buf, count); - let out_n: &mut size_t = unsafe { - match out_n.as_mut() { - Some(out_n) => out_n, - None => return NullParameter, - } - }; - let n_written: usize = match session.session.write(write_buf) { - Ok(n) => n, - Err(_) => return rustls_result::Io, - }; - *out_n = n_written; - rustls_result::Ok - } -} - -/// Read up to `count` plaintext bytes from the ClientSession 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_client_session_read_tls and -/// rustls_client_session_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/0.19.0/rustls/struct.ClientSession.html#method.read -#[no_mangle] -pub extern "C" fn rustls_client_session_read( - session: *mut rustls_client_session, - buf: *mut u8, - count: size_t, - out_n: *mut size_t, -) -> rustls_result { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - let read_buf: &mut [u8] = try_mut_slice!(buf, count); - let out_n: &mut size_t = try_mut_from_ptr!(out_n); - - let n_read: usize = match session.session.read(read_buf) { - Ok(n) => n, - // Rustls turns close_notify alerts into `io::Error` of kind `ConnectionAborted`. - // https://docs.rs/rustls/0.19.0/rustls/struct.ClientSession.html#impl-Read. - Err(e) if is_close_notify(&e) => { - return rustls_result::AlertCloseNotify; - } - Err(_) => return rustls_result::Io, - }; - *out_n = n_read; - rustls_result::Ok - } -} - -/// 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 are available. -/// The `userdata` parameter is passed through directly to `callback`. Note that -/// this is distinct from the `userdata` parameter set with -/// `rustls_client_session_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/0.19.0/rustls/trait.Session.html#tymethod.read_tls -#[no_mangle] -pub extern "C" fn rustls_client_session_read_tls( - session: *mut rustls_client_session, - callback: rustls_read_callback, - userdata: *mut c_void, - out_n: *mut size_t, -) -> rustls_io_result { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - let out_n: &mut size_t = try_mut_from_ptr!(out_n); - let callback: ReadCallback = try_callback!(callback); - - connection::read_tls(&mut session.session, callback, userdata, out_n) - } -} - -/// 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_client_session_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/0.19.0/rustls/trait.Session.html#tymethod.write_tls -#[no_mangle] -pub extern "C" fn rustls_client_session_write_tls( - session: *mut rustls_client_session, - callback: rustls_write_callback, - userdata: *mut c_void, - out_n: *mut size_t, -) -> rustls_io_result { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - let out_n: &mut size_t = try_mut_from_ptr!(out_n); - let callback: WriteCallback = try_callback!(callback); - - connection::write_tls(&mut session.session, callback, userdata, out_n) - } -} - /// Register callbacks for persistence of TLS session data. This means either /// session IDs (TLSv1.2) or . Both /// keys and values are highly sensitive data, containing enough information /// to break the security of the sessions involved. /// -/// If `userdata` has been set with rustls_client_session_set_userdata, it +/// 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] diff --git a/src/connection.rs b/src/connection.rs index e5a00e3..c12d488 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,41 +1,407 @@ -use std::ffi::c_void; +use std::{ffi::c_void, ptr::null}; +use std::{ptr::null_mut, slice}; use libc::{size_t, EIO}; -use rustls::Session; +use rustls::{Certificate, ClientSession, ServerSession, Session, SupportedCipherSuite}; -use crate::error::rustls_io_result; use crate::io::{CallbackReader, CallbackWriter, ReadCallback, WriteCallback}; +use crate::is_close_notify; +use crate::{ + cipher::{rustls_certificate, rustls_supported_ciphersuite}, + error::{map_error, rustls_io_result, rustls_result}, + io::{rustls_read_callback, rustls_write_callback}, + try_callback, try_mut_slice, +}; +use crate::{ffi_panic_boundary, try_ref_from_ptr}; +use crate::{try_mut_from_ptr, try_slice, userdata_push, CastPtr}; +use rustls_result::NullParameter; -// Call Session::read_tls, providing an &mut dyn Write implemented with a C callback. -pub(crate) fn read_tls( - session: &mut dyn Session, - callback: ReadCallback, +pub(crate) struct Connection { + conn: Inner, userdata: *mut c_void, - out_n: &mut size_t, +} + +enum Inner { + Client(ClientSession), + Server(ServerSession), +} + +impl Connection { + pub(crate) fn from_client(s: ClientSession) -> Self { + Connection { + conn: Inner::Client(s), + userdata: null_mut(), + } + } + + pub(crate) fn from_server(s: ServerSession) -> Self { + Connection { + conn: Inner::Server(s), + userdata: null_mut(), + } + } + + #[allow(dead_code)] + pub(crate) fn as_client(&self) -> Option<&ClientSession> { + match &self.conn { + Inner::Client(c) => Some(c), + _ => None, + } + } + + pub(crate) fn as_server(&self) -> Option<&ServerSession> { + match &self.conn { + Inner::Server(s) => Some(s), + _ => None, + } + } + + #[allow(dead_code)] + pub(crate) fn as_client_mut(&mut self) -> Option<&mut ClientSession> { + match &mut self.conn { + Inner::Client(c) => Some(c), + _ => None, + } + } + + #[allow(dead_code)] + pub(crate) fn as_server_mut(&mut self) -> Option<&mut ServerSession> { + match &mut self.conn { + Inner::Server(s) => Some(s), + _ => None, + } + } +} + +impl<'conn> AsRef<dyn Session + 'conn> for Connection { + fn as_ref(&self) -> &(dyn Session + 'conn) { + match &self.conn { + Inner::Client(c) => c, + Inner::Server(c) => c, + } + } +} + +impl<'conn> AsMut<dyn Session + 'conn> for Connection { + fn as_mut(&mut self) -> &mut (dyn Session + 'conn) { + match &mut self.conn { + Inner::Client(c) => c, + Inner::Server(c) => c, + } + } +} + +pub struct rustls_connection { + _private: [u8; 0], +} + +impl CastPtr for rustls_connection { + type RustType = 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 = try_mut_from_ptr!(conn); + conn.userdata = userdata; +} + +/// 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/0.19.0/rustls/trait.Session.html#tymethod.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 { - let mut reader = CallbackReader { callback, userdata }; - let n_read: usize = match session.read_tls(&mut reader) { - Ok(n) => n, - Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), - }; - *out_n = n_read; + ffi_panic_boundary! { + let conn: &mut Connection = try_mut_from_ptr!(conn); + let out_n: &mut size_t = try_mut_from_ptr!(out_n); + let callback: ReadCallback = try_callback!(callback); + + let mut reader = CallbackReader { callback, userdata }; + let n_read: usize = match conn.as_mut().read_tls(&mut reader) { + Ok(n) => n, + Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), + }; + *out_n = n_read; - rustls_io_result(0) + rustls_io_result(0) + } } -// Call Session::write_tls, providing an &mut dyn Write implemented with a C callback. -pub(crate) fn write_tls( - session: &mut dyn Session, - callback: WriteCallback, +/// 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/0.19.0/rustls/trait.Session.html#tymethod.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, + out_n: *mut size_t, ) -> rustls_io_result { - let mut writer = CallbackWriter { callback, userdata }; - let n_written: usize = match session.write_tls(&mut writer) { - Ok(n) => n, - Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), - }; - *out_n = n_written; - - rustls_io_result(0) + ffi_panic_boundary! { + let conn: &mut Connection = try_mut_from_ptr!(conn); + let out_n: &mut size_t = try_mut_from_ptr!(out_n); + let callback: WriteCallback = try_callback!(callback); + + let mut writer = CallbackWriter { callback, userdata }; + let n_written: usize = match conn.as_mut().write_tls(&mut writer) { + Ok(n) => n, + Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), + }; + *out_n = n_written; + + rustls_io_result(0) + } +} + +#[no_mangle] +pub extern "C" fn rustls_connection_process_new_packets( + conn: *mut rustls_connection, +) -> rustls_result { + ffi_panic_boundary! { + let conn: &mut Connection = try_mut_from_ptr!(conn); + let guard = match userdata_push(conn.userdata) { + Ok(g) => g, + Err(_) => return rustls_result::Panic, + }; + let result = match conn.as_mut().process_new_packets() { + Ok(()) => rustls_result::Ok, + Err(e) => map_error(e), + }; + match guard.try_drop() { + Ok(()) => result, + Err(_) => return rustls_result::Panic, + } + } +} + +#[no_mangle] +pub extern "C" fn rustls_connection_wants_read(conn: *const rustls_connection) -> bool { + ffi_panic_boundary! { + let conn: &Connection = try_ref_from_ptr!(conn); + conn.as_ref().wants_read() + } +} + +#[no_mangle] +pub extern "C" fn rustls_connection_wants_write(conn: *const rustls_connection) -> bool { + ffi_panic_boundary! { + let conn: &Connection = try_ref_from_ptr!(conn); + conn.as_ref().wants_write() + } +} + +#[no_mangle] +pub extern "C" fn rustls_connection_is_handshaking(conn: *const rustls_connection) -> bool { + ffi_panic_boundary! { + let conn: &Connection = try_ref_from_ptr!(conn); + conn.as_ref().is_handshaking() + } +} + +/// 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/0.19.0/rustls/trait.Session.html#tymethod.set_buffer_limit +#[no_mangle] +pub extern "C" fn rustls_connection_set_buffer_limit(conn: *mut rustls_connection, n: usize) { + ffi_panic_boundary! { + let conn: &mut Connection = try_mut_from_ptr!(conn); + conn.as_mut().set_buffer_limit(n); + } +} + +/// Queues a close_notify fatal alert to be sent in the next write_tls call. +/// https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.send_close_notify +#[no_mangle] +pub extern "C" fn rustls_connection_send_close_notify(conn: *mut rustls_connection) { + ffi_panic_boundary! { + let conn: &mut Connection = try_mut_from_ptr!(conn); + conn.as_mut().send_close_notify(); + } +} + +/// 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. +#[no_mangle] +pub extern "C" fn rustls_connection_get_peer_certificate( + conn: *const rustls_connection, + i: size_t, +) -> *const rustls_certificate { + ffi_panic_boundary! { + let conn: &Connection = try_ref_from_ptr!(conn); + match conn.as_ref().get_peer_certificates() { + Some(v) => match v.get(i) { + Some(cert) => cert as *const Certificate as *const _, + None => null() + }, + None => null() + } + } +} + +/// 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. +/// https://www.iana.org/assignments/tls-parameters/ +/// https://docs.rs/rustls/0.19.1/rustls/trait.Session.html#tymethod.get_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, +) { + ffi_panic_boundary! { + let conn: &Connection = try_ref_from_ptr!(conn); + let protocol_out = try_mut_from_ptr!(protocol_out); + let protocol_out_len = try_mut_from_ptr!(protocol_out_len); + match conn.as_ref().get_alpn_protocol() { + Some(p) => { + *protocol_out = p.as_ptr(); + *protocol_out_len = p.len(); + }, + None => { + *protocol_out = null(); + *protocol_out_len = 0; + } + } + } +} + +/// 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/0.19.1/rustls/trait.Session.html#tymethod.get_protocol_version +/// https://docs.rs/rustls/0.19.1/rustls/internal/msgs/enums/enum.ProtocolVersion.html +#[no_mangle] +pub extern "C" fn rustls_connection_get_protocol_version(conn: *const rustls_connection) -> u16 { + ffi_panic_boundary! { + let conn: &Connection = try_ref_from_ptr!(conn); + match conn.as_ref().get_protocol_version() { + Some(p) => p.get_u16(), + _ => 0, + } + } +} + +/// Retrieves the cipher suite agreed with the peer. +/// This returns NULL until the ciphersuite is agreed. +/// https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.get_negotiated_ciphersuite +#[no_mangle] +pub extern "C" fn rustls_connection_get_negotiated_ciphersuite( + conn: *const rustls_connection, +) -> *const rustls_supported_ciphersuite { + ffi_panic_boundary! { + let conn: &Connection = try_ref_from_ptr!(conn); + match conn.as_ref().get_negotiated_ciphersuite() { + Some(cs) => cs as *const SupportedCipherSuite as *const _, + None => null(), + } + } +} + +/// 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`). +#[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 { + ffi_panic_boundary! { + let conn: &mut Connection = try_mut_from_ptr!(conn); + let write_buf: &[u8] = try_slice!(buf, count); + let out_n: &mut size_t = unsafe { + match out_n.as_mut() { + Some(out_n) => out_n, + None => return NullParameter, + } + }; + let n_written: usize = match conn.as_mut().write(write_buf) { + Ok(n) => n, + Err(_) => return rustls_result::Io, + }; + *out_n = n_written; + rustls_result::Ok + } +} + +/// 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. +#[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 { + ffi_panic_boundary! { + let conn: &mut Connection = try_mut_from_ptr!(conn); + let read_buf: &mut [u8] = try_mut_slice!(buf, count); + let out_n: &mut size_t = try_mut_from_ptr!(out_n); + + let n_read: usize = match conn.as_mut().read(read_buf) { + Ok(n) => n, + // Rustls turns close_notify alerts into `io::Error` of kind `ConnectionAborted`. + // https://docs.rs/rustls/0.19.0/rustls/struct.ClientSession.html#impl-Read. + Err(e) if is_close_notify(&e) => { + return rustls_result::AlertCloseNotify; + } + Err(_) => return rustls_result::Io, + }; + *out_n = n_read; + rustls_result::Ok + } +} + +/// 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) { + ffi_panic_boundary! { + let conn: &mut Connection = try_mut_from_ptr!(conn); + // Convert the pointer to a Box and drop it. + unsafe { Box::from_raw(conn); } + } } diff --git a/src/crustls.h b/src/crustls.h index 3ac4ff4..c558416 100644 --- a/src/crustls.h +++ b/src/crustls.h @@ -155,7 +155,7 @@ typedef struct rustls_client_config rustls_client_config; */ typedef struct rustls_client_config_builder rustls_client_config_builder; -typedef struct rustls_client_session rustls_client_session; +typedef struct rustls_connection rustls_connection; /** * A root cert store that is done being constructed and is now read-only. @@ -182,8 +182,6 @@ typedef struct rustls_server_config rustls_server_config; */ typedef struct rustls_server_config_builder rustls_server_config_builder; -typedef struct rustls_server_session rustls_server_session; - /** * A read-only view of a slice of Rust byte slices. * @@ -282,45 +280,6 @@ typedef struct rustls_verify_server_cert_params { typedef enum rustls_result (*rustls_verify_server_cert_callback)(rustls_verify_server_cert_user_data userdata, const struct rustls_verify_server_cert_params *params); /** - * A return value for a function that may return either success (0) or a - * non-zero value representing an error. - */ -typedef int rustls_io_result; - -/** - * A callback for rustls_server_session_read_tls or rustls_client_session_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_*_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. - */ -typedef rustls_io_result (*rustls_read_callback)(void *userdata, uint8_t *buf, size_t n, size_t *out_n); - -/** - * A callback for rustls_server_session_write_tls or rustls_client_session_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. - * (including EAGAIN or EWOULDBLOCK), the implementation should return `errno`. - * 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. - */ -typedef rustls_io_result (*rustls_write_callback)(void *userdata, const uint8_t *buf, size_t n, size_t *out_n); - -/** * Any context information the callback will receive when invoked. */ typedef void *rustls_session_store_userdata; @@ -371,6 +330,45 @@ typedef enum rustls_result (*rustls_session_store_get_callback)(rustls_session_s typedef enum rustls_result (*rustls_session_store_put_callback)(rustls_session_store_userdata userdata, const struct rustls_slice_bytes *key, const struct rustls_slice_bytes *val); /** + * A return value for a function that may return either success (0) or a + * non-zero value representing an error. + */ +typedef int rustls_io_result; + +/** + * A callback for rustls_server_session_read_tls or rustls_client_session_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_*_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. + */ +typedef rustls_io_result (*rustls_read_callback)(void *userdata, uint8_t *buf, size_t n, size_t *out_n); + +/** + * A callback for rustls_server_session_write_tls or rustls_client_session_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. + * (including EAGAIN or EWOULDBLOCK), the implementation should return `errno`. + * 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. + */ +typedef rustls_io_result (*rustls_write_callback)(void *userdata, const uint8_t *buf, size_t n, size_t *out_n); + +/** * Any context information the callback will receive when invoked. */ typedef void *rustls_client_hello_userdata; @@ -419,9 +417,9 @@ typedef struct rustls_client_hello { /** * Prototype of a callback that can be installed by the application at the - * `rustls_server_config`. This callback will be invoked by a `rustls_server_session` + * `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_server_session_set_userdata. + * `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. * @@ -605,7 +603,7 @@ const struct rustls_client_config *rustls_client_config_builder_build(struct rus * * The callback must not capture any of the pointers in its * rustls_verify_server_cert_params. - * If `userdata` has been set with rustls_client_session_set_userdata, it + * 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. * @@ -677,7 +675,7 @@ void rustls_client_config_builder_set_enable_sni(struct rustls_client_config_bui /** * "Free" a client_config previously returned from * rustls_client_config_builder_build. Since client_config is actually an - * atomically reference-counted pointer, extant client_sessions may still + * 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. @@ -685,154 +683,168 @@ void rustls_client_config_builder_set_enable_sni(struct rustls_client_config_bui void rustls_client_config_free(const struct rustls_client_config *config); /** - * Create a new rustls::ClientSession, and return it in the output parameter `out`. - * If this returns an error code, the memory pointed to by `session_out` remains unchanged. - * If this returns a non-error, the memory pointed to by `session_out` is modified to point - * at a valid ClientSession. The caller now owns the ClientSession and must call - * `rustls_client_session_free` when done with it. + * 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 `session_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_client_connection_free` when done with it. + */ +enum rustls_result rustls_client_connection_new(const struct rustls_client_config *config, + const char *hostname, + struct rustls_connection **conn_out); + +/** + * Register callbacks for persistence of TLS session data. This means either + * session IDs (TLSv1.2) or . Both + * keys and values are highly sensitive data, containing enough information + * to break the security of the sessions 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. + */ +enum rustls_result rustls_client_config_builder_set_persistence(struct rustls_client_config_builder *builder, + rustls_session_store_get_callback get_cb, + rustls_session_store_put_callback put_cb); + +/** + * 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. + */ +void rustls_connection_set_userdata(struct rustls_connection *conn, void *userdata); + +/** + * 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/0.19.0/rustls/trait.Session.html#tymethod.read_tls */ -enum rustls_result rustls_client_session_new(const struct rustls_client_config *config, - const char *hostname, - struct rustls_client_session **session_out); +rustls_io_result rustls_connection_read_tls(struct rustls_connection *conn, + rustls_read_callback callback, + void *userdata, + size_t *out_n); /** - * Set the userdata pointer associated with this session. This will be passed - * to any callbacks invoked by the session, if you've set up callbacks in the config. - * The pointed-to data must outlive the session. + * 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/0.19.0/rustls/trait.Session.html#tymethod.write_tls */ -void rustls_client_session_set_userdata(struct rustls_client_session *session, void *userdata); +rustls_io_result rustls_connection_write_tls(struct rustls_connection *conn, + rustls_write_callback callback, + void *userdata, + size_t *out_n); -bool rustls_client_session_wants_read(const struct rustls_client_session *session); +enum rustls_result rustls_connection_process_new_packets(struct rustls_connection *conn); -bool rustls_client_session_wants_write(const struct rustls_client_session *session); +bool rustls_connection_wants_read(const struct rustls_connection *conn); -bool rustls_client_session_is_handshaking(const struct rustls_client_session *session); +bool rustls_connection_wants_write(const struct rustls_connection *conn); + +bool rustls_connection_is_handshaking(const struct rustls_connection *conn); /** - * 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/0.19.1/rustls/trait.Session.html#tymethod.get_protocol_version - * https://docs.rs/rustls/0.19.1/rustls/internal/msgs/enums/enum.ProtocolVersion.html + * 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/0.19.0/rustls/trait.Session.html#tymethod.set_buffer_limit */ -uint16_t rustls_client_session_get_protocol_version(const struct rustls_client_session *session); +void rustls_connection_set_buffer_limit(struct rustls_connection *conn, uintptr_t n); /** - * 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 session. - * If the session is still handshaking, or no ALPN protocol was negotiated, - * stores NULL and 0 in the output parameters. - * https://www.iana.org/assignments/tls-parameters/ - * https://docs.rs/rustls/0.19.1/rustls/trait.Session.html#tymethod.get_alpn_protocol + * Queues a close_notify fatal alert to be sent in the next write_tls call. + * https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.send_close_notify */ -void rustls_client_session_get_alpn_protocol(const struct rustls_client_session *session, - const uint8_t **protocol_out, - uintptr_t *protocol_out_len); +void rustls_connection_send_close_notify(struct rustls_connection *conn); /** - * Return the i-th certificate provided by the server. + * 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. */ -const struct rustls_certificate *rustls_client_session_get_peer_certificate(const struct rustls_client_session *session, - size_t i); +const struct rustls_certificate *rustls_connection_get_peer_certificate(const struct rustls_connection *conn, + size_t i); -enum rustls_result rustls_client_session_process_new_packets(struct rustls_client_session *session); +/** + * 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. + * https://www.iana.org/assignments/tls-parameters/ + * https://docs.rs/rustls/0.19.1/rustls/trait.Session.html#tymethod.get_alpn_protocol + */ +void rustls_connection_get_alpn_protocol(const struct rustls_connection *conn, + const uint8_t **protocol_out, + uintptr_t *protocol_out_len); /** - * Queues a close_notify fatal alert to be sent in the next write_tls call. - * https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.send_close_notify + * 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/0.19.1/rustls/trait.Session.html#tymethod.get_protocol_version + * https://docs.rs/rustls/0.19.1/rustls/internal/msgs/enums/enum.ProtocolVersion.html */ -void rustls_client_session_send_close_notify(struct rustls_client_session *session); +uint16_t rustls_connection_get_protocol_version(const struct rustls_connection *conn); /** - * Free a client_session previously returned from rustls_client_session_new. - * Calling with NULL is fine. Must not be called twice with the same value. + * Retrieves the cipher suite agreed with the peer. + * This returns NULL until the ciphersuite is agreed. + * https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.get_negotiated_ciphersuite */ -void rustls_client_session_free(struct rustls_client_session *session); +const struct rustls_supported_ciphersuite *rustls_connection_get_negotiated_ciphersuite(const struct rustls_connection *conn); /** - * Write up to `count` plaintext bytes from `buf` into the ClientSession. + * Write up to `count` plaintext bytes from `buf` into the `rustls_connection`. * This will increase the number of output bytes available to - * `rustls_client_session_write_tls`. + * `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/0.19.0/rustls/struct.ClientSession.html#method.write */ -enum rustls_result rustls_client_session_write(struct rustls_client_session *session, - const uint8_t *buf, - size_t count, - size_t *out_n); +enum rustls_result rustls_connection_write(struct rustls_connection *conn, + const uint8_t *buf, + size_t count, + size_t *out_n); /** - * Read up to `count` plaintext bytes from the ClientSession into `buf`. + * 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_client_session_read_tls and - * rustls_client_session_process_new_packets." + * 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/0.19.0/rustls/struct.ClientSession.html#method.read - */ -enum rustls_result rustls_client_session_read(struct rustls_client_session *session, - uint8_t *buf, - size_t count, - size_t *out_n); - -/** - * 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 are available. - * The `userdata` parameter is passed through directly to `callback`. Note that - * this is distinct from the `userdata` parameter set with - * `rustls_client_session_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/0.19.0/rustls/trait.Session.html#tymethod.read_tls - */ -rustls_io_result rustls_client_session_read_tls(struct rustls_client_session *session, - rustls_read_callback callback, - void *userdata, - size_t *out_n); - -/** - * 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_client_session_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/0.19.0/rustls/trait.Session.html#tymethod.write_tls */ -rustls_io_result rustls_client_session_write_tls(struct rustls_client_session *session, - rustls_write_callback callback, - void *userdata, - size_t *out_n); +enum rustls_result rustls_connection_read(struct rustls_connection *conn, + uint8_t *buf, + size_t count, + size_t *out_n); /** - * Register callbacks for persistence of TLS session data. This means either - * session IDs (TLSv1.2) or . Both - * keys and values are highly sensitive data, containing enough information - * to break the security of the sessions involved. - * - * If `userdata` has been set with rustls_client_session_set_userdata, it - * will be passed to the callbacks. Otherwise the userdata param passed to - * the callbacks will be NULL. + * Free a rustls_connection. Calling with NULL is fine. + * Must not be called twice with the same value. */ -enum rustls_result rustls_client_config_builder_set_persistence(struct rustls_client_config_builder *builder, - rustls_session_store_get_callback get_cb, - rustls_session_store_put_callback put_cb); +void rustls_connection_free(struct rustls_connection *conn); /** * After a rustls_client_session method returns an error, you may call @@ -991,7 +1003,7 @@ const struct rustls_server_config *rustls_server_config_builder_build(struct rus /** * "Free" a server_config previously returned from * rustls_server_config_builder_build. Since server_config is actually an - * atomically reference-counted pointer, extant server_sessions may still + * 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. @@ -999,126 +1011,15 @@ const struct rustls_server_config *rustls_server_config_builder_build(struct rus void rustls_server_config_free(const struct rustls_server_config *config); /** - * Create a new rustls::ServerSession, and return it in the output parameter `out`. - * If this returns an error code, the memory pointed to by `session_out` remains unchanged. - * If this returns a non-error, the memory pointed to by `session_out` is modified to point - * at a valid ServerSession. The caller now owns the ServerSession and must call - * `rustls_server_session_free` when done with it. - */ -enum rustls_result rustls_server_session_new(const struct rustls_server_config *config, - struct rustls_server_session **session_out); - -/** - * Set the userdata pointer associated with this session. This will be passed - * to any callbacks invoked by the session, if you've set up callbacks in the config. - * The pointed-to data must outlive the session. + * 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 `session_out` remains unchanged. If this returns a non-error, + * the memory pointed to by `session_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. */ -void rustls_server_session_set_userdata(struct rustls_server_session *session, void *userdata); - -bool rustls_server_session_wants_read(const struct rustls_server_session *session); - -bool rustls_server_session_wants_write(const struct rustls_server_session *session); - -bool rustls_server_session_is_handshaking(const struct rustls_server_session *session); - -/** - * 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/0.19.1/rustls/trait.Session.html#tymethod.get_protocol_version - */ -uint16_t rustls_server_session_get_protocol_version(const struct rustls_server_session *session); - -/** - * Return the i-th certificate provided by the client. If no client - * certificate was exchanged during the handshake, this will always - * return NULL. - * Otherwise, this will return the chain, starting with the end entity - * certificate at index 0, followed by the chain provided. - */ -const struct rustls_certificate *rustls_server_session_get_peer_certificate(const struct rustls_server_session *session, - size_t i); - -enum rustls_result rustls_server_session_process_new_packets(struct rustls_server_session *session); - -/** - * Queues a close_notify fatal alert to be sent in the next write_tls call. - * https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.send_close_notify - */ -void rustls_server_session_send_close_notify(struct rustls_server_session *session); - -/** - * Free a server_session previously returned from rustls_server_session_new. - * Calling with NULL is fine. Must not be called twice with the same value. - */ -void rustls_server_session_free(struct rustls_server_session *session); - -/** - * Write up to `count` plaintext bytes from `buf` into the ServerSession. - * This will increase the number of output bytes available to - * `rustls_server_session_write_tls`. - * On success, store the number of bytes actually written in *out_n - * (this may be less than `count`). - * https://docs.rs/rustls/0.19.0/rustls/struct.ServerSession.html#method.write - */ -enum rustls_result rustls_server_session_write(struct rustls_server_session *session, - const uint8_t *buf, - size_t count, - size_t *out_n); - -/** - * Read up to `count` plaintext bytes from the ServerSession 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_server_session_read_tls and - * rustls_server_session_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/0.19.0/rustls/struct.ServerSession.html#method.read - */ -enum rustls_result rustls_server_session_read(struct rustls_server_session *session, - uint8_t *buf, - size_t count, - size_t *out_n); - -/** - * 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_client_session_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/0.19.0/rustls/trait.Session.html#tymethod.read_tls - */ -rustls_io_result rustls_server_session_read_tls(struct rustls_server_session *session, - rustls_read_callback callback, - void *userdata, - size_t *out_n); - -/** - * 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_client_session_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/0.19.0/rustls/trait.Session.html#tymethod.write_tls - */ -rustls_io_result rustls_server_session_write_tls(struct rustls_server_session *session, - rustls_write_callback callback, - void *userdata, - size_t *out_n); +enum rustls_result rustls_server_connection_new(const struct rustls_server_config *config, + struct rustls_connection **conn_out); /** * Copy the SNI hostname to `buf` which can hold up to `count` bytes, @@ -1129,22 +1030,15 @@ rustls_io_result rustls_server_session_write_tls(struct rustls_server_session *s * because it hasn't been processed yet, or because the client did not send SNI. * https://docs.rs/rustls/0.19.0/rustls/struct.ServerSession.html#method.get_sni_hostname */ -enum rustls_result rustls_server_session_get_sni_hostname(const struct rustls_server_session *session, - uint8_t *buf, - size_t count, - size_t *out_n); - -/** - * Retrieves the cipher suite agreed with the peer. - * This returns NULL until the ciphersuite is agreed. - * https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.get_negotiated_ciphersuite - */ -const struct rustls_supported_ciphersuite *rustls_server_session_get_negotiated_ciphersuite(const struct rustls_server_session *session); +enum rustls_result rustls_server_connection_get_sni_hostname(const struct rustls_connection *conn, + uint8_t *buf, + size_t count, + size_t *out_n); /** * Register a callback to be invoked when a session created from this config * is seeing a TLS ClientHello message. If `userdata` has been set with - * rustls_server_session_set_userdata, it will be passed to the callback. + * 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 @@ -1176,17 +1070,17 @@ enum rustls_result rustls_server_config_builder_set_hello_callback(struct rustls * Return RUSTLS_RESULT_OK if a key was selected and RUSTLS_RESULT_NOT_FOUND * if none was suitable. */ -enum rustls_result rustls_server_session_select_certified_key(const struct rustls_client_hello *hello, - const struct rustls_certified_key *const *certified_keys, - size_t certified_keys_len, - const struct rustls_certified_key **out_key); +enum rustls_result rustls_client_hello_select_certified_key(const struct rustls_client_hello *hello, + const struct rustls_certified_key *const *certified_keys, + size_t certified_keys_len, + const struct rustls_certified_key **out_key); /** * 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 sessions involved. * - * If `userdata` has been set with rustls_server_session_set_userdata, it + * 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. */ @@ -191,7 +191,7 @@ cleanup: } -/* Read all available bytes from the client_session until EOF. +/* Read all available bytes from the rustls_connection until EOF. * Note that EOF here indicates "no more bytes until * process_new_packets", not "stream is closed". * @@ -200,7 +200,7 @@ cleanup: * CRUSTLS_DEMO_CLOSE_NOTIFY for "received close_notify" */ int -copy_plaintext_to_stdout(struct rustls_client_session *client_session) +copy_plaintext_to_stdout(struct rustls_connection *client_conn) { int result; char buf[2048]; @@ -208,8 +208,8 @@ copy_plaintext_to_stdout(struct rustls_client_session *client_session) for(;;) { bzero(buf, sizeof(buf)); - result = rustls_client_session_read( - client_session, (uint8_t *)buf, sizeof(buf), &n); + result = rustls_connection_read( + client_conn, (uint8_t *)buf, sizeof(buf), &n); if(result == RUSTLS_RESULT_ALERT_CLOSE_NOTIFY) { fprintf(stderr, "Received close_notify, cleanly ending connection\n"); return CRUSTLS_DEMO_CLOSE_NOTIFY; @@ -219,7 +219,7 @@ copy_plaintext_to_stdout(struct rustls_client_session *client_session) return CRUSTLS_DEMO_ERROR; } if(n == 0) { - /* EOF from ClientSession::read. This is expected. */ + /* This is expected. It just means "no more bytes for now." */ return CRUSTLS_DEMO_OK; } @@ -271,7 +271,7 @@ int write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n /* * Do one read from the socket, and process all resulting bytes into the - * client_session, then copy all plaintext bytes from the session to stdout. + * rustls_connection, then copy all plaintext bytes from the session to stdout. * Returns: * - CRUSTLS_DEMO_OK for success * - CRUSTLS_DEMO_AGAIN if we got an EAGAIN or EWOULDBLOCK reading from the @@ -280,13 +280,13 @@ int write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n * - CRUSTLS_DEMO_ERROR for other errors. */ enum crustls_demo_result -do_read(struct demo_conn *conn, struct rustls_client_session *client_session) +do_read(struct demo_conn *conn, struct rustls_connection *client_conn) { int err = 1; int result = 1; size_t n = 0; - err = rustls_client_session_read_tls(client_session, read_cb, conn, &n); + err = rustls_connection_read_tls(client_conn, read_cb, conn, &n); if(err == EAGAIN || err == EWOULDBLOCK) { fprintf(stderr, @@ -299,13 +299,13 @@ do_read(struct demo_conn *conn, struct rustls_client_session *client_session) return CRUSTLS_DEMO_ERROR; } - result = rustls_client_session_process_new_packets(client_session); + result = rustls_connection_process_new_packets(client_conn); if(result != RUSTLS_RESULT_OK) { print_error("in process_new_packets", result); return CRUSTLS_DEMO_ERROR; } - result = copy_plaintext_to_stdout(client_session); + result = copy_plaintext_to_stdout(client_conn); if(result != CRUSTLS_DEMO_CLOSE_NOTIFY) { return result; } @@ -322,13 +322,13 @@ do_read(struct demo_conn *conn, struct rustls_client_session *client_session) } /* - * Given an established TCP connection, and a rustls client_session, send an + * Given an established TCP connection, and a rustls_connection, send an * HTTP request and read the response. On success, return 0. On error, print * the message and return 1. */ int send_request_and_read_response(struct demo_conn *conn, - struct rustls_client_session *client_session, + struct rustls_connection *client_conn, const char *hostname, const char *path) { int sockfd = conn->fd; @@ -351,8 +351,8 @@ send_request_and_read_response(struct demo_conn *conn, "\r\n", path, hostname); - result = rustls_client_session_write( - client_session, (uint8_t *)buf, strlen(buf), &n); + result = rustls_connection_write( + client_conn, (uint8_t *)buf, strlen(buf), &n); if(result != RUSTLS_RESULT_OK) { fprintf(stderr, "error writing plaintext bytes to ClientSession\n"); goto cleanup; @@ -374,7 +374,7 @@ send_request_and_read_response(struct demo_conn *conn, goto cleanup; } - if(rustls_client_session_wants_read(client_session) && + if(rustls_connection_wants_read(client_conn) && FD_ISSET(sockfd, &read_fds)) { fprintf(stderr, "ClientSession wants us to read_tls. First we need to pull some " @@ -383,7 +383,7 @@ send_request_and_read_response(struct demo_conn *conn, /* Read all bytes until we get EAGAIN. Then loop again to wind up in select awaiting the next bit of data. */ for(;;) { - result = do_read(conn, client_session); + result = do_read(conn, client_conn); if(result == CRUSTLS_DEMO_AGAIN) { break; } @@ -396,10 +396,10 @@ send_request_and_read_response(struct demo_conn *conn, } } } - if(rustls_client_session_wants_write(client_session) && + if(rustls_connection_wants_write(client_conn) && FD_ISSET(sockfd, &write_fds)) { fprintf(stderr, "ClientSession wants us to write_tls.\n"); - err = rustls_client_session_write_tls(client_session, write_cb, conn, &n); + err = rustls_connection_write_tls(client_conn, write_cb, conn, &n); if(err != 0) { fprintf(stderr, "Error in ClientSession::write_tls: errno %d\n", err); goto cleanup; @@ -424,7 +424,7 @@ int do_request(const struct rustls_client_config *client_config, const char *hostname, const char *path) { - struct rustls_client_session *client_session = NULL; + struct rustls_connection *client_conn = NULL; struct demo_conn *conn = NULL; int ret = 1; int sockfd = make_conn(hostname); @@ -441,15 +441,15 @@ do_request(const struct rustls_client_config *client_config, conn->verify_arg = "verify_arg"; rustls_result result = - rustls_client_session_new(client_config, hostname, &client_session); + rustls_client_connection_new(client_config, hostname, &client_conn); if(result != RUSTLS_RESULT_OK) { - print_error("client_session_new", result); + print_error("client_connection_new", result); goto cleanup; } - rustls_client_session_set_userdata(client_session, conn); + rustls_connection_set_userdata(client_conn, conn); - ret = send_request_and_read_response(conn, client_session, hostname, path); + ret = send_request_and_read_response(conn, client_conn, hostname, path); if(ret != RUSTLS_RESULT_OK) { goto cleanup; } @@ -457,7 +457,7 @@ do_request(const struct rustls_client_config *client_config, ret = 0; cleanup: - rustls_client_session_free(client_session); + rustls_connection_free(client_conn); if(sockfd > 0) { close(sockfd); } diff --git a/src/server.rs b/src/server.rs index 9faa7bd..a098cc6 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,7 +1,6 @@ use std::convert::TryInto; use std::ffi::c_void; -use std::io::{Read, Write}; -use std::ptr::{null, null_mut}; +use std::ptr::null_mut; use std::slice; use std::sync::Arc; @@ -9,29 +8,27 @@ use libc::size_t; use rustls::sign::CertifiedKey; use rustls::{ AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, ClientHello, NoClientAuth, - ServerConfig, ServerSession, Session, + ServerConfig, ServerSession, }; -use rustls::{Certificate, SignatureScheme, SupportedCipherSuite}; use rustls::{ResolvesServerCert, ALL_CIPHERSUITES}; +use rustls::{SignatureScheme, SupportedCipherSuite}; use crate::cipher::{ - rustls_certificate, rustls_certified_key, rustls_client_cert_verifier, - rustls_client_cert_verifier_optional, rustls_supported_ciphersuite, + rustls_certified_key, rustls_client_cert_verifier, rustls_client_cert_verifier_optional, + rustls_supported_ciphersuite, }; -use crate::connection; +use crate::connection::{rustls_connection, Connection}; use crate::enums::rustls_tls_version_from_u16; -use crate::error::rustls_io_result; +use crate::error::rustls_result; use crate::error::rustls_result::{InvalidParameter, NullParameter}; -use crate::error::{map_error, rustls_result}; -use crate::io::{rustls_read_callback, rustls_write_callback, ReadCallback, WriteCallback}; 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_with_incref_from_raw, ffi_panic_boundary, is_close_notify, try_callback, try_mut_from_ptr, - try_mut_slice, try_ref_from_ptr, try_slice, userdata_get, userdata_push, CastPtr, + arc_with_incref_from_raw, ffi_panic_boundary, try_mut_from_ptr, try_mut_slice, + try_ref_from_ptr, try_slice, userdata_get, CastPtr, }; /// A server config being constructed. A builder can be modified by, @@ -66,19 +63,6 @@ impl CastPtr for rustls_server_config { type RustType = ServerConfig; } -pub struct rustls_server_session { - _private: [u8; 0], -} - -pub(crate) struct Sess { - session: ServerSession, - userdata: *mut c_void, -} - -impl CastPtr for rustls_server_session { - type RustType = Sess; -} - /// 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. @@ -318,7 +302,7 @@ pub extern "C" fn rustls_server_config_builder_build( /// "Free" a server_config previously returned from /// rustls_server_config_builder_build. Since server_config is actually an -/// atomically reference-counted pointer, extant server_sessions may still +/// 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. @@ -333,15 +317,16 @@ pub extern "C" fn rustls_server_config_free(config: *const rustls_server_config) } } -/// Create a new rustls::ServerSession, and return it in the output parameter `out`. -/// If this returns an error code, the memory pointed to by `session_out` remains unchanged. -/// If this returns a non-error, the memory pointed to by `session_out` is modified to point -/// at a valid ServerSession. The caller now owns the ServerSession and must call -/// `rustls_server_session_free` when done with it. +/// 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 `session_out` remains unchanged. If this returns a non-error, +/// the memory pointed to by `session_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_session_new( +pub extern "C" fn rustls_server_connection_new( config: *const rustls_server_config, - session_out: *mut *mut rustls_server_session, + conn_out: *mut *mut rustls_connection, ) -> rustls_result { ffi_panic_boundary! { let config: Arc<ServerConfig> = unsafe { @@ -354,259 +339,15 @@ pub extern "C" fn rustls_server_session_new( // We've succeeded. Put the server on the heap, and transfer ownership // to the caller. After this point, we must return CRUSTLS_OK so the // caller knows it is responsible for this memory. - let c = Sess { - session: ServerSession::new(&config), - userdata: null_mut(), - }; + let c = Connection::from_server(ServerSession::new(&config)); unsafe { - *session_out = Box::into_raw(Box::new(c)) as *mut _; + *conn_out = Box::into_raw(Box::new(c)) as *mut _; } return rustls_result::Ok; } } -/// Set the userdata pointer associated with this session. This will be passed -/// to any callbacks invoked by the session, if you've set up callbacks in the config. -/// The pointed-to data must outlive the session. -#[no_mangle] -pub extern "C" fn rustls_server_session_set_userdata( - session: *mut rustls_server_session, - userdata: *mut c_void, -) { - let session: &mut Sess = try_mut_from_ptr!(session); - session.userdata = userdata; -} - -#[no_mangle] -pub extern "C" fn rustls_server_session_wants_read(session: *const rustls_server_session) -> bool { - ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); - session.session.wants_read() - } -} - -#[no_mangle] -pub extern "C" fn rustls_server_session_wants_write(session: *const rustls_server_session) -> bool { - ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); - session.session.wants_write() - } -} - -#[no_mangle] -pub extern "C" fn rustls_server_session_is_handshaking( - session: *const rustls_server_session, -) -> bool { - ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); - session.session.is_handshaking() - } -} - -/// 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/0.19.1/rustls/trait.Session.html#tymethod.get_protocol_version -#[no_mangle] -pub extern "C" fn rustls_server_session_get_protocol_version( - session: *const rustls_server_session, -) -> u16 { - ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); - match session.session.get_protocol_version() { - Some(v) => v.get_u16(), - None => 0 - } - } -} - -/// Return the i-th certificate provided by the client. If no client -/// certificate was exchanged during the handshake, this will always -/// return NULL. -/// Otherwise, this will return the chain, starting with the end entity -/// certificate at index 0, followed by the chain provided. -#[no_mangle] -pub extern "C" fn rustls_server_session_get_peer_certificate( - session: *const rustls_server_session, - i: size_t, -) -> *const rustls_certificate { - ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); - match session.session.get_peer_certificates() { - Some(v) => match v.get(i) { - Some(cert) => cert as *const Certificate as *const _, - None => null() - }, - None => null() - } - } -} - -#[no_mangle] -pub extern "C" fn rustls_server_session_process_new_packets( - session: *mut rustls_server_session, -) -> rustls_result { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - let guard = match userdata_push(session.userdata) { - Ok(g) => g, - Err(_) => return rustls_result::Panic, - }; - let result = match session.session.process_new_packets() { - Ok(()) => rustls_result::Ok, - Err(e) => map_error(e), - }; - match guard.try_drop() { - Ok(()) => result, - Err(_) => return rustls_result::Panic, - } - } -} - -/// Queues a close_notify fatal alert to be sent in the next write_tls call. -/// https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.send_close_notify -#[no_mangle] -pub extern "C" fn rustls_server_session_send_close_notify(session: *mut rustls_server_session) { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - session.session.send_close_notify() - } -} - -/// Free a server_session previously returned from rustls_server_session_new. -/// Calling with NULL is fine. Must not be called twice with the same value. -#[no_mangle] -pub extern "C" fn rustls_server_session_free(session: *mut rustls_server_session) { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - // Convert the pointer to a Box and drop it. - unsafe { Box::from_raw(session); } - } -} - -/// Write up to `count` plaintext bytes from `buf` into the ServerSession. -/// This will increase the number of output bytes available to -/// `rustls_server_session_write_tls`. -/// On success, store the number of bytes actually written in *out_n -/// (this may be less than `count`). -/// https://docs.rs/rustls/0.19.0/rustls/struct.ServerSession.html#method.write -#[no_mangle] -pub extern "C" fn rustls_server_session_write( - session: *mut rustls_server_session, - buf: *const u8, - count: size_t, - out_n: *mut size_t, -) -> rustls_result { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - let write_buf: &[u8] = try_slice!(buf, count); - let out_n: &mut size_t = unsafe { - match out_n.as_mut() { - Some(out_n) => out_n, - None => return NullParameter, - } - }; - let n_written: usize = match session.session.write(write_buf) { - Ok(n) => n, - Err(_) => return rustls_result::Io, - }; - *out_n = n_written; - rustls_result::Ok - } -} - -/// Read up to `count` plaintext bytes from the ServerSession 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_server_session_read_tls and -/// rustls_server_session_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/0.19.0/rustls/struct.ServerSession.html#method.read -#[no_mangle] -pub extern "C" fn rustls_server_session_read( - session: *mut rustls_server_session, - buf: *mut u8, - count: size_t, - out_n: *mut size_t, -) -> rustls_result { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - let read_buf: &mut [u8] = try_mut_slice!(buf, count); - let out_n: &mut size_t = try_mut_from_ptr!(out_n); - - let n_read: usize = match session.session.read(read_buf) { - Ok(n) => n, - // Rustls turns close_notify alerts into `io::Error` of kind `ConnectionAborted`. - // https://docs.rs/rustls/0.19.0/rustls/struct.ClientSession.html#impl-Read. - Err(e) if is_close_notify(&e) => { - return rustls_result::AlertCloseNotify; - } - Err(_) => return rustls_result::Io, - }; - *out_n = n_read; - rustls_result::Ok - } -} - -/// 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_client_session_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/0.19.0/rustls/trait.Session.html#tymethod.read_tls -#[no_mangle] -pub extern "C" fn rustls_server_session_read_tls( - session: *mut rustls_server_session, - callback: rustls_read_callback, - userdata: *mut c_void, - out_n: *mut size_t, -) -> rustls_io_result { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - let out_n: &mut size_t = try_mut_from_ptr!(out_n); - let callback: ReadCallback = try_callback!(callback); - - connection::read_tls(&mut session.session, callback, userdata, out_n) - } -} - -/// 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_client_session_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/0.19.0/rustls/trait.Session.html#tymethod.write_tls -#[no_mangle] -pub extern "C" fn rustls_server_session_write_tls( - session: *mut rustls_server_session, - callback: rustls_write_callback, - userdata: *mut c_void, - out_n: *mut size_t, -) -> rustls_io_result { - ffi_panic_boundary! { - let session: &mut Sess = try_mut_from_ptr!(session); - let out_n: &mut size_t = try_mut_from_ptr!(out_n); - let callback: WriteCallback = try_callback!(callback); - - connection::write_tls(&mut session.session, callback, userdata, out_n) - } -} - /// Copy the SNI hostname to `buf` which can hold up to `count` bytes, /// and the length of that hostname in `out_n`. The string is stored in UTF-8 /// with no terminating NUL byte. @@ -615,17 +356,21 @@ pub extern "C" fn rustls_server_session_write_tls( /// because it hasn't been processed yet, or because the client did not send SNI. /// https://docs.rs/rustls/0.19.0/rustls/struct.ServerSession.html#method.get_sni_hostname #[no_mangle] -pub extern "C" fn rustls_server_session_get_sni_hostname( - session: *const rustls_server_session, +pub extern "C" fn rustls_server_connection_get_sni_hostname( + conn: *const rustls_connection, buf: *mut u8, count: size_t, out_n: *mut size_t, ) -> rustls_result { ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); + let conn: &Connection = try_ref_from_ptr!(conn); let write_buf: &mut [u8] = try_mut_slice!(buf, count); let out_n: &mut size_t = try_mut_from_ptr!(out_n); - let sni_hostname = match session.session.get_sni_hostname() { + let server_session = match conn.as_server() { + Some(s) => s, + _ => return rustls_result::InvalidParameter, + }; + let sni_hostname = match server_session.get_sni_hostname() { Some(sni_hostname) => sni_hostname, None => { return rustls_result::Ok @@ -641,22 +386,6 @@ pub extern "C" fn rustls_server_session_get_sni_hostname( } } -/// Retrieves the cipher suite agreed with the peer. -/// This returns NULL until the ciphersuite is agreed. -/// https://docs.rs/rustls/0.19.0/rustls/trait.Session.html#tymethod.get_negotiated_ciphersuite -#[no_mangle] -pub extern "C" fn rustls_server_session_get_negotiated_ciphersuite( - session: *const rustls_server_session, -) -> *const rustls_supported_ciphersuite { - ffi_panic_boundary! { - let session: &Sess = try_ref_from_ptr!(session); - match session.session.get_negotiated_ciphersuite() { - Some(cs) => cs as *const SupportedCipherSuite as *const _, - None => null(), - } - } -} - /// Choose the server certificate to be used for a session based on certificate /// type. Will pick the first CertfiedKey available that is suitable for /// the SignatureSchemes supported by the client. @@ -714,9 +443,9 @@ impl<'a> CastPtr for rustls_client_hello<'a> { 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_server_session` +/// `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_server_session_set_userdata. +/// `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. /// @@ -799,7 +528,7 @@ unsafe impl Send for ClientHelloResolver {} /// Register a callback to be invoked when a session created from this config /// is seeing a TLS ClientHello message. If `userdata` has been set with -/// rustls_server_session_set_userdata, it will be passed to the callback. +/// 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 @@ -869,7 +598,7 @@ fn sigschemes(input: &[u16]) -> Vec<SignatureScheme> { /// 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_server_session_select_certified_key( +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, @@ -900,7 +629,7 @@ pub extern "C" fn rustls_server_session_select_certified_key( /// keys and values are highly sensitive data, containing enough information /// to break the security of the sessions involved. /// -/// If `userdata` has been set with rustls_server_session_set_userdata, it +/// 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] |