summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libssh2-sys/lib.rs82
-rw-r--r--src/agent.rs2
-rw-r--r--src/knownhosts.rs234
-rw-r--r--src/lib.rs28
-rw-r--r--src/session.rs39
-rw-r--r--tests/all.rs1
-rw-r--r--tests/channel.rs7
-rw-r--r--tests/knownhosts.rs40
-rw-r--r--tests/session.rs1
9 files changed, 433 insertions, 1 deletions
diff --git a/libssh2-sys/lib.rs b/libssh2-sys/lib.rs
index 44ea38b..a8f5c16 100644
--- a/libssh2-sys/lib.rs
+++ b/libssh2-sys/lib.rs
@@ -92,10 +92,31 @@ pub static LIBSSH2_ERROR_ENCRYPT: c_int = -44;
pub static LIBSSH2_ERROR_BAD_SOCKET: c_int = -45;
pub static LIBSSH2_ERROR_KNOWN_HOSTS: c_int = -46;
+pub static LIBSSH2_HOSTKEY_HASH_MD5: c_int = 1;
+pub static LIBSSH2_HOSTKEY_HASH_SHA1: c_int = 2;
+
+pub static LIBSSH2_KNOWNHOST_FILE_OPENSSH: c_int = 1;
+
+pub static LIBSSH2_KNOWNHOST_CHECK_MATCH: c_int = 0;
+pub static LIBSSH2_KNOWNHOST_CHECK_MISMATCH: c_int = 1;
+pub static LIBSSH2_KNOWNHOST_CHECK_NOTFOUND: c_int = 2;
+pub static LIBSSH2_KNOWNHOST_CHECK_FAILURE: c_int = 3;
+
+pub static LIBSSH2_KNOWNHOST_TYPE_PLAIN: c_int = 1;
+pub static LIBSSH2_KNOWNHOST_TYPE_SHA1: c_int = 2;
+pub static LIBSSH2_KNOWNHOST_TYPE_CUSTOM: c_int = 3;
+pub static LIBSSH2_KNOWNHOST_KEYENC_RAW: c_int = 1 << 16;
+pub static LIBSSH2_KNOWNHOST_KEYENC_BASE64: c_int = 2 << 16;
+pub static LIBSSH2_KNOWNHOST_KEY_RSA1: c_int = 1 << 18;
+pub static LIBSSH2_KNOWNHOST_KEY_SSHRSA: c_int = 2 << 18;
+pub static LIBSSH2_KNOWNHOST_KEY_SSHDSS: c_int = 3 << 18;
+pub static LIBSSH2_KNOWNHOST_KEY_UNKNOWN: c_int = 7 << 18;
+
pub enum LIBSSH2_SESSION {}
pub enum LIBSSH2_AGENT {}
pub enum LIBSSH2_CHANNEL {}
pub enum LIBSSH2_LISTENER {}
+pub enum LIBSSH2_KNOWNHOSTS {}
#[repr(C)]
pub struct libssh2_agent_publickey {
@@ -106,6 +127,15 @@ pub struct libssh2_agent_publickey {
pub comment: *const c_char,
}
+#[repr(C)]
+pub struct libssh2_knownhost {
+ pub magic: c_uint,
+ pub node: *mut c_void,
+ pub name: *mut c_char,
+ pub key: *mut c_char,
+ pub typemask: c_int,
+}
+
pub type LIBSSH2_ALLOC_FUNC = extern fn(size_t, *mut *mut c_void) -> *mut c_void;
pub type LIBSSH2_FREE_FUNC = extern fn(*mut c_void, *mut *mut c_void);
pub type LIBSSH2_REALLOC_FUNC = extern fn(*mut c_void, size_t, *mut *mut c_void)
@@ -133,6 +163,8 @@ extern {
pub fn libssh2_init(flag: c_int) -> c_int;
pub fn libssh2_exit();
pub fn libssh2_free(sess: *mut LIBSSH2_SESSION, ptr: *mut c_void);
+ pub fn libssh2_hostkey_hash(session: *mut LIBSSH2_SESSION,
+ hash_type: c_int) -> *const c_char;
// session
pub fn libssh2_session_init_ex(alloc: Option<LIBSSH2_ALLOC_FUNC>,
@@ -281,4 +313,54 @@ extern {
pub fn libssh2_userauth_list(sess: *mut LIBSSH2_SESSION,
username: *const c_char,
username_len: c_uint) -> *const c_char;
+
+ // knownhost
+ pub fn libssh2_knownhost_free(hosts: *mut LIBSSH2_KNOWNHOSTS);
+ pub fn libssh2_knownhost_addc(hosts: *mut LIBSSH2_KNOWNHOSTS,
+ host: *mut c_char,
+ salt: *mut c_char,
+ key: *mut c_char,
+ keylen: size_t,
+ comment: *const c_char,
+ commentlen: size_t,
+ typemask: c_int,
+ store: *mut *mut libssh2_knownhost) -> c_int;
+ pub fn libssh2_knownhost_check(hosts: *mut LIBSSH2_KNOWNHOSTS,
+ host: *const c_char,
+ key: *const c_char,
+ keylen: size_t,
+ typemask: c_int,
+ knownhost: *mut *mut libssh2_knownhost)
+ -> c_int;
+ pub fn libssh2_knownhost_checkp(hosts: *mut LIBSSH2_KNOWNHOSTS,
+ host: *const c_char,
+ port: c_int,
+ key: *const c_char,
+ keylen: size_t,
+ typemask: c_int,
+ knownhost: *mut *mut libssh2_knownhost)
+ -> c_int;
+ pub fn libssh2_knownhost_del(hosts: *mut LIBSSH2_KNOWNHOSTS,
+ entry: *mut libssh2_knownhost) -> c_int;
+ pub fn libssh2_knownhost_get(hosts: *mut LIBSSH2_KNOWNHOSTS,
+ store: *mut *mut libssh2_knownhost,
+ prev: *mut libssh2_knownhost) -> c_int;
+ pub fn libssh2_knownhost_readfile(hosts: *mut LIBSSH2_KNOWNHOSTS,
+ filename: *const c_char,
+ kind: c_int) -> c_int;
+ pub fn libssh2_knownhost_readline(hosts: *mut LIBSSH2_KNOWNHOSTS,
+ line: *const c_char,
+ len: size_t,
+ kind: c_int) -> c_int;
+ pub fn libssh2_knownhost_writefile(hosts: *mut LIBSSH2_KNOWNHOSTS,
+ filename: *const c_char,
+ kind: c_int) -> c_int;
+ pub fn libssh2_knownhost_writeline(hosts: *mut LIBSSH2_KNOWNHOSTS,
+ known: *mut libssh2_knownhost,
+ buffer: *mut c_char,
+ buflen: size_t,
+ outlen: *mut size_t,
+ kind: c_int) -> c_int;
+ pub fn libssh2_knownhost_init(sess: *mut LIBSSH2_SESSION)
+ -> *mut LIBSSH2_KNOWNHOSTS;
}
diff --git a/src/agent.rs b/src/agent.rs
index e96f5c8..d3e4ed3 100644
--- a/src/agent.rs
+++ b/src/agent.rs
@@ -21,6 +21,7 @@ pub struct PublicKey<'a> {
marker1: marker::NoSync,
marker2: marker::NoSend,
marker3: marker::ContravariantLifetime<'a>,
+ marker4: marker::NoCopy,
}
impl<'a> Agent<'a> {
@@ -103,6 +104,7 @@ impl<'a> PublicKey<'a> {
marker1: marker::NoSync,
marker2: marker::NoSend,
marker3: marker::ContravariantLifetime,
+ marker4: marker::NoCopy,
}
}
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 }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 329a3a7..5b4e168 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -61,12 +61,14 @@ use std::sync::{Once, ONCE_INIT};
pub use agent::{Agent, Identities, PublicKey};
pub use channel::{Channel, ExitSignal, ReadWindow, WriteWindow};
pub use error::Error;
+pub use knownhosts::{KnownHosts, Hosts, Host};
pub use listener::Listener;
pub use session::Session;
mod agent;
mod channel;
mod error;
+mod knownhosts;
mod listener;
mod session;
@@ -152,3 +154,29 @@ pub enum MethodType {
pub static FlushExtendedData: uint = -1;
pub static FlushAll: uint = -2;
pub static ExtendedDataStderr: uint = 1;
+
+pub enum HashType {
+ HashMd5 = raw::LIBSSH2_HOSTKEY_HASH_MD5 as int,
+ HashSha1 = raw:: LIBSSH2_HOSTKEY_HASH_SHA1 as int,
+}
+
+pub enum KnownHostFileKind {
+ OpenSSH = raw::LIBSSH2_KNOWNHOST_FILE_OPENSSH as int,
+}
+
+pub enum CheckResult {
+ /// Hosts and keys match
+ CheckMatch = raw::LIBSSH2_KNOWNHOST_CHECK_MATCH as int,
+ /// Host was found, but the keys didn't match!
+ CheckMismatch = raw::LIBSSH2_KNOWNHOST_CHECK_MISMATCH as int,
+ /// No host match was found
+ CheckNotFound = raw::LIBSSH2_KNOWNHOST_CHECK_NOTFOUND as int,
+ /// Something prevented the check to be made
+ CheckFailure = raw::LIBSSH2_KNOWNHOST_CHECK_FAILURE as int,
+}
+
+pub enum KnownHostKeyFormat {
+ KeyRsa1 = raw::LIBSSH2_KNOWNHOST_KEY_RSA1 as int,
+ KeySshRsa = raw::LIBSSH2_KNOWNHOST_KEY_SSHRSA as int,
+ KeySshDss = raw::LIBSSH2_KNOWNHOST_KEY_SSHDSS as int,
+}
diff --git a/src/session.rs b/src/session.rs
index 23e3643..07bcf93 100644
--- a/src/session.rs
+++ b/src/session.rs
@@ -5,7 +5,7 @@ use std::str;
use libc::{c_uint, c_int, c_void, c_long};
use {raw, Error, DisconnectCode, ByApplication, SessionFlag, HostKeyType};
-use {MethodType, Agent, Channel, Listener};
+use {MethodType, Agent, Channel, Listener, HashType, KnownHosts};
pub struct Session {
raw: *mut raw::LIBSSH2_SESSION,
@@ -155,6 +155,28 @@ impl Session {
}
}
+ /// Returns the computed digest of the remote system's hostkey.
+ ///
+ /// The bytes returned are the raw hash, and are not printable. If the hash
+ /// is not yet available `None` is returned.
+ pub fn host_key_hash(&self, hash: HashType) -> Option<&[u8]> {
+ let len = match hash {
+ ::HashMd5 => 16,
+ ::HashSha1 => 20,
+ };
+ unsafe {
+ let ret = raw::libssh2_hostkey_hash(self.raw, hash as c_int);
+ if ret.is_null() {
+ None
+ } else {
+ Some(mem::transmute(stdraw::Slice {
+ data: ret as *const u8,
+ len: len,
+ }))
+ }
+ }
+ }
+
/// Set preferred key exchange method
///
/// The preferences provided are a comma delimited list of preferred methods
@@ -377,6 +399,21 @@ impl Session {
Ok(ret as uint)
}
+ /// Init a collection of known hosts for this session.
+ ///
+ /// Returns the handle to an internal representation of a known host
+ /// collection.
+ pub fn known_hosts(&self) -> Result<KnownHosts, Error> {
+ unsafe {
+ let ret = raw::libssh2_knownhost_init(self.raw);
+ if ret.is_null() {
+ Err(Error::last_error(self).unwrap())
+ } else {
+ Ok(KnownHosts::from_raw(self, ret))
+ }
+ }
+ }
+
/// Gain access to the underlying raw libssh2 session pointer.
pub fn raw(&self) -> *mut raw::LIBSSH2_SESSION { self.raw }
diff --git a/tests/all.rs b/tests/all.rs
index fb3bc21..bd5f085 100644
--- a/tests/all.rs
+++ b/tests/all.rs
@@ -9,6 +9,7 @@ use native::io::net::TcpStream;
mod agent;
mod session;
mod channel;
+mod knownhosts;
pub fn socket() -> TcpStream {
let stream = TcpStream::connect(SocketAddr {
diff --git a/tests/channel.rs b/tests/channel.rs
index bce11af..2344cce 100644
--- a/tests/channel.rs
+++ b/tests/channel.rs
@@ -56,8 +56,15 @@ fn shell() {
#[test]
fn setenv() {
let (_tcp, sess) = ::authed_session();
+ {
let mut channel = sess.channel_session().unwrap();
let _ = channel.setenv("FOO", "BAR");
+ channel.close().unwrap();
+ drop(channel);
+ }
+ sess.disconnect(None, "lol", None).unwrap();
+ drop(sess);
+ drop(_tcp);
}
#[test]
diff --git a/tests/knownhosts.rs b/tests/knownhosts.rs
new file mode 100644
index 0000000..b1c1f57
--- /dev/null
+++ b/tests/knownhosts.rs
@@ -0,0 +1,40 @@
+use ssh2::{Session, OpenSSH};
+
+#[test]
+fn smoke() {
+ let sess = Session::new().unwrap();
+ let hosts = sess.known_hosts().unwrap();
+ assert_eq!(hosts.iter().count(), 0);
+}
+
+#[test]
+fn reading() {
+ let encoded = "\
+|1|VXwDpq2cv4j3QtmrGiY+HntJc+Q=|80E+wqnFDhkxBDxRBOIPJPAVE6Y= \
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9I\
+DSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVD\
+BfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eF\
+zLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKS\
+CZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2R\
+PW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi\
+/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
+";
+ let sess = Session::new().unwrap();
+ let mut hosts = sess.known_hosts().unwrap();
+ hosts.read_str(encoded, OpenSSH).unwrap();
+
+ assert_eq!(hosts.iter().count(), 1);
+ let host = hosts.iter().next().unwrap().unwrap();
+ assert_eq!(host.name(), None);
+ assert_eq!(host.key(), "\
+AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9I\
+DSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVD\
+BfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eF\
+zLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKS\
+CZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2R\
+PW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi\
+/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==");
+
+ assert_eq!(hosts.write_string(&host, OpenSSH).unwrap().as_slice(), encoded);
+ hosts.remove(host).unwrap();
+}
diff --git a/tests/session.rs b/tests/session.rs
index c93e514..4c8f907 100644
--- a/tests/session.rs
+++ b/tests/session.rs
@@ -39,6 +39,7 @@ fn smoke_handshake() {
agent.userauth(user.as_slice(), &identity).unwrap();
}
assert!(sess.authenticated());
+ sess.host_key_hash(ssh2::HashMd5).unwrap();
}
#[test]