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