]> git.proxmox.com Git - cargo.git/blob - vendor/openssl/src/error.rs
New upstream version 0.47.0
[cargo.git] / vendor / openssl / src / error.rs
1 //! Errors returned by OpenSSL library.
2 //!
3 //! OpenSSL errors are stored in an `ErrorStack`. Most methods in the crate
4 //! returns a `Result<T, ErrorStack>` type.
5 //!
6 //! # Examples
7 //!
8 //! ```
9 //! use openssl::error::ErrorStack;
10 //! use openssl::bn::BigNum;
11 //!
12 //! let an_error = BigNum::from_dec_str("Cannot parse letters");
13 //! match an_error {
14 //! Ok(_) => (),
15 //! Err(e) => println!("Parsing Error: {:?}", e),
16 //! }
17 //! ```
18 use libc::{c_char, c_int, c_ulong};
19 use std::borrow::Cow;
20 use std::error;
21 use std::ffi::CStr;
22 use std::fmt;
23 use std::io;
24 use std::ptr;
25 use std::str;
26
27 use ffi;
28
29 /// Collection of [`Error`]s from OpenSSL.
30 ///
31 /// [`Error`]: struct.Error.html
32 #[derive(Debug, Clone)]
33 pub struct ErrorStack(Vec<Error>);
34
35 impl ErrorStack {
36 /// Returns the contents of the OpenSSL error stack.
37 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 /// Pushes the errors back onto the OpenSSL error stack.
46 pub fn put(&self) {
47 for error in self.errors() {
48 error.put();
49 }
50 }
51 }
52
53 impl ErrorStack {
54 /// Returns the errors in the stack.
55 pub fn errors(&self) -> &[Error] {
56 &self.0
57 }
58 }
59
60 impl 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("OpenSSL error");
64 }
65
66 let mut first = true;
67 for err in &self.0 {
68 if !first {
69 fmt.write_str(", ")?;
70 }
71 write!(fmt, "{}", err)?;
72 first = false;
73 }
74 Ok(())
75 }
76 }
77
78 impl error::Error for ErrorStack {}
79
80 impl From<ErrorStack> for io::Error {
81 fn from(e: ErrorStack) -> io::Error {
82 io::Error::new(io::ErrorKind::Other, e)
83 }
84 }
85
86 impl From<ErrorStack> for fmt::Error {
87 fn from(_: ErrorStack) -> fmt::Error {
88 fmt::Error
89 }
90 }
91
92 /// An error reported from OpenSSL.
93 #[derive(Clone)]
94 pub struct Error {
95 code: c_ulong,
96 file: *const c_char,
97 line: c_int,
98 data: Option<Cow<'static, str>>,
99 }
100
101 unsafe impl Sync for Error {}
102 unsafe impl Send for Error {}
103
104 impl Error {
105 /// Returns the first error on the OpenSSL error stack.
106 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 // The memory referenced by data is only valid until that slot is overwritten
118 // in the error stack, so we'll need to copy it off if it's dynamic
119 let data = if flags & ffi::ERR_TXT_STRING != 0 {
120 let bytes = CStr::from_ptr(data as *const _).to_bytes();
121 let data = str::from_utf8(bytes).unwrap();
122 let data = if flags & ffi::ERR_TXT_MALLOCED != 0 {
123 Cow::Owned(data.to_string())
124 } else {
125 Cow::Borrowed(data)
126 };
127 Some(data)
128 } else {
129 None
130 };
131 Some(Error {
132 code,
133 file,
134 line,
135 data,
136 })
137 }
138 }
139 }
140 }
141
142 /// Pushes the error back onto the OpenSSL error stack.
143 pub fn put(&self) {
144 unsafe {
145 ffi::ERR_put_error(
146 ffi::ERR_GET_LIB(self.code),
147 ffi::ERR_GET_FUNC(self.code),
148 ffi::ERR_GET_REASON(self.code),
149 self.file,
150 self.line,
151 );
152 let data = match self.data {
153 Some(Cow::Borrowed(data)) => Some((data.as_ptr() as *mut c_char, 0)),
154 Some(Cow::Owned(ref data)) => {
155 let ptr = ffi::CRYPTO_malloc(
156 (data.len() + 1) as _,
157 concat!(file!(), "\0").as_ptr() as _,
158 line!() as _,
159 ) as *mut c_char;
160 if ptr.is_null() {
161 None
162 } else {
163 ptr::copy_nonoverlapping(data.as_ptr(), ptr as *mut u8, data.len());
164 *ptr.add(data.len()) = 0;
165 Some((ptr, ffi::ERR_TXT_MALLOCED))
166 }
167 }
168 None => None,
169 };
170 if let Some((ptr, flags)) = data {
171 ffi::ERR_set_error_data(ptr, flags | ffi::ERR_TXT_STRING);
172 }
173 }
174 }
175
176 /// Returns the raw OpenSSL error code for this error.
177 pub fn code(&self) -> c_ulong {
178 self.code
179 }
180
181 /// Returns the name of the library reporting the error, if available.
182 pub fn library(&self) -> Option<&'static str> {
183 unsafe {
184 let cstr = ffi::ERR_lib_error_string(self.code);
185 if cstr.is_null() {
186 return None;
187 }
188 let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
189 Some(str::from_utf8(bytes).unwrap())
190 }
191 }
192
193 /// Returns the name of the function reporting the error.
194 pub fn function(&self) -> Option<&'static str> {
195 unsafe {
196 let cstr = ffi::ERR_func_error_string(self.code);
197 if cstr.is_null() {
198 return None;
199 }
200 let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
201 Some(str::from_utf8(bytes).unwrap())
202 }
203 }
204
205 /// Returns the reason for the error.
206 pub fn reason(&self) -> Option<&'static str> {
207 unsafe {
208 let cstr = ffi::ERR_reason_error_string(self.code);
209 if cstr.is_null() {
210 return None;
211 }
212 let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
213 Some(str::from_utf8(bytes).unwrap())
214 }
215 }
216
217 /// Returns the name of the source file which encountered the error.
218 pub fn file(&self) -> &'static str {
219 unsafe {
220 assert!(!self.file.is_null());
221 let bytes = CStr::from_ptr(self.file as *const _).to_bytes();
222 str::from_utf8(bytes).unwrap()
223 }
224 }
225
226 /// Returns the line in the source file which encountered the error.
227 pub fn line(&self) -> u32 {
228 self.line as u32
229 }
230
231 /// Returns additional data describing the error.
232 #[allow(clippy::option_as_ref_deref)]
233 pub fn data(&self) -> Option<&str> {
234 self.data.as_ref().map(|s| &**s)
235 }
236 }
237
238 impl fmt::Debug for Error {
239 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
240 let mut builder = fmt.debug_struct("Error");
241 builder.field("code", &self.code());
242 if let Some(library) = self.library() {
243 builder.field("library", &library);
244 }
245 if let Some(function) = self.function() {
246 builder.field("function", &function);
247 }
248 if let Some(reason) = self.reason() {
249 builder.field("reason", &reason);
250 }
251 builder.field("file", &self.file());
252 builder.field("line", &self.line());
253 if let Some(data) = self.data() {
254 builder.field("data", &data);
255 }
256 builder.finish()
257 }
258 }
259
260 impl fmt::Display for Error {
261 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
262 write!(fmt, "error:{:08X}", self.code())?;
263 match self.library() {
264 Some(l) => write!(fmt, ":{}", l)?,
265 None => write!(fmt, ":lib({})", ffi::ERR_GET_LIB(self.code()))?,
266 }
267 match self.function() {
268 Some(f) => write!(fmt, ":{}", f)?,
269 None => write!(fmt, ":func({})", ffi::ERR_GET_FUNC(self.code()))?,
270 }
271 match self.reason() {
272 Some(r) => write!(fmt, ":{}", r)?,
273 None => write!(fmt, ":reason({})", ffi::ERR_GET_REASON(self.code()))?,
274 }
275 write!(
276 fmt,
277 ":{}:{}:{}",
278 self.file(),
279 self.line(),
280 self.data().unwrap_or("")
281 )
282 }
283 }
284
285 impl error::Error for Error {}