summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Hoffman-Andrews <github@hoffman-andrews.com>2021-05-20 17:49:17 -0700
committerGitHub <noreply@github.com>2021-05-20 17:49:17 -0700
commit7d3b1e58e30e8c3dc6b2845947e8229fc68a1e83 (patch)
tree8873b6f9697ab0b3f2016fc31913da6113342263
parent2ff80553c8cf0ba2ee6d78f1119867c2318c50bf (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.md21
-rw-r--r--src/client.rs329
-rw-r--r--src/connection.rs422
-rw-r--r--src/crustls.h464
-rw-r--r--src/main.c48
-rw-r--r--src/server.rs337
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.
*/
diff --git a/src/main.c b/src/main.c
index c63ba13..0f623c7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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]