summaryrefslogtreecommitdiff
path: root/openssl-errors
diff options
context:
space:
mode:
authorSteven Fackler <sfackler@gmail.com>2019-03-10 22:17:52 -0700
committerSteven Fackler <sfackler@gmail.com>2019-03-12 07:55:40 -0700
commitacb629a47fcaa0371e614fe1ea1a39a6bc987f60 (patch)
tree9d065c6ddbdba4dd592f04dbcfcb18c761fc6c8a /openssl-errors
parent546405dc58e0e11d695ba6957aab567f9d559192 (diff)
downloadrust-openssl-acb629a47fcaa0371e614fe1ea1a39a6bc987f60.zip
Add a crate to define custom error libraries
Diffstat (limited to 'openssl-errors')
-rw-r--r--openssl-errors/Cargo.toml13
-rw-r--r--openssl-errors/src/lib.rs213
-rw-r--r--openssl-errors/tests/test.rs54
3 files changed, 280 insertions, 0 deletions
diff --git a/openssl-errors/Cargo.toml b/openssl-errors/Cargo.toml
new file mode 100644
index 00000000..9f08cf78
--- /dev/null
+++ b/openssl-errors/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "openssl-errors"
+version = "0.1.0"
+authors = ["Steven Fackler <sfackler@gmail.com>"]
+edition = "2018"
+
+[dependencies]
+libc = "0.2"
+
+openssl-sys = { version = "0.9.42", path = "../openssl-sys" }
+
+[dev-dependencies]
+openssl = { version = "0.10.19", path = "../openssl" }
diff --git a/openssl-errors/src/lib.rs b/openssl-errors/src/lib.rs
new file mode 100644
index 00000000..01a616d5
--- /dev/null
+++ b/openssl-errors/src/lib.rs
@@ -0,0 +1,213 @@
+use libc::{c_char, c_int};
+use std::ptr;
+use std::borrow::Cow;
+use std::marker::PhantomData;
+
+#[doc(hidden)]
+pub mod export {
+ pub use libc::{c_char, c_int};
+ pub use openssl_sys::{
+ init, ERR_get_next_error_library, ERR_load_strings, ERR_PACK, ERR_STRING_DATA,
+ };
+ pub use std::borrow::Cow;
+ pub use std::option::Option;
+ pub use std::ptr::null;
+ pub use std::sync::Once;
+}
+
+pub trait Library {
+ fn id() -> c_int;
+}
+
+pub struct Function<T>(c_int, PhantomData<T>);
+
+impl<T> Function<T> {
+ #[inline]
+ pub const fn from_raw(raw: c_int) -> Function<T> {
+ Function(raw, PhantomData)
+ }
+
+ #[inline]
+ pub const fn as_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+pub struct Reason<T>(c_int, PhantomData<T>);
+
+impl<T> Reason<T> {
+ #[inline]
+ pub const fn from_raw(raw: c_int) -> Reason<T> {
+ Reason(raw, PhantomData)
+ }
+
+ #[inline]
+ pub const fn as_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+/// This is not considered part of this crate's public API. It is subject to change at any time.
+///
+/// # Safety
+///
+/// `file` and `message` must be null-terminated.
+#[doc(hidden)]
+pub unsafe fn __put_error<T>(
+ func: Function<T>,
+ reason: Reason<T>,
+ file: &'static str,
+ line: u32,
+ message: Option<Cow<'static, str>>,
+) where
+ T: Library,
+{
+ openssl_sys::ERR_put_error(
+ T::id(),
+ func.as_raw(),
+ reason.as_raw(),
+ file.as_ptr() as *const c_char,
+ line as c_int,
+ );
+ let data = match message {
+ Some(Cow::Borrowed(s)) => Some((s.as_ptr() as *const c_char as *mut c_char, 0)),
+ Some(Cow::Owned(s)) => {
+ let ptr = openssl_sys::CRYPTO_malloc(
+ s.len() as _,
+ concat!(file!(), "\0").as_ptr() as *const c_char,
+ line!() as c_int,
+ ) as *mut c_char;
+ if ptr.is_null() {
+ None
+ } else {
+ ptr::copy_nonoverlapping(s.as_ptr(), ptr as *mut u8, s.len());
+ Some((ptr, openssl_sys::ERR_TXT_MALLOCED))
+ }
+ }
+ None => None
+ };
+ if let Some((ptr, flags)) = data {
+ openssl_sys::ERR_set_error_data(ptr, flags | openssl_sys::ERR_TXT_STRING);
+ }
+}
+
+#[macro_export]
+macro_rules! put_error {
+ ($function:expr, $reason:expr) => {
+ unsafe {
+ $crate::__put_error(
+ $function,
+ $reason,
+ concat!(file!(), "\0"),
+ line!(),
+ $crate::export::Option::None,
+ );
+ }
+ };
+ ($function:expr, $reason:expr, $message:expr) => {
+ unsafe {
+ $crate::__put_error(
+ $function,
+ $reason,
+ concat!(file!(), "\0"),
+ line!(),
+ $crate::export::Option::Some($crate::export::Cow::Borrowed(
+ concat!($message, "\0"),
+ )),
+ );
+ }
+ };
+ ($function:expr, $reason:expr, $message:expr, $($args:tt)*) => {
+ unsafe {
+ $crate::__put_error(
+ $function,
+ $reason,
+ concat!(file!(), "\0"),
+ line!(),
+ $crate::export::Option::Some($crate::export::Cow::Owned(
+ format!(concat!($message, "\0"), $($args)*)),
+ ),
+ );
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! openssl_errors {
+ ($(
+ $lib_vis:vis library $lib_name:ident($lib_str:expr) {
+ functions {
+ $(
+ $func_name:ident($func_str:expr);
+ )*
+ }
+
+ reasons {
+ $(
+ $reason_name:ident($reason_str:expr);
+ )*
+ }
+ }
+ )*) => {$(
+ $lib_vis enum $lib_name {}
+
+ impl $crate::Library for $lib_name {
+ fn id() -> $crate::export::c_int {
+ static INIT: $crate::export::Once = $crate::export::Once::new();
+ static mut LIB_NUM: $crate::export::c_int = 0;
+ static mut STRINGS: [$crate::export::ERR_STRING_DATA; 2 + $crate::openssl_errors!(@count $($func_name;)* $($reason_name;)*)] = [
+ $crate::export::ERR_STRING_DATA {
+ error: 0,
+ string: concat!($lib_str, "\0").as_ptr() as *const $crate::export::c_char,
+ },
+ $(
+ $crate::export::ERR_STRING_DATA {
+ error: $crate::export::ERR_PACK(0, $lib_name::$func_name.as_raw(), 0),
+ string: concat!($func_str, "\0").as_ptr() as *const $crate::export::c_char,
+ },
+ )*
+ $(
+ $crate::export::ERR_STRING_DATA {
+ error: $crate::export::ERR_PACK(0, 0, $lib_name::$reason_name.as_raw()),
+ string: concat!($reason_str, "\0").as_ptr() as *const $crate::export::c_char,
+ },
+ )*
+ $crate::export::ERR_STRING_DATA {
+ error: 0,
+ string: $crate::export::null(),
+ }
+ ];
+
+ unsafe {
+ INIT.call_once(|| {
+ $crate::export::init();
+ LIB_NUM = $crate::export::ERR_get_next_error_library();
+ STRINGS[0].error = $crate::export::ERR_PACK(LIB_NUM, 0, 0);
+ $crate::export::ERR_load_strings(LIB_NUM, STRINGS.as_mut_ptr());
+ });
+
+ LIB_NUM
+ }
+ }
+ }
+
+ impl $lib_name {
+ $crate::openssl_errors!(@func_consts $lib_name; 1; $($func_name;)*);
+ $crate::openssl_errors!(@reason_consts $lib_name; 1; $($reason_name;)*);
+ }
+ )*};
+ (@func_consts $lib_name:ident; $n:expr; $name:ident; $($tt:tt)*) => {
+ pub const $name: $crate::Function<$lib_name> = $crate::Function::from_raw($n);
+ $crate::openssl_errors!(@func_consts $lib_name; $n + 1; $($tt)*);
+ };
+ (@func_consts $lib_name:ident; $n:expr;) => {};
+ (@reason_consts $lib_name:ident; $n:expr; $name:ident; $($tt:tt)*) => {
+ pub const $name: $crate::Reason<$lib_name> = $crate::Reason::from_raw($n);
+ $crate::openssl_errors!(@reason_consts $lib_name; $n + 1; $($tt)*);
+ };
+ (@reason_consts $lib_name:ident; $n:expr;) => {};
+ (@count $i:ident; $($tt:tt)*) => {
+ 1 + $crate::openssl_errors!(@count $($tt)*)
+ };
+ (@count) => { 0 };
+}
diff --git a/openssl-errors/tests/test.rs b/openssl-errors/tests/test.rs
new file mode 100644
index 00000000..86dfc3b1
--- /dev/null
+++ b/openssl-errors/tests/test.rs
@@ -0,0 +1,54 @@
+use openssl::error::Error;
+
+openssl_errors::openssl_errors! {
+ library Test("test library") {
+ functions {
+ FOO("function foo");
+ BAR("function bar");
+ }
+
+ reasons {
+ NO_MILK("out of milk");
+ NO_BACON("out of bacon");
+ }
+ }
+}
+
+#[test]
+fn basic() {
+ openssl_errors::put_error!(Test::FOO, Test::NO_MILK);
+
+ let error = Error::get().unwrap();
+ assert_eq!(error.library().unwrap(), "test library");
+ assert_eq!(error.function().unwrap(), "function foo");
+ assert_eq!(error.reason().unwrap(), "out of milk");
+ assert_eq!(error.file(), "openssl-errors/tests/test.rs");
+ assert_eq!(error.line(), 19);
+ assert_eq!(error.data(), None);
+}
+
+#[test]
+fn static_data() {
+ openssl_errors::put_error!(Test::BAR, Test::NO_BACON, "foobar");
+
+ let error = Error::get().unwrap();
+ assert_eq!(error.library().unwrap(), "test library");
+ assert_eq!(error.function().unwrap(), "function bar");
+ assert_eq!(error.reason().unwrap(), "out of bacon");
+ assert_eq!(error.file(), "openssl-errors/tests/test.rs");
+ assert_eq!(error.line(), 32);
+ assert_eq!(error.data(), Some("foobar"));
+}
+
+#[test]
+fn dynamic_data() {
+ openssl_errors::put_error!(Test::BAR, Test::NO_MILK, "hello {}", "world");
+
+ let error = Error::get().unwrap();
+ assert_eq!(error.library().unwrap(), "test library");
+ assert_eq!(error.function().unwrap(), "function bar");
+ assert_eq!(error.reason().unwrap(), "out of milk");
+ assert_eq!(error.file(), "openssl-errors/tests/test.rs");
+ assert_eq!(error.line(), 45);
+ assert_eq!(error.data(), Some("hello world"));
+}