Skip to main content

compio_py_dynamic_openssl/
error.rs

1// This module is mostly copied and modified from:
2// https://github.com/rust-openssl/rust-openssl/blob/openssl-v0.10.75/openssl/src/error.rs
3//
4// SPDX-License-Identifier: Apache-2.0
5// Copyright 2011-2017 Google Inc.
6//           2013 Jack Lloyd
7//           2013-2014 Steven Fackler
8//
9// SPDX-License-Identifier: Apache-2.0 OR MulanPSL-2.0
10// Copyright 2026 Fantix King
11
12use std::{
13    borrow::Cow,
14    error,
15    ffi::{CStr, CString, c_char, c_int, c_ulong},
16    fmt, io, ptr,
17};
18
19use crate::sys as ffi;
20
21type ErrType = c_ulong;
22
23#[derive(Debug, Clone)]
24pub struct ErrorStack(Vec<Error>);
25
26impl ErrorStack {
27    pub fn get() -> Self {
28        let mut vec = vec![];
29        while let Some(err) = Error::get() {
30            vec.push(err);
31        }
32        ErrorStack(vec)
33    }
34
35    pub fn errors(&self) -> &[Error] {
36        &self.0
37    }
38}
39
40impl fmt::Display for ErrorStack {
41    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
42        if self.0.is_empty() {
43            return fmt.write_str("OpenSSL error");
44        }
45
46        let mut first = true;
47        for err in &self.0 {
48            if !first {
49                fmt.write_str(", ")?;
50            }
51            write!(fmt, "{}", err)?;
52            first = false;
53        }
54        Ok(())
55    }
56}
57
58impl error::Error for ErrorStack {}
59
60impl From<ErrorStack> for io::Error {
61    fn from(e: ErrorStack) -> io::Error {
62        io::Error::new(io::ErrorKind::Other, e)
63    }
64}
65
66impl From<ErrorStack> for fmt::Error {
67    fn from(_: ErrorStack) -> fmt::Error {
68        fmt::Error
69    }
70}
71
72#[derive(Clone)]
73pub struct Error {
74    code: ErrType,
75    file: ShimStr,
76    line: c_int,
77    func: Option<ShimStr>,
78    data: Option<Cow<'static, str>>,
79}
80
81unsafe impl Sync for Error {}
82unsafe impl Send for Error {}
83
84impl Error {
85    pub fn get() -> Option<Self> {
86        let ffi = crate::get();
87        unsafe {
88            let mut file = ptr::null();
89            let mut line = 0;
90            let mut func = ptr::null();
91            let mut data = ptr::null();
92            let mut flags = 0;
93            match ffi.ERR_get_error_all(&mut file, &mut line, &mut func, &mut data, &mut flags) {
94                0 => None,
95                code => {
96                    let data = if flags & ffi::ERR_TXT_STRING != 0 {
97                        let bytes = CStr::from_ptr(data as *const _).to_bytes();
98                        let data = str::from_utf8(bytes).unwrap();
99                        let data = if flags & ffi::ERR_TXT_MALLOCED != 0 {
100                            Cow::Owned(data.to_string())
101                        } else {
102                            Cow::Borrowed(data)
103                        };
104                        Some(data)
105                    } else {
106                        None
107                    };
108
109                    let file = ShimStr::new(file);
110
111                    let func = if func.is_null() {
112                        None
113                    } else {
114                        Some(ShimStr::new(func))
115                    };
116
117                    Some(Error {
118                        code,
119                        file,
120                        line,
121                        func,
122                        data,
123                    })
124                }
125            }
126        }
127    }
128
129    pub fn code(&self) -> ErrType {
130        self.code
131    }
132
133    pub fn library(&self) -> Option<&'static str> {
134        let ffi = crate::get();
135        unsafe {
136            let cstr = (ffi.ERR_lib_error_string)(self.code);
137            if cstr.is_null() {
138                return None;
139            }
140            let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
141            Some(str::from_utf8(bytes).unwrap())
142        }
143    }
144
145    pub fn library_code(&self) -> c_int {
146        let ffi = crate::get();
147        ffi.ERR_GET_LIB(self.code)
148    }
149
150    pub fn function(&self) -> Option<&str> {
151        self.func.as_ref().map(|s| s.as_str())
152    }
153
154    pub fn reason(&self) -> Option<&'static str> {
155        let ffi = crate::get();
156        unsafe {
157            let cstr = (ffi.ERR_reason_error_string)(self.code);
158            if cstr.is_null() {
159                return None;
160            }
161            let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
162            Some(str::from_utf8(bytes).unwrap())
163        }
164    }
165
166    pub fn reason_code(&self) -> c_int {
167        let ffi = crate::get();
168        ffi.ERR_GET_REASON(self.code)
169    }
170
171    pub fn file(&self) -> &str {
172        self.file.as_str()
173    }
174
175    pub fn line(&self) -> u32 {
176        self.line as u32
177    }
178
179    pub fn data(&self) -> Option<&str> {
180        self.data.as_ref().map(|s| &**s)
181    }
182}
183
184impl fmt::Debug for Error {
185    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
186        let mut builder = fmt.debug_struct("Error");
187        builder.field("code", &self.code());
188        if let Some(library) = self.library() {
189            builder.field("library", &library);
190        }
191        if let Some(function) = self.function() {
192            builder.field("function", &function);
193        }
194        if let Some(reason) = self.reason() {
195            builder.field("reason", &reason);
196        }
197        builder.field("file", &self.file());
198        builder.field("line", &self.line());
199        if let Some(data) = self.data() {
200            builder.field("data", &data);
201        }
202        builder.finish()
203    }
204}
205
206impl fmt::Display for Error {
207    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
208        write!(fmt, "error:{:08X}", self.code())?;
209        match self.library() {
210            Some(l) => write!(fmt, ":{}", l)?,
211            None => write!(fmt, ":lib({})", self.library_code())?,
212        }
213        match self.function() {
214            Some(f) => write!(fmt, ":{}", f)?,
215            None => write!(fmt, ":func({})", crate::get().ERR_GET_FUNC(self.code()))?,
216        }
217        match self.reason() {
218            Some(r) => write!(fmt, ":{}", r)?,
219            None => write!(fmt, ":reason({})", self.reason_code())?,
220        }
221        write!(
222            fmt,
223            ":{}:{}:{}",
224            self.file(),
225            self.line(),
226            self.data().unwrap_or("")
227        )
228    }
229}
230
231impl error::Error for Error {}
232
233#[derive(Clone)]
234enum ShimStr {
235    Owned(CString),
236    Borrowed(*const c_char),
237}
238
239impl ShimStr {
240    unsafe fn new(s: *const c_char) -> Self {
241        if crate::get().version_num < 0x30000000 {
242            ShimStr::Borrowed(s)
243        } else {
244            ShimStr::Owned(unsafe { CStr::from_ptr(s) }.to_owned())
245        }
246    }
247
248    fn as_str(&self) -> &str {
249        match self {
250            ShimStr::Owned(s) => s.to_str().unwrap(),
251            ShimStr::Borrowed(s) => unsafe { CStr::from_ptr(*s).to_str().unwrap() },
252        }
253    }
254}