1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use std::error;
use std::ffi::NulError;
use std::fmt;
use std::str;
use libc;

use {raw, Session};
use util::Binding;

/// Representation of an error that can occur within libssh2
#[derive(Debug)]
#[allow(missing_copy_implementations)]
pub struct Error {
    code: libc::c_int,
    msg: &'static str,
}

impl Error {
    /// Generate the last error that occurred for a `Session`.
    ///
    /// Returns `None` if there was no last error.
    pub fn last_error(sess: &Session) -> Option<Error> {
        static STATIC: () = ();
        unsafe {
            let mut msg = 0 as *mut _;
            let rc = raw::libssh2_session_last_error(sess.raw(), &mut msg,
                                                     0 as *mut _, 0);
            if rc == 0 { return None }
            let s = ::opt_bytes(&STATIC, msg).unwrap();
            Some(Error::new(rc, str::from_utf8(s).unwrap()))
        }
    }

    /// Create a new error for the given code and message
    pub fn new(code: libc::c_int, msg: &'static str) -> Error {
        Error {
            code: code,
            msg: msg,
        }
    }

    /// Generate an error that represents EOF
    pub fn eof() -> Error {
        Error::new(raw::LIBSSH2_ERROR_CHANNEL_EOF_SENT, "end of file")
    }

    /// Construct an error from an error code from libssh2
    pub fn from_errno(code: libc::c_int) -> Error {
        let msg = match code {
            raw::LIBSSH2_ERROR_BANNER_RECV => "banner recv failure",
            raw::LIBSSH2_ERROR_BANNER_SEND => "banner send failure",
            raw::LIBSSH2_ERROR_INVALID_MAC => "invalid mac",
            raw::LIBSSH2_ERROR_KEX_FAILURE => "kex failure",
            raw::LIBSSH2_ERROR_ALLOC => "alloc failure",
            raw::LIBSSH2_ERROR_SOCKET_SEND => "socket send faiulre",
            raw::LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE => "key exchange failure",
            raw::LIBSSH2_ERROR_TIMEOUT => "timed out",
            raw::LIBSSH2_ERROR_HOSTKEY_INIT => "hostkey init error",
            raw::LIBSSH2_ERROR_HOSTKEY_SIGN => "hostkey sign error",
            raw::LIBSSH2_ERROR_DECRYPT => "decrypt error",
            raw::LIBSSH2_ERROR_SOCKET_DISCONNECT => "socket disconnected",
            raw::LIBSSH2_ERROR_PROTO => "protocol error",
            raw::LIBSSH2_ERROR_PASSWORD_EXPIRED => "password expired",
            raw::LIBSSH2_ERROR_FILE => "file error",
            raw::LIBSSH2_ERROR_METHOD_NONE => "bad method name",
            raw::LIBSSH2_ERROR_AUTHENTICATION_FAILED => "authentication failed",
            raw::LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED => "public key unverified",
            raw::LIBSSH2_ERROR_CHANNEL_OUTOFORDER => "channel out of order",
            raw::LIBSSH2_ERROR_CHANNEL_FAILURE => "channel failure",
            raw::LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED => "request denied",
            raw::LIBSSH2_ERROR_CHANNEL_UNKNOWN => "unknown channel error",
            raw::LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED => "window exceeded",
            raw::LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED => "packet exceeded",
            raw::LIBSSH2_ERROR_CHANNEL_CLOSED => "closed channel",
            raw::LIBSSH2_ERROR_CHANNEL_EOF_SENT => "eof sent",
            raw::LIBSSH2_ERROR_SCP_PROTOCOL => "scp protocol error",
            raw::LIBSSH2_ERROR_ZLIB => "zlib error",
            raw::LIBSSH2_ERROR_SOCKET_TIMEOUT => "socket timeout",
            raw::LIBSSH2_ERROR_SFTP_PROTOCOL => "sftp protocol error",
            raw::LIBSSH2_ERROR_REQUEST_DENIED => "request denied",
            raw::LIBSSH2_ERROR_METHOD_NOT_SUPPORTED => "method not supported",
            raw::LIBSSH2_ERROR_INVAL => "invalid",
            raw::LIBSSH2_ERROR_INVALID_POLL_TYPE => "invalid poll type",
            raw::LIBSSH2_ERROR_PUBLICKEY_PROTOCOL => "public key protocol error",
            raw::LIBSSH2_ERROR_EAGAIN => "operation would block",
            raw::LIBSSH2_ERROR_BUFFER_TOO_SMALL => "buffer too small",
            raw::LIBSSH2_ERROR_BAD_USE => "bad use error",
            raw::LIBSSH2_ERROR_COMPRESS => "compression error",
            raw::LIBSSH2_ERROR_OUT_OF_BOUNDARY => "out of bounds",
            raw::LIBSSH2_ERROR_AGENT_PROTOCOL => "invalid agent protocol",
            raw::LIBSSH2_ERROR_SOCKET_RECV => "error receiving on socket",
            raw::LIBSSH2_ERROR_ENCRYPT => "bad encrypt",
            raw::LIBSSH2_ERROR_BAD_SOCKET => "bad socket",
            raw::LIBSSH2_ERROR_KNOWN_HOSTS => "known hosts error",
            _ => "unknown error"
        };
        Error::new(code, msg)
    }

    /// Get the message corresponding to this error
    pub fn message(&self) -> &str { self.msg }

    /// Return the code for this error
    pub fn code(&self) -> libc::c_int { self.code }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}] {}", self.code, self.msg)
    }
}

impl error::Error for Error {
    fn description(&self) -> &str { self.message() }
}

impl From<NulError> for Error {
    fn from(_: NulError) -> Error {
        Error::new(raw::LIBSSH2_ERROR_INVAL,
                   "provided data contained a nul byte and could not be used \
                    as as string")
    }
}