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. get() -> ErrorStack37 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. put(&self)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. errors(&self) -> &[Error]55 pub fn errors(&self) -> &[Error] { 56 &self.0 57 } 58 } 59 60 impl fmt::Display for ErrorStack { fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result61 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 { from(e: ErrorStack) -> io::Error81 fn from(e: ErrorStack) -> io::Error { 82 io::Error::new(io::ErrorKind::Other, e) 83 } 84 } 85 86 impl From<ErrorStack> for fmt::Error { from(_: ErrorStack) -> fmt::Error87 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. get() -> Option<Error>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. put(&self)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. code(&self) -> c_ulong177 pub fn code(&self) -> c_ulong { 178 self.code 179 } 180 181 /// Returns the name of the library reporting the error, if available. library(&self) -> Option<&'static str>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. function(&self) -> Option<&'static str>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. reason(&self) -> Option<&'static str>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. file(&self) -> &'static str218 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. line(&self) -> u32227 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)] data(&self) -> Option<&str>233 pub fn data(&self) -> Option<&str> { 234 self.data.as_ref().map(|s| &**s) 235 } 236 } 237 238 impl fmt::Debug for Error { fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result239 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 { fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result261 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 {} 286