diff --git a/librustls/src/connection.rs b/librustls/src/connection.rs index 55ff4f27..acdf91d3 100644 --- a/librustls/src/connection.rs +++ b/librustls/src/connection.rs @@ -1,3 +1,4 @@ +use std::cmp::min; use std::io::{ErrorKind, Read, Write}; use std::{ffi::c_void, ptr::null}; use std::{ptr::null_mut, slice}; @@ -27,6 +28,7 @@ pub(crate) struct Connection { conn: rustls::Connection, userdata: *mut c_void, log_callback: rustls_log_callback, + last_error_msg: Option, } impl Connection { @@ -35,6 +37,7 @@ impl Connection { conn: conn.into(), userdata: null_mut(), log_callback: None, + last_error_msg: None, } } @@ -43,6 +46,7 @@ impl Connection { conn: conn.into(), userdata: null_mut(), log_callback: None, + last_error_msg: None, } } @@ -142,6 +146,7 @@ impl rustls_connection { ffi_panic_boundary! { let conn = try_mut_from_ptr!(conn); if out_n.is_null() { + conn.last_error_msg = Some("EINVAL".to_owned()); return rustls_io_result(EINVAL); } let callback = try_callback!(callback); @@ -149,7 +154,10 @@ impl rustls_connection { let mut reader = CallbackReader { callback, userdata }; let n_read = match conn.read_tls(&mut reader) { Ok(n) => n, - Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), + Err(e) => { + conn.last_error_msg = Some(format!("{}", e)); + return rustls_io_result(e.raw_os_error().unwrap_or(EIO)); + } }; unsafe { *out_n = n_read; @@ -179,6 +187,7 @@ impl rustls_connection { ffi_panic_boundary! { let conn = try_mut_from_ptr!(conn); if out_n.is_null() { + conn.last_error_msg = Some("EINVAL".to_owned()); return rustls_io_result(EINVAL); } let callback = try_callback!(callback); @@ -186,7 +195,10 @@ impl rustls_connection { let mut writer = CallbackWriter { callback, userdata }; let n_written = match conn.write_tls(&mut writer) { Ok(n) => n, - Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), + Err(e) => { + conn.last_error_msg = Some(format!("{}", e)); + return rustls_io_result(e.raw_os_error().unwrap_or(EIO)); + } }; unsafe { *out_n = n_written; @@ -216,6 +228,7 @@ impl rustls_connection { ffi_panic_boundary! { let conn = try_mut_from_ptr!(conn); if out_n.is_null() { + conn.last_error_msg = Some("EINVAL".to_owned()); return rustls_io_result(EINVAL); } let callback = try_callback!(callback); @@ -223,7 +236,10 @@ impl rustls_connection { let mut writer = VectoredCallbackWriter { callback, userdata }; let n_written = match conn.write_tls(&mut writer) { Ok(n) => n, - Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)), + Err(e) => { + conn.last_error_msg = Some(format!("{}", e)); + return rustls_io_result(e.raw_os_error().unwrap_or(EIO)); + } }; unsafe { *out_n = n_written; @@ -245,15 +261,24 @@ impl rustls_connection { let conn = try_mut_from_ptr!(conn); let guard = match userdata_push(conn.userdata, conn.log_callback) { Ok(g) => g, - Err(_) => return rustls_result::Panic, + Err(e) => { + conn.last_error_msg = Some(format!("{:?}", e)); + return rustls_result::Panic; + } }; let result = match conn.process_new_packets() { Ok(_) => rustls_result::Ok, - Err(e) => map_error(e), + Err(e) => { + conn.last_error_msg = Some(format!("{}", e)); + map_error(e) + } }; match guard.try_drop() { Ok(()) => result, - Err(_) => rustls_result::Panic, + Err(e) => { + conn.last_error_msg = Some(format!("{:?}", e)); + rustls_result::Panic + } } } } @@ -337,7 +362,11 @@ impl rustls_connection { ffi_panic_boundary! { match try_mut_from_ptr!(conn).refresh_traffic_keys() { Ok(_) => rustls_result::Ok, - Err(e) => map_error(e), + Err(e) => { + let conn = try_mut_from_ptr!(conn); + conn.last_error_msg = Some(format!("{}", e)); + map_error(e) + } } } } @@ -508,11 +537,16 @@ impl rustls_connection { let conn = try_mut_from_ptr!(conn); let write_buf = try_slice!(buf, count); if out_n.is_null() { - return rustls_result::NullParameter; + let res = rustls_result::NullParameter; + conn.last_error_msg = Some(format!("{:?}", res)); + return res; } let n_written = match conn.writer().write(write_buf) { Ok(n) => n, - Err(_) => return rustls_result::Io, + Err(e) => { + conn.last_error_msg = Some(format!("{}", e)); + return rustls_result::Io; + } }; unsafe { *out_n = n_written; @@ -543,9 +577,13 @@ impl rustls_connection { ffi_panic_boundary! { let conn = try_mut_from_ptr!(conn); if buf.is_null() { - return rustls_result::NullParameter; + let res = rustls_result::NullParameter; + conn.last_error_msg = Some(format!("{:?}", res)); + return res; } if out_n.is_null() { + let res = rustls_result::NullParameter; + conn.last_error_msg = Some(format!("{:?}", res)); return rustls_result::NullParameter; } @@ -556,12 +594,17 @@ impl rustls_connection { let n_read = match conn.reader().read(read_buf) { Ok(n) => n, Err(e) if e.kind() == ErrorKind::UnexpectedEof => { + conn.last_error_msg = Some(format!("{}", e)); return rustls_result::UnexpectedEof; } Err(e) if e.kind() == ErrorKind::WouldBlock => { + conn.last_error_msg = Some(format!("{}", e)); return rustls_result::PlaintextEmpty; } - Err(_) => return rustls_result::Io, + Err(e) => { + conn.last_error_msg = Some(format!("{}", e)); + return rustls_result::Io; + } }; unsafe { *out_n = n_read; @@ -594,19 +637,26 @@ impl rustls_connection { ffi_panic_boundary! { let conn = try_mut_from_ptr!(conn); if buf.is_null() || out_n.is_null() { - return rustls_result::NullParameter; + let res = rustls_result::NullParameter; + conn.last_error_msg = Some(format!("{:?}", res)); + return res; } let mut read_buf: std::io::BorrowedBuf<'_> = try_slice_mut!(buf, count).into(); let n_read = match conn.reader().read_buf(read_buf.unfilled()) { Ok(()) => read_buf.filled().len(), Err(e) if e.kind() == ErrorKind::UnexpectedEof => { + conn.last_error_msg = Some(format!("{}", e)); return rustls_result::UnexpectedEof; } Err(e) if e.kind() == ErrorKind::WouldBlock => { + conn.last_error_msg = Some(format!("{}", e)); return rustls_result::PlaintextEmpty; } - Err(_) => return rustls_result::Io, + Err(e) => { + conn.last_error_msg = Some(format!("{}", e)); + return rustls_result::Io; + } }; unsafe { *out_n = n_read; @@ -632,6 +682,41 @@ impl rustls_connection { } } + /// Read up to `count` bytes of the last error message into `buf`. + /// If there is any error message, store the number of bytes in *out_n and + /// returns `true`. If there is no last error message, stores 0 in *out_n + /// and returns `false`. + #[no_mangle] + pub extern "C" fn rustls_connection_last_error_msg( + conn: &mut rustls_connection, + buf: *mut u8, + count: size_t, + out_n: *mut size_t, + ) -> bool { + ffi_panic_boundary! { + let conn = try_ref_from_ptr!(conn); + match &conn.last_error_msg { + Some(msg) => { + let n = min(msg.len(), count); + if n > 0 { + let buf = try_slice_mut!(buf, n); + buf.copy_from_slice(msg.as_bytes()); + } + unsafe { + *out_n = n; + } + true + } + None => { + unsafe { + *out_n = 0; + } + false + } + } + } + } + /// Free a rustls_connection. Calling with NULL is fine. /// Must not be called twice with the same value. #[no_mangle] diff --git a/librustls/src/rustls.h b/librustls/src/rustls.h index 34dd0f28..0dbc3c27 100644 --- a/librustls/src/rustls.h +++ b/librustls/src/rustls.h @@ -1868,6 +1868,17 @@ rustls_result rustls_connection_read_2(struct rustls_connection *conn, */ bool rustls_connection_fips(const struct rustls_connection *conn); +/** + * Read up to `count` bytes of the last error message into `buf`. + * If there is any error message, store the number of bytes in *out_n and + * returns `true`. If there is no last error message, stores 0 in *out_n + * and returns `false`. + */ +bool rustls_connection_last_error_msg(struct rustls_connection *conn, + uint8_t *buf, + size_t count, + size_t *out_n); + /** * Free a rustls_connection. Calling with NULL is fine. * Must not be called twice with the same value.