diff options
author | Alex Crichton <alex@alexcrichton.com> | 2014-09-18 13:17:24 -0700 |
---|---|---|
committer | Alex Crichton <alex@alexcrichton.com> | 2014-09-18 13:17:24 -0700 |
commit | c95e6ea1e74f82f1a889302978e37a7baf4073cf (patch) | |
tree | 4c09a436ede27b29c712e7151cc17917cd3a9e4f | |
parent | 547e6c2679e8c686a51367b95a2bab272c51beb0 (diff) | |
download | ssh2-rs-c95e6ea1e74f82f1a889302978e37a7baf4073cf.zip |
Split things up and write some docs
-rw-r--r-- | Cargo.toml | 12 | ||||
-rw-r--r-- | libssh2-sys/Cargo.toml | 14 | ||||
-rw-r--r-- | libssh2-sys/lib.rs (renamed from src/raw.rs) | 55 | ||||
-rw-r--r-- | src/agent.rs | 20 | ||||
-rw-r--r-- | src/channel.rs | 200 | ||||
-rw-r--r-- | src/lib.rs | 60 | ||||
-rw-r--r-- | src/session.rs | 133 | ||||
-rw-r--r-- | src/test.rs | 0 | ||||
-rw-r--r-- | tests/agent.rs | 16 | ||||
-rw-r--r-- | tests/all.rs | 20 | ||||
-rw-r--r-- | tests/session.rs | 42 |
11 files changed, 502 insertions, 70 deletions
@@ -3,12 +3,8 @@ version = "0.0.1" authors = ["Alex Crichton <alex@alexcrichton.com>"] -[lib] - name = "ssh2" - doctest = false - -[dependencies.libssh2-static-sys] - git = "https://github.com/alexcrichton/libssh2-static-sys" +[[test]] + name = "all" -[dependencies.link-config] - git = "https://github.com/alexcrichton/link-config" +[dependencies.libssh2-sys] + path = "libssh2-sys" diff --git a/libssh2-sys/Cargo.toml b/libssh2-sys/Cargo.toml new file mode 100644 index 0000000..6201c08 --- /dev/null +++ b/libssh2-sys/Cargo.toml @@ -0,0 +1,14 @@ +[package] + name = "libssh2-sys" + version = "0.0.1" + authors = ["Alex Crichton <alex@alexcrichton.com>"] + +[lib] + name = "libssh2-sys" + path = "lib.rs" + +[dependencies.libssh2-static-sys] + git = "https://github.com/alexcrichton/libssh2-static-sys" + +[dependencies.link-config] + git = "https://github.com/alexcrichton/link-config" diff --git a/src/raw.rs b/libssh2-sys/lib.rs index d24e21a..bd2dd63 100644 --- a/src/raw.rs +++ b/libssh2-sys/lib.rs @@ -1,5 +1,10 @@ +#![feature(phase)] #![allow(bad_style)] +extern crate libc; +#[phase(plugin)] +extern crate "link-config" as link_conifg; + use libc::{c_int, size_t, c_void, c_char, c_long, c_uchar, c_uint}; pub static SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT: c_int = 1; @@ -36,8 +41,12 @@ pub static LIBSSH2_METHOD_COMP_SC: c_int = 7; pub static LIBSSH2_METHOD_LANG_CS: c_int = 8; pub static LIBSSH2_METHOD_LANG_SC: c_int = 9; +pub static LIBSSH2_CHANNEL_PACKET_DEFAULT: c_uint = 32768; +pub static LIBSSH2_CHANNEL_WINDOW_DEFAULT: c_uint = 2 * 1024 * 1024; + pub enum LIBSSH2_SESSION {} pub enum LIBSSH2_AGENT {} +pub enum LIBSSH2_CHANNEL {} #[repr(C)] pub struct libssh2_agent_publickey { @@ -53,6 +62,9 @@ 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) -> *mut c_void; +#[cfg(unix)] pub type libssh2_socket_t = c_int; +#[cfg(windows)] pub type libssh2_socket_t = libc::SOCKET; + #[cfg(unix)] link_config!("libssh2", ["favor_static"]) @@ -68,10 +80,12 @@ extern {} extern {} extern { + // misc 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); + // session pub fn libssh2_session_init_ex(alloc: Option<LIBSSH2_ALLOC_FUNC>, free: Option<LIBSSH2_FREE_FUNC>, realloc: Option<LIBSSH2_REALLOC_FUNC>) @@ -107,7 +121,10 @@ extern { msg: *mut *mut c_char, len: *mut c_int, want_buf: c_int) -> c_int; + pub fn libssh2_session_handshake(sess: *mut LIBSSH2_SESSION, + socket: libssh2_socket_t) -> c_int; + // agent pub fn libssh2_agent_init(sess: *mut LIBSSH2_SESSION) -> *mut LIBSSH2_AGENT; pub fn libssh2_agent_free(agent: *mut LIBSSH2_AGENT); pub fn libssh2_agent_connect(agent: *mut LIBSSH2_AGENT) -> c_int; @@ -120,4 +137,42 @@ extern { pub fn libssh2_agent_userauth(agent: *mut LIBSSH2_AGENT, username: *const c_char, identity: *mut libssh2_agent_publickey) -> c_int; + + // channels + pub fn libssh2_channel_free(chan: *mut LIBSSH2_CHANNEL) -> c_int; + pub fn libssh2_channel_close(chan: *mut LIBSSH2_CHANNEL) -> c_int; + pub fn libssh2_channel_wait_close(chan: *mut LIBSSH2_CHANNEL) -> c_int; + pub fn libssh2_channel_eof(chan: *mut LIBSSH2_CHANNEL) -> c_int; + pub fn libssh2_channel_process_startup(chan: *mut LIBSSH2_CHANNEL, + req: *const c_char, + req_len: c_uint, + msg: *const c_char, + msg_len: c_uint) -> c_int; + pub fn libssh2_channel_flush_ex(chan: *mut LIBSSH2_CHANNEL, + streamid: c_int) -> c_int; + pub fn libssh2_channel_write_ex(chan: *mut LIBSSH2_CHANNEL, + stream_id: c_int, + buf: *mut c_char, + buflen: size_t) -> c_int; + pub fn libssh2_channel_get_exit_signal(chan: *mut LIBSSH2_CHANNEL, + exitsignal: *mut *mut c_char, + exitsignal_len: *mut size_t, + errmsg: *mut *mut c_char, + errmsg_len: *mut size_t, + langtag: *mut *mut c_char, + langtag_len: *mut size_t) -> c_int; + pub fn libssh2_channel_get_exit_status(chan: *mut LIBSSH2_CHANNEL) -> c_int; + pub fn libssh2_channel_open_ex(sess: *mut LIBSSH2_SESSION, + channel_type: *const c_char, + channel_type_len: c_uint, + window_size: c_uint, + packet_size: c_uint, + message: *const c_char, + message_len: c_uint) -> *mut LIBSSH2_CHANNEL; + + // userauth + pub fn libssh2_userauth_authenticated(sess: *mut LIBSSH2_SESSION) -> c_int; + pub fn libssh2_userauth_list(sess: *mut LIBSSH2_SESSION, + username: *const c_char, + username_len: c_uint) -> *const c_char; } diff --git a/src/agent.rs b/src/agent.rs index 5cc5fb6..e96f5c8 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -127,23 +127,3 @@ impl<'a> PublicKey<'a> { /// Gain access to the underlying raw pointer pub fn raw(&self) -> *mut raw::libssh2_agent_publickey { self.raw } } - -#[cfg(test)] -mod tests { - use Session; - - #[test] - fn smoke() { - let sess = Session::new().unwrap(); - let mut agent = sess.agent().unwrap(); - agent.connect().unwrap(); - agent.list_identities().unwrap(); - { - let mut a = agent.identities(); - let i1 = a.next().unwrap().unwrap(); - a.count(); - assert!(agent.userauth("foo", &i1).is_err()); - } - agent.disconnect().unwrap(); - } -} diff --git a/src/channel.rs b/src/channel.rs new file mode 100644 index 0000000..bf1172c --- /dev/null +++ b/src/channel.rs @@ -0,0 +1,200 @@ +use std::io; +use std::kinds::marker; +use std::vec; +use libc::{c_uint, c_int, size_t, c_char, c_void}; + +use {raw, Session, Error}; + +pub struct Channel<'a> { + raw: *mut raw::LIBSSH2_CHANNEL, + sess: &'a Session, + marker: marker::NoSync, +} + +/// Data received from when a program exits with a signal. +pub struct ExitSignal { + /// The exit signal received, if the program did not exit cleanly. Does not + /// contain a SIG prefix + pub exit_signal: Option<String>, + /// Error message provided by the remote server (if any) + pub error_message: Option<String>, + /// Language tag provided by the remote server (if any) + pub lang_tag: Option<String>, +} + +impl<'a> Channel<'a> { + /// Wraps a raw pointer in a new Channel 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_CHANNEL) -> Channel { + Channel { + raw: raw, + sess: sess, + marker: marker::NoSync, + } + } + + /// Close an active data channel. + /// + /// In practice this means sending an SSH_MSG_CLOSE packet to the remote + /// host which serves as instruction that no further data will be sent to + /// it. The remote host may still send data back until it sends its own + /// close message in response. + /// + /// To wait for the remote end to close its connection as well, follow this + /// command with `wait_closed` + pub fn close(&mut self) -> Result<(), Error> { + unsafe { + self.sess.rc(raw::libssh2_channel_close(self.raw)) + } + } + + /// Enter a temporary blocking state until the remote host closes the named + /// channel. + /// + /// Typically sent after `close` in order to examine the exit status. + pub fn wait_close(&mut self) -> Result<(), Error> { + unsafe { self.sess.rc(raw::libssh2_channel_wait_close(self.raw)) } + } + + /// Check if the remote host has sent an EOF status for the selected stream. + pub fn eof(&self) -> bool { + unsafe { raw::libssh2_channel_eof(self.raw) != 0 } + } + + /// Initiate a request on a session type channel. + /// + /// The SSH2 protocol currently defines shell, exec, and subsystem as + /// standard process services. + pub fn process_startup(&mut self, request: &str, message: &str) + -> Result<(), Error> { + unsafe { + let rc = raw::libssh2_channel_process_startup(self.raw, + request.as_ptr() as *const _, request.len() as c_uint, + message.as_ptr() as *const _, message.len() as c_uint); + self.sess.rc(rc) + } + } + + /// Execute a command + pub fn exec(&mut self, command: &str) -> Result<(), Error> { + self.process_startup("exec", command) + } + + /// Flush the read buffer for a given channel instance. + /// + /// Groups of substreams may be flushed by passing on of the following + /// constants + /// + /// * FlushExtendedData - Flush all extended data substreams + /// * FlushAll - Flush all substreams + pub fn flush_stream(&mut self, stream_id: uint) -> Result<(), Error> { + unsafe { + self.sess.rc(raw::libssh2_channel_flush_ex(self.raw, + stream_id as c_int)) + } + } + + /// Flush the stderr buffers. + pub fn flush_stderr(&mut self) -> Result<(), Error> { + self.flush_stream(::ExtendedDataStderr) + } + + /// Write data to a channel stream. + /// + /// All channel streams have one standard I/O substream (stream_id == 0), + /// and may have up to 2^32 extended data streams as identified by the + /// selected stream_id. The SSH2 protocol currently defines a stream ID of 1 + /// to be the stderr substream. + pub fn write_stream(&mut self, stream_id: uint, data: &[u8]) + -> Result<(), Error> { + unsafe { + let rc = raw::libssh2_channel_write_ex(self.raw, + stream_id as c_int, + data.as_ptr() as *mut _, + data.len() as size_t); + self.sess.rc(rc) + } + } + + /// Write data to the channel stderr stream. + pub fn write_stderr(&mut self, data: &[u8]) -> Result<(), Error> { + self.write_stream(::ExtendedDataStderr, data) + } + + /// Get the remote exit signal. + pub fn get_exit_signal(&self) -> Result<ExitSignal, Error> { + unsafe { + let mut sig = 0 as *mut _; + let mut siglen = 0; + let mut msg = 0 as *mut _; + let mut msglen = 0; + let mut lang = 0 as *mut _; + let mut langlen = 0; + let rc = raw::libssh2_channel_get_exit_signal(self.raw, + &mut sig, &mut siglen, + &mut msg, &mut msglen, + &mut lang, + &mut langlen); + try!(self.sess.rc(rc)); + return Ok(ExitSignal { + exit_signal: convert(self, sig, siglen), + error_message: convert(self, msg, msglen), + lang_tag: convert(self, lang, langlen), + }) + } + + unsafe fn convert(chan: &Channel, ptr: *mut c_char, + len: size_t) -> Option<String> { + if ptr.is_null() { return None } + let ret = vec::raw::from_buf(ptr as *const u8, len as uint); + raw::libssh2_free(chan.sess.raw(), ptr as *mut c_void); + String::from_utf8(ret).ok() + } + } + + /// Returns the exit code raised by the process running on the remote host + /// at the other end of the named channel. + /// + /// Note that the exit status may not be available if the remote end has not + /// yet set its status to closed. + pub fn get_exit_status(&self) -> Result<int, Error> { + let ret = unsafe { raw::libssh2_channel_get_exit_status(self.raw) }; + if ret == 0 { + Err(Error::last_error(self.sess).unwrap()) + } else { + Ok(ret as int) + } + } +} + +impl<'a> Writer for Channel<'a> { + fn write(&mut self, buf: &[u8]) -> io::IoResult<()> { + self.write_stream(0, buf).map_err(|e| { + io::IoError { + kind: io::OtherIoError, + desc: "ssh write error", + detail: Some(e.to_string()), + } + }) + } + + fn flush(&mut self) -> io::IoResult<()> { + self.flush_stream(0).map_err(|e| { + io::IoError { + kind: io::OtherIoError, + desc: "ssh write error", + detail: Some(e.to_string()), + } + }) + } +} + +#[unsafe_destructor] +impl<'a> Drop for Channel<'a> { + fn drop(&mut self) { + unsafe { assert_eq!(raw::libssh2_channel_free(self.raw), 0) } + } +} @@ -1,7 +1,56 @@ +//! Rust bindings to libssh2, an SSH client library. +//! +//! This library intends to provide a safe interface to the libssh2 library. It +//! will build the library if it's not available on the local system, and +//! otherwise link to an installed copy. +//! +//! Note that libssh2 only supports SSH *clients*, not SSH *servers*. +//! Additionally it only supports protocol v2, not protocol v1. +//! +//! # Examples +//! +//! ## Inspecting ssh-agent +//! +//! ``` +//! use ssh2::Session; +//! +//! // Almost all APIs require a `Session` to be available +//! let sess = Session::new().unwrap(); +//! let mut agent = sess.agent().unwrap(); +//! +//! // Connect the agent and request a list of identities +//! agent.connect().unwrap(); +//! agent.list_identities().unwrap(); +//! +//! for identity in agent.identities() { +//! let identity = identity.unwrap(); // assume no I/O errors +//! println!("{}", identity.comment()) +//! let pubkey = identity.blob(); +//! } +//! ``` +//! +//! ## Authenticating with ssh-agent +//! +//! ```no_run +//! use ssh2::Session; +//! +//! // Almost all APIs require a `Session` to be available +//! let sess = Session::new().unwrap(); +//! let mut agent = sess.agent().unwrap(); +//! +//! // Try to authenticate with the first identity in the agent. +//! agent.connect().unwrap(); +//! agent.list_identities().unwrap(); +//! let identity = agent.identities().next().unwrap().unwrap(); +//! agent.userauth("foo", &identity).unwrap(); +//! +//! // Make sure we succeeded +//! assert!(sess.authenticated()); +//! ``` + #![feature(phase, unsafe_destructor)] -#[phase(plugin)] -extern crate "link-config" as link_config; +extern crate "libssh2-sys" as raw; extern crate libc; use std::c_str::CString; @@ -10,11 +59,12 @@ use std::rt; use std::sync::{Once, ONCE_INIT}; pub use agent::{Agent, Identities, PublicKey}; +pub use channel::{Channel, ExitSignal}; pub use error::Error; pub use session::Session; -pub mod raw; mod agent; +mod channel; mod error; mod session; @@ -96,3 +146,7 @@ pub enum MethodType { MethodLangCs = raw::LIBSSH2_METHOD_LANG_CS as int, MethodLangSc = raw::LIBSSH2_METHOD_LANG_SC as int, } + +pub static FlushExtendedData: uint = -1; +pub static FlushAll: uint = -2; +pub static ExtendedDataStderr: uint = 1; diff --git a/src/session.rs b/src/session.rs index 5abd3b4..babe233 100644 --- a/src/session.rs +++ b/src/session.rs @@ -2,10 +2,10 @@ use std::kinds::marker; use std::mem; use std::raw as stdraw; use std::str; -use libc; +use libc::{c_uint, c_int, c_void, c_long}; use {raw, Error, DisconnectCode, ByApplication, SessionFlag, HostKeyType}; -use {MethodType, Agent}; +use {MethodType, Agent, Channel}; pub struct Session { raw: *mut raw::LIBSSH2_SESSION, @@ -13,7 +13,7 @@ pub struct Session { } impl Session { - /// Initializes an SSH session object + /// Initializes an SSH session object. pub fn new() -> Option<Session> { ::init(); unsafe { @@ -72,7 +72,7 @@ impl Session { reason: Option<DisconnectCode>, description: &str, lang: Option<&str>) -> Result<(), Error> { - let reason = reason.unwrap_or(ByApplication) as libc::c_int; + let reason = reason.unwrap_or(ByApplication) as c_int; let description = description.to_c_str(); let lang = lang.unwrap_or("").to_c_str(); unsafe { @@ -86,8 +86,8 @@ impl Session { /// Enable or disable a flag for this session. pub fn flag(&self, flag: SessionFlag, enable: bool) -> Result<(), Error> { unsafe { - self.rc(raw::libssh2_session_flag(self.raw, flag as libc::c_int, - enable as libc::c_int)) + self.rc(raw::libssh2_session_flag(self.raw, flag as c_int, + enable as c_int)) } } @@ -108,7 +108,7 @@ impl Session { /// immediately without writing anything. pub fn set_blocking(&self, blocking: bool) { unsafe { - raw::libssh2_session_set_blocking(self.raw, blocking as libc::c_int) + raw::libssh2_session_set_blocking(self.raw, blocking as c_int) } } @@ -129,14 +129,14 @@ impl Session { /// By default or if you set the timeout to zero, libssh2 has no timeout /// for blocking functions. pub fn set_timeout(&self, timeout_ms: uint) { - let timeout_ms = timeout_ms as libc::c_long; + let timeout_ms = timeout_ms as c_long; unsafe { raw::libssh2_session_set_timeout(self.raw, timeout_ms) } } /// Get the remote key. /// /// Returns `None` if something went wrong. - pub fn hostkey(&self) -> Option<(&[u8], HostKeyType)> { + pub fn host_key(&self) -> Option<(&[u8], HostKeyType)> { let mut len = 0; let mut kind = 0; unsafe { @@ -168,7 +168,7 @@ impl Session { let prefs = prefs.to_c_str(); unsafe { self.rc(raw::libssh2_session_method_pref(self.raw, - method_type as libc::c_int, + method_type as c_int, prefs.as_ptr())) } } @@ -180,7 +180,7 @@ impl Session { pub fn methods(&self, method_type: MethodType) -> Option<&str> { unsafe { let ptr = raw::libssh2_session_methods(self.raw, - method_type as libc::c_int); + method_type as c_int); ::opt_bytes(self, ptr).and_then(str::from_utf8) } } @@ -188,7 +188,7 @@ impl Session { /// Get list of supported algorithms. pub fn supported_algs(&self, method_type: MethodType) -> Result<Vec<&'static str>, Error> { - let method_type = method_type as libc::c_int; + let method_type = method_type as c_int; let mut ret = Vec::new(); unsafe { let mut ptr = 0 as *mut _; @@ -198,7 +198,7 @@ impl Session { for i in range(0, rc as int) { ret.push(str::raw::c_str_to_static_slice(*ptr.offset(i))); } - raw::libssh2_free(self.raw, ptr as *mut libc::c_void); + raw::libssh2_free(self.raw, ptr as *mut c_void); } Ok(ret) } @@ -206,13 +206,90 @@ impl Session { /// Init an ssh-agent handle. /// /// The returned agent will still need to be connected manually before use. - pub fn agent(&self) -> Option<Agent> { + pub fn agent(&self) -> Result<Agent, Error> { unsafe { let ptr = raw::libssh2_agent_init(self.raw); if ptr.is_null() { - None + Err(Error::last_error(self).unwrap()) } else { - Some(Agent::from_raw(self, ptr)) + Ok(Agent::from_raw(self, ptr)) + } + } + } + + /// Begin transport layer protocol negotiation with the connected host. + /// + /// The socket provided is a connected socket descriptor. Typically a TCP + /// connection though the protocol allows for any reliable transport and + /// the library will attempt to use any berkeley socket. + pub fn handshake(&self, socket: raw::libssh2_socket_t) -> Result<(), Error> { + unsafe { + self.rc(raw::libssh2_session_handshake(self.raw, socket)) + } + } + + /// Allocate a new channel for exchanging data with the server. + /// + /// This is typically not called directly but rather through + /// `channel_open_session`, `channel_direct_tcpip`, or + /// `channel_forward_listen`. + pub fn channel_open(&self, channel_type: &str, + window_size: uint, packet_size: uint, + message: Option<&str>) -> Result<Channel, Error> { + let ret = unsafe { + let channel_type_len = channel_type.len(); + let channel_type = channel_type.to_c_str(); + let message_len = message.map(|s| s.len()).unwrap_or(0); + let message = message.map(|s| s.to_c_str()); + raw::libssh2_channel_open_ex(self.raw, + channel_type.as_ptr(), + channel_type_len as c_uint, + window_size as c_uint, + packet_size as c_uint, + message.as_ref().map(|s| s.as_ptr()) + .unwrap_or(0 as *const _), + message_len as c_uint) + }; + if ret.is_null() { + Err(Error::last_error(self).unwrap()) + } else { + Ok(unsafe { Channel::from_raw(self, ret) }) + } + } + + /// Establish a new session-based channel. + pub fn channel_session(&self) -> Result<Channel, Error> { + self.channel_open("session", + raw::LIBSSH2_CHANNEL_WINDOW_DEFAULT as uint, + raw::LIBSSH2_CHANNEL_PACKET_DEFAULT as uint, None) + } + + /// Indicates whether or not the named session has been successfully + /// authenticated. + pub fn authenticated(&self) -> bool { + unsafe { raw::libssh2_userauth_authenticated(self.raw) != 0 } + } + + /// Send a SSH_USERAUTH_NONE request to the remote host. + /// + /// Unless the remote host is configured to accept none as a viable + /// authentication scheme (unlikely), it will return SSH_USERAUTH_FAILURE + /// along with a listing of what authentication schemes it does support. In + /// the unlikely event that none authentication succeeds, this method with + /// return NULL. This case may be distinguished from a failing case by + /// examining libssh2_userauth_authenticated. + /// + /// The return value is a comma-separated string of supported auth schemes. + pub fn auth_methods(&self, username: &str) -> Result<&str, Error> { + let len = username.len(); + let username = username.to_c_str(); + unsafe { + let ret = raw::libssh2_userauth_list(self.raw, username.as_ptr(), + len as c_uint); + if ret.is_null() { + Err(Error::last_error(self).unwrap()) + } else { + Ok(str::raw::c_str_to_static_slice(ret)) } } } @@ -221,7 +298,7 @@ impl Session { pub fn raw(&self) -> *mut raw::LIBSSH2_SESSION { self.raw } /// Translate a return code into a Rust-`Result`. - pub fn rc(&self, rc: libc::c_int) -> Result<(), Error> { + pub fn rc(&self, rc: c_int) -> Result<(), Error> { if rc == 0 { Ok(()) } else { @@ -240,25 +317,3 @@ impl Drop for Session { } } } - -#[cfg(test)] -mod tests { - use Session; - - #[test] - fn smoke() { - let sess = Session::new().unwrap(); - assert!(sess.banner_bytes().is_none()); - sess.set_banner("foo").unwrap(); - assert!(sess.is_blocking()); - assert_eq!(sess.timeout(), 0); - sess.flag(::Compress, true).unwrap(); - assert!(sess.hostkey().is_none()); - sess.method_pref(::MethodKex, "diffie-hellman-group14-sha1").unwrap(); - assert!(sess.methods(::MethodKex).is_none()); - sess.set_blocking(true); - sess.set_timeout(0); - sess.supported_algs(::MethodKex).unwrap(); - sess.supported_algs(::MethodHostKey).unwrap(); - } -} diff --git a/src/test.rs b/src/test.rs new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/test.rs diff --git a/tests/agent.rs b/tests/agent.rs new file mode 100644 index 0000000..8feee06 --- /dev/null +++ b/tests/agent.rs @@ -0,0 +1,16 @@ +use ssh2::Session; + +#[test] +fn smoke() { + let sess = Session::new().unwrap(); + let mut agent = sess.agent().unwrap(); + agent.connect().unwrap(); + agent.list_identities().unwrap(); + { + let mut a = agent.identities(); + let i1 = a.next().unwrap().unwrap(); + a.count(); + assert!(agent.userauth("foo", &i1).is_err()); + } + agent.disconnect().unwrap(); +} diff --git a/tests/all.rs b/tests/all.rs new file mode 100644 index 0000000..90473a3 --- /dev/null +++ b/tests/all.rs @@ -0,0 +1,20 @@ +extern crate ssh2; +extern crate native; +extern crate rustrt; + +use rustrt::rtio::{SocketAddr, Ipv4Addr}; +use native::io::net::TcpStream; + +mod agent; +mod session; + +pub fn socket() -> TcpStream { + let stream = TcpStream::connect(SocketAddr { + ip: Ipv4Addr(127, 0, 0, 1), + port: 22, + }, None); + match stream { + Ok(s) => s, + Err(e) => fail!("no socket: [{}]: {}", e.code, e.detail), + } +} diff --git a/tests/session.rs b/tests/session.rs new file mode 100644 index 0000000..5c91cff --- /dev/null +++ b/tests/session.rs @@ -0,0 +1,42 @@ +use std::os; + +use ssh2::{mod, Session}; + +#[test] +fn smoke() { + let sess = Session::new().unwrap(); + assert!(sess.banner_bytes().is_none()); + sess.set_banner("foo").unwrap(); + assert!(sess.is_blocking()); + assert_eq!(sess.timeout(), 0); + sess.flag(ssh2::Compress, true).unwrap(); + assert!(sess.host_key().is_none()); + sess.method_pref(ssh2::MethodKex, "diffie-hellman-group14-sha1").unwrap(); + assert!(sess.methods(ssh2::MethodKex).is_none()); + sess.set_blocking(true); + sess.set_timeout(0); + sess.supported_algs(ssh2::MethodKex).unwrap(); + sess.supported_algs(ssh2::MethodHostKey).unwrap(); + sess.channel_session().err().unwrap(); +} + +#[test] +fn smoke_handshake() { + let user = os::getenv("USER").unwrap(); + let sess = Session::new().unwrap(); + let socket = ::socket(); + sess.handshake(socket.fd()).unwrap(); + sess.host_key().unwrap(); + let methods = sess.auth_methods(user.as_slice()).unwrap(); + assert!(methods.contains("publickey"), "{}", methods); + assert!(!sess.authenticated()); + + let mut agent = sess.agent().unwrap(); + agent.connect().unwrap(); + agent.list_identities().unwrap(); + { + let identity = agent.identities().next().unwrap().unwrap(); + agent.userauth(user.as_slice(), &identity).unwrap(); + } + assert!(sess.authenticated()); +} |