Commit 26828833 authored by Steven Fackler's avatar Steven Fackler
Browse files

Expose more error information

parent 618cc70d
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -143,6 +143,9 @@ pub const BIO_FLAGS_SHOULD_RETRY: c_int = 0x08;

pub const CRYPTO_LOCK: c_int = 1;

pub const ERR_TXT_MALLOCED: c_int = 0x01;
pub const ERR_TXT_STRING: c_int = 0x02;

pub const ERR_LIB_PEM: c_int = 9;
pub const PEM_R_NO_START_LINE: c_int = 108;

@@ -1542,6 +1545,7 @@ extern {

    pub fn ERR_peek_last_error() -> c_ulong;
    pub fn ERR_get_error() -> c_ulong;
    pub fn ERR_get_error_line_data(file: *mut *const c_char, line: *mut c_int, data: *mut *const c_char, flags: *mut c_int) -> c_ulong;
    pub fn ERR_lib_error_string(err: c_ulong) -> *const c_char;
    pub fn ERR_func_error_string(err: c_ulong) -> *const c_char;
    pub fn ERR_reason_error_string(err: c_ulong) -> *const c_char;
+79 −17
Original line number Diff line number Diff line
use libc::c_ulong;
use libc::{c_ulong, c_char, c_int};
use std::fmt;
use std::error;
use std::ffi::CStr;
use std::io;
use std::str;
use std::ptr;
use std::borrow::Cow;

use ffi;

@@ -62,28 +64,63 @@ impl From<ErrorStack> for fmt::Error {

/// An error reported from OpenSSL.
#[derive(Clone)]
pub struct Error(c_ulong);
pub struct Error {
    code: c_ulong,
    file: *const c_char,
    line: c_int,
    data: Option<Cow<'static, str>>,
}

unsafe impl Sync for Error {}
unsafe impl Send for Error {}

impl Error {
    /// Returns the first error on the OpenSSL error stack.
    pub fn get() -> Option<Error> {
        unsafe {
            ffi::init();

        match unsafe { ffi::ERR_get_error() } {
            let mut file = ptr::null();
            let mut line = 0;
            let mut data = ptr::null();
            let mut flags = 0;
            match ffi::ERR_get_error_line_data(&mut file, &mut line, &mut data, &mut flags) {
                0 => None,
            err => Some(Error(err)),
                code => {
                    // The memory referenced by data is only valid until that slot is overwritten
                    // in the error stack, so we'll need to copy it off if it's dynamic
                    let data = if flags & ffi::ERR_TXT_STRING != 0 {
                        let bytes = CStr::from_ptr(data as *const _).to_bytes();
                        let data = str::from_utf8(bytes).unwrap();
                        let data = if flags & ffi::ERR_TXT_MALLOCED != 0 {
                            Cow::Owned(data.to_string())
                        } else {
                            Cow::Borrowed(data)
                        };
                        Some(data)
                    } else {
                        None
                    };
                    Some(Error {
                        code: code,
                        file: file,
                        line: line,
                        data: data,
                    })
                }
            }
        }
    }

    /// Returns the raw OpenSSL error code for this error.
    pub fn code(&self) -> c_ulong {
        self.0
        self.code
    }

    /// Returns the name of the library reporting the error, if available.
    pub fn library(&self) -> Option<&'static str> {
        unsafe {
            let cstr = ffi::ERR_lib_error_string(self.0);
            let cstr = ffi::ERR_lib_error_string(self.code);
            if cstr.is_null() {
                return None;
            }
@@ -95,7 +132,7 @@ impl Error {
    /// Returns the name of the function reporting the error.
    pub fn function(&self) -> Option<&'static str> {
        unsafe {
            let cstr = ffi::ERR_func_error_string(self.0);
            let cstr = ffi::ERR_func_error_string(self.code);
            if cstr.is_null() {
                return None;
            }
@@ -107,7 +144,7 @@ impl Error {
    /// Returns the reason for the error.
    pub fn reason(&self) -> Option<&'static str> {
        unsafe {
            let cstr = ffi::ERR_reason_error_string(self.0);
            let cstr = ffi::ERR_reason_error_string(self.code);
            if cstr.is_null() {
                return None;
            }
@@ -115,6 +152,25 @@ impl Error {
            Some(str::from_utf8(bytes).unwrap())
        }
    }

    /// Returns the name of the source file which encountered the error.
    pub fn file(&self) -> &'static str {
        unsafe {
            assert!(!self.file.is_null());
            let bytes = CStr::from_ptr(self.file as *const _).to_bytes();
            str::from_utf8(bytes).unwrap()
        }
    }

    /// Returns the line in the source file which encountered the error.
    pub fn line(&self) -> c_int {
        self.line
    }

    /// Returns additional data describing the error.
    pub fn data(&self) -> Option<&str> {
        self.data.as_ref().map(|s| &**s)
    }
}

impl fmt::Debug for Error {
@@ -130,30 +186,36 @@ impl fmt::Debug for Error {
        if let Some(reason) = self.reason() {
            builder.field("reason", &reason);
        }
        builder.field("file", &self.file());
        builder.field("line", &self.line());
        if let Some(data) = self.data() {
            builder.field("data", &data);
        }
        builder.finish()
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(fmt, "error:{:08X}", self.0));
        try!(write!(fmt, "error:{:08X}", self.code()));
        match self.library() {
            Some(l) => try!(write!(fmt, ":{}", l)),
            None => try!(write!(fmt, ":lib({})", ffi::ERR_GET_LIB(self.0))),
            None => try!(write!(fmt, ":lib({})", ffi::ERR_GET_LIB(self.code()))),
        }
        match self.function() {
            Some(f) => try!(write!(fmt, ":{}", f)),
            None => try!(write!(fmt, ":func({})", ffi::ERR_GET_FUNC(self.0))),
            None => try!(write!(fmt, ":func({})", ffi::ERR_GET_FUNC(self.code()))),
        }
        match self.reason() {
            Some(r) => write!(fmt, ":{}", r),
            None => write!(fmt, ":reason({})", ffi::ERR_GET_FUNC(self.0)),
            Some(r) => try!(write!(fmt, ":{}", r)),
            None => try!(write!(fmt, ":reason({})", ffi::ERR_GET_FUNC(self.code()))),
        }
        write!(fmt, ":{}:{}:{}", self.file(), self.line(), self.data().unwrap_or(""))
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        "An OpenSSL error"
        "an OpenSSL error"
    }
}