diff options
Diffstat (limited to 'src/knownhosts.rs')
-rw-r--r-- | src/knownhosts.rs | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/src/knownhosts.rs b/src/knownhosts.rs new file mode 100644 index 0000000..efdd7ae --- /dev/null +++ b/src/knownhosts.rs @@ -0,0 +1,234 @@ +use std::kinds::marker; +use std::str; +use libc::{c_int, size_t}; + +use {raw, Session, Error, KnownHostFileKind, CheckResult}; + +pub struct KnownHosts<'a> { + raw: *mut raw::LIBSSH2_KNOWNHOSTS, + sess: &'a Session, + marker: marker::NoSync, +} + +pub struct Hosts<'a> { + prev: *mut raw::libssh2_knownhost, + hosts: &'a KnownHosts<'a>, +} + +pub struct Host<'a> { + raw: *mut raw::libssh2_knownhost, + marker1: marker::NoSync, + marker2: marker::NoSend, + marker3: marker::ContravariantLifetime<'a>, + marker4: marker::NoCopy, +} + +impl<'a> KnownHosts<'a> { + /// Wraps a raw pointer in a new KnownHosts structure tied to the lifetime + /// of the given session. + /// + /// This consumes ownership of `raw`. + pub unsafe fn from_raw(sess: &Session, + raw: *mut raw::LIBSSH2_KNOWNHOSTS) -> KnownHosts { + KnownHosts { + raw: raw, + sess: sess, + marker: marker::NoSync, + } + } + + /// Reads a collection of known hosts from a specified file and adds them to + /// the collection of known hosts. + pub fn read_file(&mut self, file: &Path, kind: KnownHostFileKind) + -> Result<uint, Error> { + let file = file.to_c_str(); + let n = unsafe { + raw::libssh2_knownhost_readfile(self.raw, file.as_ptr(), + kind as c_int) + }; + if n < 0 { try!(self.sess.rc(n)) } + Ok(n as uint) + } + + /// Read a line as if it were from a known hosts file. + pub fn read_str(&mut self, s: &str, kind: KnownHostFileKind) + -> Result<(), Error> { + self.sess.rc(unsafe { + raw::libssh2_knownhost_readline(self.raw, + s.as_ptr() as *const _, + s.len() as size_t, + kind as c_int) + }) + } + + /// Writes all the known hosts to the specified file using the specified + /// file format. + pub fn write_file(&self, file: &Path, kind: KnownHostFileKind) + -> Result<(), Error> { + let file = file.to_c_str(); + let n = unsafe { + raw::libssh2_knownhost_writefile(self.raw, file.as_ptr(), + kind as c_int) + }; + self.sess.rc(n) + } + + /// Converts a single known host to a single line of output for storage, + /// using the 'type' output format. + pub fn write_string(&self, host: &Host, kind: KnownHostFileKind) + -> Result<String, Error> { + let mut v = Vec::with_capacity(128); + loop { + let mut outlen = 0; + unsafe { + let rc = raw::libssh2_knownhost_writeline(self.raw, + host.raw, + v.as_mut_ptr() + as *mut _, + v.capacity() as size_t, + &mut outlen, + kind as c_int); + if rc == raw::LIBSSH2_ERROR_BUFFER_TOO_SMALL { + v.reserve(outlen as uint); + } else { + try!(self.sess.rc(rc)); + v.set_len(outlen as uint); + break + } + } + } + Ok(String::from_utf8(v).unwrap()) + } + + /// Create an iterator over all of the known hosts in this structure. + pub fn iter(&self) -> Hosts { + Hosts { prev: 0 as *mut _, hosts: self } + } + + /// Delete a known host entry from the collection of known hosts. + pub fn remove(&self, host: Host) -> Result<(), Error> { + self.sess.rc(unsafe { + raw::libssh2_knownhost_del(self.raw, host.raw) + }) + } + + /// Checks a host and its associated key against the collection of known + /// hosts, and returns info back about the (partially) matched entry. + /// + /// The host name can be the IP numerical address of the host or the full + /// name. The key must be the raw data of the key. + pub fn check(&self, host: &str, key: &[u8]) -> CheckResult { + self.check_port_(host, -1, key) + } + + /// Same as `check`, but takes a port as well. + pub fn check_port(&self, host: &str, port: u16, key: &[u8]) -> CheckResult { + self.check_port_(host, port as int, key) + } + + fn check_port_(&self, host: &str, port: int, key: &[u8]) -> CheckResult { + let host = host.to_c_str(); + let flags = raw::LIBSSH2_KNOWNHOST_TYPE_PLAIN | + raw::LIBSSH2_KNOWNHOST_KEYENC_RAW; + unsafe { + let rc = raw::libssh2_knownhost_checkp(self.raw, + host.as_ptr(), + port as c_int, + key.as_ptr() as *const _, + key.len() as size_t, + flags, + 0 as *mut _); + match rc { + raw::LIBSSH2_KNOWNHOST_CHECK_MATCH => ::CheckMatch, + raw::LIBSSH2_KNOWNHOST_CHECK_MISMATCH => ::CheckMismatch, + raw::LIBSSH2_KNOWNHOST_CHECK_NOTFOUND => ::CheckNotFound, + _ => ::CheckFailure, + } + } + } + + /// Adds a known host to the collection of known hosts. + /// + /// The host is the host name in plain text. The host name can be the IP + /// numerical address of the host or the full name. If you want to add a key + /// for a specific port number for the given host, you must provide the host + /// name like '[host]:port' with the actual characters '[' and ']' enclosing + /// the host name and a colon separating the host part from the port number. + /// For example: "[host.example.com]:222". + /// + /// The key provided must be the raw key for the host. + pub fn add(&mut self, host: &str, key: &[u8], comment: &str, + fmt: ::KnownHostKeyFormat) + -> Result<(), Error> { + let host = host.to_c_str(); + let flags = raw::LIBSSH2_KNOWNHOST_TYPE_PLAIN | + raw::LIBSSH2_KNOWNHOST_KEYENC_RAW | + (fmt as c_int); + unsafe { + let rc = raw::libssh2_knownhost_addc(self.raw, + host.as_ptr() as *mut _, + 0 as *mut _, + key.as_ptr() as *mut _, + key.len() as size_t, + comment.as_ptr() as *const _, + comment.len() as size_t, + flags, + 0 as *mut _); + self.sess.rc(rc) + } + } +} + +#[unsafe_destructor] +impl<'a> Drop for KnownHosts<'a> { + fn drop(&mut self) { + unsafe { raw::libssh2_knownhost_free(self.raw) } + } +} + +impl<'a> Iterator<Result<Host<'a>, Error>> for Hosts<'a> { + fn next(&mut self) -> Option<Result<Host<'a>, Error>> { + unsafe { + let mut next = 0 as *mut _; + match raw::libssh2_knownhost_get(self.hosts.raw, + &mut next, + self.prev) { + 0 => { self.prev = next; Some(Ok(Host::from_raw(next))) } + 1 => None, + rc => Some(Err(self.hosts.sess.rc(rc).err().unwrap())), + } + } + } +} + +impl<'a> Host<'a> { + pub unsafe fn from_raw<'a>(raw: *mut raw::libssh2_knownhost) + -> Host<'a> { + Host { + raw: raw, + marker1: marker::NoSync, + marker2: marker::NoSend, + marker3: marker::ContravariantLifetime, + marker4: marker::NoCopy, + } + } + + /// This is `None` if no plain text host name exists. + pub fn name(&self) -> Option<&str> { + unsafe { + ::opt_bytes(self, (*self.raw).name as *const _) + .and_then(str::from_utf8) + } + } + + /// Returns the key in base64/printable format + pub fn key(&self) -> &str { + let bytes = unsafe { + ::opt_bytes(self, (*self.raw).key as *const _).unwrap() + }; + str::from_utf8(bytes).unwrap() + } + + /// Gain access to the underlying raw pointer + pub fn raw(&self) -> *mut raw::libssh2_knownhost { self.raw } +} |