1use crate::libc_types::{c_char, c_int, c_uint};
19use openssl_macros::corresponds;
20use std::borrow::Cow;
21use std::error;
22use std::ffi::CStr;
23use std::ffi::CString;
24use std::fmt;
25use std::io;
26use std::ptr;
27use std::str;
28
29use crate::ffi;
30
31pub use crate::ffi::ErrLib;
32
33#[derive(Debug, Clone)]
37pub struct ErrorStack(Vec<Error>);
38
39impl ErrorStack {
40 #[corresponds(ERR_get_error_line_data)]
45 #[must_use = "Use ErrorStack::clear() to drop the error stack"]
46 pub fn get() -> ErrorStack {
47 let mut vec = vec![];
48 while let Some(err) = Error::get() {
49 vec.push(err);
50 }
51 ErrorStack(vec)
52 }
53
54 #[corresponds(ERR_put_error)]
56 pub fn put(&self) {
57 for error in self.errors() {
58 error.put();
59 }
60 }
61
62 #[cold]
64 pub(crate) fn internal_error(err: impl error::Error) -> Self {
65 Self(vec![Error::new_internal(Data::String(err.to_string()))])
66 }
67
68 #[cold]
70 pub(crate) fn internal_error_str(message: &'static str) -> Self {
71 Self(vec![Error::new_internal(Data::Static(message))])
72 }
73
74 #[corresponds(ERR_clear_error)]
76 pub(crate) fn clear() {
77 unsafe {
78 ffi::ERR_clear_error();
79 }
80 }
81}
82
83impl ErrorStack {
84 #[must_use]
86 pub fn errors(&self) -> &[Error] {
87 &self.0
88 }
89}
90
91impl fmt::Display for ErrorStack {
92 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
93 if self.0.is_empty() {
94 return fmt.write_str("unknown BoringSSL error");
95 }
96
97 let mut first = true;
98 for err in &self.0 {
99 if !first {
100 fmt.write_str(" ")?;
101 }
102 first = false;
103 write!(
104 fmt,
105 "[{}]",
106 err.reason()
107 .or_else(|| err.library())
108 .unwrap_or("unknown reason")
109 )?;
110 }
111 Ok(())
112 }
113}
114
115impl error::Error for ErrorStack {}
116
117impl From<ErrorStack> for io::Error {
118 fn from(e: ErrorStack) -> io::Error {
119 io::Error::other(e)
120 }
121}
122
123impl From<ErrorStack> for fmt::Error {
124 fn from(_: ErrorStack) -> fmt::Error {
125 fmt::Error
126 }
127}
128
129#[derive(Clone)]
131pub struct Error {
132 code: c_uint,
133 file: *const c_char,
134 line: c_uint,
135 data: Data,
136}
137
138#[derive(Clone)]
139enum Data {
140 None,
141 CString(CString),
142 String(String),
143 Static(&'static str),
144}
145
146unsafe impl Sync for Error {}
147unsafe impl Send for Error {}
148
149static BORING_INTERNAL: &CStr = c"boring-rust";
150
151impl Error {
152 #[must_use = "Use ErrorStack::clear() to drop the error stack"]
154 #[corresponds(ERR_get_error_line_data)]
155 pub fn get() -> Option<Error> {
156 unsafe {
157 ffi::init();
158
159 let mut file = ptr::null();
160 let mut line = 0;
161 let mut data = ptr::null();
162 let mut flags = 0;
163 match ffi::ERR_get_error_line_data(&mut file, &mut line, &mut data, &mut flags) {
164 0 => None,
165 code => {
166 let data = if flags & ffi::ERR_FLAG_STRING != 0 {
169 Data::CString(CStr::from_ptr(data.cast()).to_owned())
170 } else {
171 Data::None
172 };
173 Some(Error {
174 code,
175 file,
176 line: line as c_uint,
177 data,
178 })
179 }
180 }
181 }
182 }
183
184 #[corresponds(ERR_put_error)]
186 pub fn put(&self) {
187 unsafe {
188 ffi::ERR_put_error(
189 ffi::ERR_GET_LIB(self.code),
190 ffi::ERR_GET_FUNC(self.code),
191 ffi::ERR_GET_REASON(self.code),
192 self.file,
193 self.line,
194 );
195 if let Some(cstr) = self.data_cstr() {
196 ffi::ERR_add_error_data(1, cstr.as_ptr().cast_mut());
197 }
198 }
199 }
200
201 #[inline]
205 #[must_use]
206 #[track_caller]
207 pub fn library_reason(&self, library_code: ErrLib) -> Option<c_int> {
208 debug_assert!(library_code.0 < ffi::ERR_NUM_LIBS.0);
209 (self.library_code() == library_code.0 as c_int).then_some(self.reason_code())
210 }
211
212 #[must_use]
217 #[deprecated(note = "use library_reason() to compare error codes")]
218 pub fn code(&self) -> c_uint {
219 self.code
220 }
221
222 #[must_use]
224 pub fn library(&self) -> Option<&'static str> {
225 if self.is_internal() {
226 return None;
227 }
228 unsafe {
229 let cstr = ffi::ERR_lib_error_string(self.code);
230 if cstr.is_null() {
231 return None;
232 }
233 CStr::from_ptr(cstr.cast())
234 .to_str()
235 .ok()
236 .filter(|&msg| msg != "unknown library")
237 }
238 }
239
240 #[must_use]
244 pub fn library_code(&self) -> c_int {
245 ffi::ERR_GET_LIB(self.code)
246 }
247
248 pub fn function(&self) -> Option<&'static str> {
250 None
251 }
252
253 #[must_use]
255 pub fn reason(&self) -> Option<&str> {
256 if self.is_internal() {
257 return self.data();
258 }
259 unsafe {
260 let cstr = ffi::ERR_reason_error_string(self.code);
261 if cstr.is_null() {
262 return None;
263 }
264 CStr::from_ptr(cstr.cast()).to_str().ok()
265 }
266 }
267
268 #[must_use]
276 pub fn reason_code(&self) -> c_int {
277 ffi::ERR_GET_REASON(self.code)
278 }
279
280 #[must_use]
282 pub fn file(&self) -> &'static str {
283 unsafe {
284 if self.file.is_null() {
285 return "";
286 }
287 CStr::from_ptr(self.file.cast())
288 .to_str()
289 .unwrap_or_default()
290 }
291 }
292
293 #[allow(clippy::unnecessary_cast)]
297 #[must_use]
298 pub fn line(&self) -> u32 {
299 self.line as u32
300 }
301
302 #[must_use]
304 pub fn data(&self) -> Option<&str> {
305 match &self.data {
306 Data::None => None,
307 Data::CString(cstring) => cstring.to_str().ok(),
308 Data::String(s) => Some(s),
309 Data::Static(s) => Some(s),
310 }
311 }
312
313 #[must_use]
314 fn data_cstr(&self) -> Option<Cow<'_, CStr>> {
315 let s = match &self.data {
316 Data::None => return None,
317 Data::CString(cstr) => return Some(Cow::Borrowed(cstr)),
318 Data::String(s) => s.as_str(),
319 Data::Static(s) => s,
320 };
321 CString::new(s).ok().map(Cow::Owned)
322 }
323
324 fn new_internal(msg: Data) -> Self {
325 Self {
326 code: ffi::ERR_PACK(ffi::ERR_LIB_NONE.0 as _, 0, 0) as _,
327 file: BORING_INTERNAL.as_ptr(),
328 line: 0,
329 data: msg,
330 }
331 }
332
333 fn is_internal(&self) -> bool {
334 std::ptr::eq(self.file, BORING_INTERNAL.as_ptr())
335 }
336}
337
338impl fmt::Debug for Error {
339 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
340 let mut builder = fmt.debug_struct("Error");
341 builder.field("code", &self.code);
342 if !self.is_internal() {
343 if let Some(library) = self.library() {
344 builder.field("library", &library);
345 }
346 builder.field("library_code", &self.library_code());
347 if let Some(reason) = self.reason() {
348 builder.field("reason", &reason);
349 }
350 builder.field("reason_code", &self.reason_code());
351 builder.field("file", &self.file());
352 builder.field("line", &self.line());
353 }
354 if let Some(data) = self.data() {
355 builder.field("data", &data);
356 }
357 builder.finish()
358 }
359}
360
361impl fmt::Display for Error {
362 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
363 write!(
364 fmt,
365 "{}\n\nCode: {:08X}\nLoc: {}:{}",
366 self.reason().unwrap_or("unknown TLS error"),
367 &self.code,
368 self.file(),
369 self.line()
370 )
371 }
372}
373
374impl error::Error for Error {}
375
376#[test]
377fn internal_err() {
378 let e = ErrorStack::internal_error(io::Error::other("hello, boring"));
379 assert_eq!(1, e.errors().len());
380 assert!(e.to_string().contains("hello, boring"), "{e} {e:?}");
381
382 e.put();
383 let e = ErrorStack::get();
384 assert!(e.to_string().contains("hello, boring"), "{e} {e:?}");
385}