summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-09-18 13:17:24 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-09-18 13:17:24 -0700
commitc95e6ea1e74f82f1a889302978e37a7baf4073cf (patch)
tree4c09a436ede27b29c712e7151cc17917cd3a9e4f
parent547e6c2679e8c686a51367b95a2bab272c51beb0 (diff)
downloadssh2-rs-c95e6ea1e74f82f1a889302978e37a7baf4073cf.zip
Split things up and write some docs
-rw-r--r--Cargo.toml12
-rw-r--r--libssh2-sys/Cargo.toml14
-rw-r--r--libssh2-sys/lib.rs (renamed from src/raw.rs)55
-rw-r--r--src/agent.rs20
-rw-r--r--src/channel.rs200
-rw-r--r--src/lib.rs60
-rw-r--r--src/session.rs133
-rw-r--r--src/test.rs0
-rw-r--r--tests/agent.rs16
-rw-r--r--tests/all.rs20
-rw-r--r--tests/session.rs42
11 files changed, 502 insertions, 70 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 1e7e7cb..ad3e4fe 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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) }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index b02424a..5010137 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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());
+}