boring_imp/
error.rs

1//! Errors returned by OpenSSL library.
2//!
3//! OpenSSL errors are stored in an `ErrorStack`.  Most methods in the crate
4//! returns a `Result<T, ErrorStack>` type.
5//!
6//! # Examples
7//!
8//! ```
9//! use boring::error::ErrorStack;
10//! use boring::bn::BigNum;
11//!
12//! let an_error = BigNum::from_dec_str("Cannot parse letters");
13//! match an_error {
14//!     Ok(_)  => (),
15//!     Err(e) => println!("Parsing Error: {:?}", e),
16//! }
17//! ```
18use libc::{c_char, c_uint};
19use std::borrow::Cow;
20use std::error;
21use std::ffi::CStr;
22use std::fmt;
23use std::io;
24use std::ptr;
25use std::str;
26
27use crate::ffi;
28
29/// Collection of [`Error`]s from OpenSSL.
30///
31/// [`Error`]: struct.Error.html
32#[derive(Debug, Clone)]
33pub struct ErrorStack(Vec<Error>);
34
35impl ErrorStack {
36    /// Returns the contents of the OpenSSL error stack.
37    pub fn get() -> ErrorStack {
38        let mut vec = vec![];
39        while let Some(err) = Error::get() {
40            vec.push(err);
41        }
42        ErrorStack(vec)
43    }
44
45    /// Pushes the errors back onto the OpenSSL error stack.
46    pub fn put(&self) {
47        for error in self.errors() {
48            error.put();
49        }
50    }
51}
52
53impl ErrorStack {
54    /// Returns the errors in the stack.
55    pub fn errors(&self) -> &[Error] {
56        &self.0
57    }
58}
59
60impl fmt::Display for ErrorStack {
61    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
62        if self.0.is_empty() {
63            return fmt.write_str("unknown BoringSSL error");
64        }
65
66        let mut first = true;
67        for err in &self.0 {
68            if !first {
69                fmt.write_str(" ")?;
70            }
71            first = false;
72            write!(fmt, "[{}]", err.reason().unwrap_or("unknown reason"))?;
73        }
74        Ok(())
75    }
76}
77
78impl error::Error for ErrorStack {}
79
80impl From<ErrorStack> for io::Error {
81    fn from(e: ErrorStack) -> io::Error {
82        io::Error::new(io::ErrorKind::Other, e)
83    }
84}
85
86impl From<ErrorStack> for fmt::Error {
87    fn from(_: ErrorStack) -> fmt::Error {
88        fmt::Error
89    }
90}
91
92/// An error reported from OpenSSL.
93#[derive(Clone)]
94pub struct Error {
95    code: c_uint,
96    file: *const c_char,
97    line: c_uint,
98    data: Option<Cow<'static, str>>,
99}
100
101unsafe impl Sync for Error {}
102unsafe impl Send for Error {}
103
104impl Error {
105    /// Returns the first error on the OpenSSL error stack.
106    pub fn get() -> Option<Error> {
107        unsafe {
108            ffi::init();
109
110            let mut file = ptr::null();
111            let mut line = 0;
112            let mut data = ptr::null();
113            let mut flags = 0;
114            match ffi::ERR_get_error_line_data(&mut file, &mut line, &mut data, &mut flags) {
115                0 => None,
116                code => {
117                    // The memory referenced by data is only valid until that slot is overwritten
118                    // in the error stack, so we'll need to copy it off if it's dynamic
119                    let data = if flags & ffi::ERR_FLAG_STRING != 0 {
120                        let bytes = CStr::from_ptr(data as *const _).to_bytes();
121                        let data = str::from_utf8(bytes).unwrap();
122                        let data = Cow::Owned(data.to_string());
123                        Some(data)
124                    } else {
125                        None
126                    };
127                    Some(Error {
128                        code,
129                        file,
130                        line: line as c_uint,
131                        data,
132                    })
133                }
134            }
135        }
136    }
137
138    /// Pushes the error back onto the OpenSSL error stack.
139    pub fn put(&self) {
140        unsafe {
141            ffi::ERR_put_error(
142                ffi::ERR_GET_LIB(self.code),
143                ffi::ERR_GET_FUNC(self.code),
144                ffi::ERR_GET_REASON(self.code),
145                self.file,
146                self.line,
147            );
148            let ptr = match self.data {
149                Some(Cow::Borrowed(data)) => Some(data.as_ptr() as *mut c_char),
150                Some(Cow::Owned(ref data)) => {
151                    let ptr = ffi::OPENSSL_malloc((data.len() + 1) as _) as *mut c_char;
152                    if ptr.is_null() {
153                        None
154                    } else {
155                        ptr::copy_nonoverlapping(data.as_ptr(), ptr as *mut u8, data.len());
156                        *ptr.add(data.len()) = 0;
157                        Some(ptr)
158                    }
159                }
160                None => None,
161            };
162            if let Some(ptr) = ptr {
163                ffi::ERR_add_error_data(1, ptr);
164            }
165        }
166    }
167
168    /// Returns the raw OpenSSL error code for this error.
169    pub fn code(&self) -> c_uint {
170        self.code
171    }
172
173    /// Returns the name of the library reporting the error, if available.
174    pub fn library(&self) -> Option<&'static str> {
175        unsafe {
176            let cstr = ffi::ERR_lib_error_string(self.code);
177            if cstr.is_null() {
178                return None;
179            }
180            let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
181            Some(str::from_utf8(bytes).unwrap())
182        }
183    }
184
185    /// Returns the name of the function reporting the error.
186    pub fn function(&self) -> Option<&'static str> {
187        unsafe {
188            let cstr = ffi::ERR_func_error_string(self.code);
189            if cstr.is_null() {
190                return None;
191            }
192            let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
193            Some(str::from_utf8(bytes).unwrap())
194        }
195    }
196
197    /// Returns the reason for the error.
198    pub fn reason(&self) -> Option<&'static str> {
199        unsafe {
200            let cstr = ffi::ERR_reason_error_string(self.code);
201            if cstr.is_null() {
202                return None;
203            }
204            let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
205            Some(str::from_utf8(bytes).unwrap())
206        }
207    }
208
209    /// Returns the name of the source file which encountered the error.
210    pub fn file(&self) -> &'static str {
211        unsafe {
212            assert!(!self.file.is_null());
213            let bytes = CStr::from_ptr(self.file as *const _).to_bytes();
214            str::from_utf8(bytes).unwrap()
215        }
216    }
217
218    /// Returns the line in the source file which encountered the error.
219    #[allow(clippy::unnecessary_cast)]
220    pub fn line(&self) -> u32 {
221        self.line as u32
222    }
223
224    /// Returns additional data describing the error.
225    #[allow(clippy::option_as_ref_deref)]
226    pub fn data(&self) -> Option<&str> {
227        self.data.as_ref().map(|s| &**s)
228    }
229}
230
231impl fmt::Debug for Error {
232    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
233        let mut builder = fmt.debug_struct("Error");
234        builder.field("code", &self.code());
235        if let Some(library) = self.library() {
236            builder.field("library", &library);
237        }
238        if let Some(function) = self.function() {
239            builder.field("function", &function);
240        }
241        if let Some(reason) = self.reason() {
242            builder.field("reason", &reason);
243        }
244        builder.field("file", &self.file());
245        builder.field("line", &self.line());
246        if let Some(data) = self.data() {
247            builder.field("data", &data);
248        }
249        builder.finish()
250    }
251}
252
253impl fmt::Display for Error {
254    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
255        write!(
256            fmt,
257            "{}\n\nCode: {:08X}\nLoc: {}:{}",
258            self.reason().unwrap_or("unknown TLS error"),
259            self.code(),
260            self.file(),
261            self.line()
262        )
263    }
264}
265
266impl error::Error for Error {}