1use 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#[derive(Debug, Clone)]
33pub struct ErrorStack(Vec<Error>);
34
35impl ErrorStack {
36 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 pub fn put(&self) {
47 for error in self.errors() {
48 error.put();
49 }
50 }
51}
52
53impl ErrorStack {
54 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#[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 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 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 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 pub fn code(&self) -> c_uint {
170 self.code
171 }
172
173 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 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 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 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 #[allow(clippy::unnecessary_cast)]
220 pub fn line(&self) -> u32 {
221 self.line as u32
222 }
223
224 #[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 {}