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::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 library_code(&self) -> libc::c_int {
188 ffi::ERR_GET_LIB(self.code)
189 }
190
191 pub fn function(&self) -> Option<&'static str> {
193 unsafe {
194 let cstr = ffi::ERR_func_error_string(self.code);
195 if cstr.is_null() {
196 return None;
197 }
198 let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
199 Some(str::from_utf8(bytes).unwrap())
200 }
201 }
202
203 pub fn reason(&self) -> Option<&'static str> {
205 unsafe {
206 let cstr = ffi::ERR_reason_error_string(self.code);
207 if cstr.is_null() {
208 return None;
209 }
210 let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
211 Some(str::from_utf8(bytes).unwrap())
212 }
213 }
214
215 pub fn reason_code(&self) -> libc::c_int {
217 ffi::ERR_GET_REASON(self.code)
218 }
219
220 pub fn file(&self) -> &'static str {
222 unsafe {
223 assert!(!self.file.is_null());
224 let bytes = CStr::from_ptr(self.file as *const _).to_bytes();
225 str::from_utf8(bytes).unwrap()
226 }
227 }
228
229 #[allow(clippy::unnecessary_cast)]
231 pub fn line(&self) -> u32 {
232 self.line as u32
233 }
234
235 #[allow(clippy::option_as_ref_deref)]
237 pub fn data(&self) -> Option<&str> {
238 self.data.as_ref().map(|s| &**s)
239 }
240}
241
242impl fmt::Debug for Error {
243 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
244 let mut builder = fmt.debug_struct("Error");
245 builder.field("code", &self.code());
246 if let Some(library) = self.library() {
247 builder.field("library", &library);
248 }
249 builder.field("library_code", &self.library_code());
250 if let Some(function) = self.function() {
251 builder.field("function", &function);
252 }
253 if let Some(reason) = self.reason() {
254 builder.field("reason", &reason);
255 }
256 builder.field("reason_code", &self.reason_code());
257 builder.field("file", &self.file());
258 builder.field("line", &self.line());
259 if let Some(data) = self.data() {
260 builder.field("data", &data);
261 }
262 builder.finish()
263 }
264}
265
266impl fmt::Display for Error {
267 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
268 write!(
269 fmt,
270 "{}\n\nCode: {:08X}\nLoc: {}:{}",
271 self.reason().unwrap_or("unknown TLS error"),
272 self.code(),
273 self.file(),
274 self.line()
275 )
276 }
277}
278
279impl error::Error for Error {}