1use cfg_if::cfg_if;
19use libc::{c_char, c_int};
20use std::borrow::Cow;
21#[cfg(any(boringssl, awslc))]
22use std::convert::TryInto;
23use std::error;
24use std::ffi::CStr;
25use std::fmt;
26use std::io;
27use std::ptr;
28use std::str;
29
30#[cfg(not(any(boringssl, awslc)))]
31type ErrType = libc::c_ulong;
32#[cfg(any(boringssl, awslc))]
33type ErrType = libc::c_uint;
34
35#[derive(Debug, Clone)]
39pub struct ErrorStack(Vec<Error>);
40
41impl ErrorStack {
42 pub fn get() -> ErrorStack {
44 let mut vec = vec![];
45 while let Some(err) = Error::get() {
46 vec.push(err);
47 }
48 ErrorStack(vec)
49 }
50
51 pub fn put(&self) {
53 for error in self.errors() {
54 error.put();
55 }
56 }
57}
58
59impl ErrorStack {
60 pub fn errors(&self) -> &[Error] {
62 &self.0
63 }
64}
65
66impl fmt::Display for ErrorStack {
67 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
68 if self.0.is_empty() {
69 return fmt.write_str("OpenSSL error");
70 }
71
72 let mut first = true;
73 for err in &self.0 {
74 if !first {
75 fmt.write_str(", ")?;
76 }
77 write!(fmt, "{}", err)?;
78 first = false;
79 }
80 Ok(())
81 }
82}
83
84impl error::Error for ErrorStack {}
85
86impl From<ErrorStack> for io::Error {
87 fn from(e: ErrorStack) -> io::Error {
88 io::Error::new(io::ErrorKind::Other, e)
89 }
90}
91
92impl From<ErrorStack> for fmt::Error {
93 fn from(_: ErrorStack) -> fmt::Error {
94 fmt::Error
95 }
96}
97
98#[derive(Clone)]
100pub struct Error {
101 code: ErrType,
102 file: ShimStr,
103 line: c_int,
104 func: Option<ShimStr>,
105 data: Option<Cow<'static, str>>,
106}
107
108unsafe impl Sync for Error {}
109unsafe impl Send for Error {}
110
111impl Error {
112 pub fn get() -> Option<Error> {
114 unsafe {
115 ffi::init();
116
117 let mut file = ptr::null();
118 let mut line = 0;
119 let mut func = ptr::null();
120 let mut data = ptr::null();
121 let mut flags = 0;
122 match ERR_get_error_all(&mut file, &mut line, &mut func, &mut data, &mut flags) {
123 0 => None,
124 code => {
125 let data = if flags & ffi::ERR_TXT_STRING != 0 {
128 let bytes = CStr::from_ptr(data as *const _).to_bytes();
129 let data = str::from_utf8(bytes).unwrap();
130 let data = if flags & ffi::ERR_TXT_MALLOCED != 0 {
131 Cow::Owned(data.to_string())
132 } else {
133 Cow::Borrowed(data)
134 };
135 Some(data)
136 } else {
137 None
138 };
139
140 let file = ShimStr::new(file);
141
142 let func = if func.is_null() {
143 None
144 } else {
145 Some(ShimStr::new(func))
146 };
147
148 Some(Error {
149 code,
150 file,
151 line,
152 func,
153 data,
154 })
155 }
156 }
157 }
158 }
159
160 pub fn put(&self) {
162 self.put_error();
163
164 unsafe {
165 let data = match self.data {
166 Some(Cow::Borrowed(data)) => Some((data.as_ptr() as *mut c_char, 0)),
167 Some(Cow::Owned(ref data)) => {
168 let ptr = ffi::CRYPTO_malloc(
169 (data.len() + 1) as _,
170 concat!(file!(), "\0").as_ptr() as _,
171 line!() as _,
172 ) as *mut c_char;
173 if ptr.is_null() {
174 None
175 } else {
176 ptr::copy_nonoverlapping(data.as_ptr(), ptr as *mut u8, data.len());
177 *ptr.add(data.len()) = 0;
178 Some((ptr, ffi::ERR_TXT_MALLOCED))
179 }
180 }
181 None => None,
182 };
183 if let Some((ptr, flags)) = data {
184 ffi::ERR_set_error_data(ptr, flags | ffi::ERR_TXT_STRING);
185 }
186 }
187 }
188
189 #[cfg(ossl300)]
190 fn put_error(&self) {
191 unsafe {
192 ffi::ERR_new();
193 ffi::ERR_set_debug(
194 self.file.as_ptr(),
195 self.line,
196 self.func.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
197 );
198 ffi::ERR_set_error(self.library_code(), self.reason_code(), ptr::null());
199 }
200 }
201
202 #[cfg(not(ossl300))]
203 fn put_error(&self) {
204 #[cfg(not(any(boringssl, awslc)))]
205 let line = self.line;
206 #[cfg(any(boringssl, awslc))]
207 let line = self.line.try_into().unwrap();
208 unsafe {
209 ffi::ERR_put_error(
210 self.library_code(),
211 ffi::ERR_GET_FUNC(self.code),
212 self.reason_code(),
213 self.file.as_ptr(),
214 line,
215 );
216 }
217 }
218
219 pub fn code(&self) -> ErrType {
221 self.code
222 }
223
224 pub fn library(&self) -> Option<&'static str> {
226 unsafe {
227 let cstr = ffi::ERR_lib_error_string(self.code);
228 if cstr.is_null() {
229 return None;
230 }
231 let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
232 Some(str::from_utf8(bytes).unwrap())
233 }
234 }
235
236 #[allow(unused_unsafe)]
241 pub fn library_code(&self) -> libc::c_int {
242 unsafe { ffi::ERR_GET_LIB(self.code) }
243 }
244
245 pub fn function(&self) -> Option<RetStr<'_>> {
247 self.func.as_ref().map(|s| s.as_str())
248 }
249
250 pub fn reason(&self) -> Option<&'static str> {
252 unsafe {
253 let cstr = ffi::ERR_reason_error_string(self.code);
254 if cstr.is_null() {
255 return None;
256 }
257 let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
258 Some(str::from_utf8(bytes).unwrap())
259 }
260 }
261
262 #[allow(unused_unsafe)]
266 pub fn reason_code(&self) -> libc::c_int {
267 unsafe { ffi::ERR_GET_REASON(self.code) }
268 }
269
270 pub fn file(&self) -> RetStr<'_> {
272 self.file.as_str()
273 }
274
275 pub fn line(&self) -> u32 {
277 self.line as u32
278 }
279
280 #[allow(clippy::option_as_ref_deref)]
282 pub fn data(&self) -> Option<&str> {
283 self.data.as_ref().map(|s| &**s)
284 }
285}
286
287impl fmt::Debug for Error {
288 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
289 let mut builder = fmt.debug_struct("Error");
290 builder.field("code", &self.code());
291 if let Some(library) = self.library() {
292 builder.field("library", &library);
293 }
294 if let Some(function) = self.function() {
295 builder.field("function", &function);
296 }
297 if let Some(reason) = self.reason() {
298 builder.field("reason", &reason);
299 }
300 builder.field("file", &self.file());
301 builder.field("line", &self.line());
302 if let Some(data) = self.data() {
303 builder.field("data", &data);
304 }
305 builder.finish()
306 }
307}
308
309impl fmt::Display for Error {
310 #[allow(unused_unsafe)]
313 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
314 write!(fmt, "error:{:08X}", self.code())?;
315 match self.library() {
316 Some(l) => write!(fmt, ":{}", l)?,
317 None => write!(fmt, ":lib({})", self.library_code())?,
318 }
319 match self.function() {
320 Some(f) => write!(fmt, ":{}", f)?,
321 None => write!(fmt, ":func({})", unsafe { ffi::ERR_GET_FUNC(self.code()) })?,
322 }
323 match self.reason() {
324 Some(r) => write!(fmt, ":{}", r)?,
325 None => write!(fmt, ":reason({})", self.reason_code())?,
326 }
327 write!(
328 fmt,
329 ":{}:{}:{}",
330 self.file(),
331 self.line(),
332 self.data().unwrap_or("")
333 )
334 }
335}
336
337impl error::Error for Error {}
338
339cfg_if! {
340 if #[cfg(ossl300)] {
341 use std::ffi::{CString};
342 use ffi::ERR_get_error_all;
343
344 type RetStr<'a> = &'a str;
345
346 #[derive(Clone)]
347 struct ShimStr(CString);
348
349 impl ShimStr {
350 unsafe fn new(s: *const c_char) -> Self {
351 ShimStr(CStr::from_ptr(s).to_owned())
352 }
353
354 fn as_ptr(&self) -> *const c_char {
355 self.0.as_ptr()
356 }
357
358 fn as_str(&self) -> &str {
359 self.0.to_str().unwrap()
360 }
361 }
362 } else {
363 #[allow(bad_style)]
364 unsafe extern "C" fn ERR_get_error_all(
365 file: *mut *const c_char,
366 line: *mut c_int,
367 func: *mut *const c_char,
368 data: *mut *const c_char,
369 flags: *mut c_int,
370 ) -> ErrType {
371 let code = ffi::ERR_get_error_line_data(file, line, data, flags);
372 *func = ffi::ERR_func_error_string(code);
373 code
374 }
375
376 type RetStr<'a> = &'static str;
377
378 #[derive(Clone)]
379 struct ShimStr(*const c_char);
380
381 impl ShimStr {
382 unsafe fn new(s: *const c_char) -> Self {
383 ShimStr(s)
384 }
385
386 fn as_ptr(&self) -> *const c_char {
387 self.0
388 }
389
390 fn as_str(&self) -> &'static str {
391 unsafe {
392 CStr::from_ptr(self.0).to_str().unwrap()
393 }
394 }
395 }
396 }
397}
398
399#[cfg(test)]
400mod tests {
401 #[cfg(not(ossl310))]
402 use crate::nid::Nid;
403
404 #[test]
405 #[cfg(not(ossl310))]
407 fn test_error_library_code() {
408 let stack = Nid::create("not-an-oid", "invalid", "invalid").unwrap_err();
409 let errors = stack.errors();
410 #[cfg(not(any(boringssl, awslc)))]
411 assert_eq!(errors[0].library_code(), ffi::ERR_LIB_ASN1);
412 #[cfg(any(boringssl, awslc))]
413 assert_eq!(errors[0].library_code(), ffi::ERR_LIB_OBJ as libc::c_int);
414 }
415}