diff options
author | Alex Crichton <alex@alexcrichton.com> | 2014-09-17 21:23:41 -0700 |
---|---|---|
committer | Alex Crichton <alex@alexcrichton.com> | 2014-09-17 21:23:41 -0700 |
commit | b8ac001faa09b1d692ecd651aa0ca121afb40b0e (patch) | |
tree | a3578b6c8a8c7826b6cf05a1120a96adc1ff330d /src/session.rs | |
download | ssh2-rs-b8ac001faa09b1d692ecd651aa0ca121afb40b0e.zip |
Initial commit
Bind a Session type (no I/O yet)
Diffstat (limited to 'src/session.rs')
-rw-r--r-- | src/session.rs | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/src/session.rs b/src/session.rs new file mode 100644 index 0000000..421ec44 --- /dev/null +++ b/src/session.rs @@ -0,0 +1,236 @@ +use std::str; +use std::mem; +use std::raw as stdraw; +use libc; + +use {raw, Error, DisconnectCode, ByApplication, SessionFlag, HostKeyType}; +use MethodType; + +pub struct Session { + raw: *mut raw::LIBSSH2_SESSION, +} + +impl Session { + /// Initializes an SSH session object + pub fn new() -> Option<Session> { + ::init(); + unsafe { + let ret = raw::libssh2_session_init_ex(None, None, None); + if ret.is_null() { return None } + Some(Session { + raw: ret, + }) + } + } + + /// Get the remote banner + /// + /// Once the session has been setup and handshake() has completed + /// successfully, this function can be used to get the server id from the + /// banner each server presents. + /// + /// May return `None` on invalid utf-8 or if an error has ocurred. + pub fn banner(&self) -> Option<&str> { + self.banner_bytes().and_then(str::from_utf8) + } + + /// See `banner`. + /// + /// Will only return `None` if an error has ocurred. + pub fn banner_bytes(&self) -> Option<&[u8]> { + unsafe { ::opt_bytes(self, raw::libssh2_session_banner_get(self.raw)) } + } + + /// Set the SSH protocol banner for the local client + /// + /// Set the banner that will be sent to the remote host when the SSH session + /// is started with handshake(). This is optional; a banner + /// corresponding to the protocol and libssh2 version will be sent by + /// default. + pub fn set_banner(&mut self, banner: &str) -> Result<(), Error> { + let banner = banner.to_c_str(); + unsafe { + self.rc(raw::libssh2_session_banner_set(self.raw, banner.as_ptr())) + } + } + + /// Terminate the transport layer. + /// + /// Send a disconnect message to the remote host associated with session, + /// along with a reason symbol and a verbose description. + pub fn disconnect(&mut self, + reason: Option<DisconnectCode>, + description: &str, + lang: Option<&str>) -> Result<(), Error> { + let reason = reason.unwrap_or(ByApplication) as libc::c_int; + let description = description.to_c_str(); + let lang = lang.unwrap_or("").to_c_str(); + unsafe { + self.rc(raw::libssh2_session_disconnect_ex(self.raw, + reason, + description.as_ptr(), + lang.as_ptr())) + } + } + + /// Enable or disable a flag for this session. + pub fn flag(&mut 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)) + } + } + + /// Returns whether the session was previously set to nonblocking. + pub fn is_blocking(&self) -> bool { + unsafe { raw::libssh2_session_get_blocking(self.raw) != 0 } + } + + /// Set or clear blocking mode on session + /// + /// Set or clear blocking mode on the selected on the session. This will + /// instantly affect any channels associated with this session. If a read + /// is performed on a session with no data currently available, a blocking + /// session will wait for data to arrive and return what it receives. A + /// non-blocking session will return immediately with an empty buffer. If a + /// write is performed on a session with no room for more data, a blocking + /// session will wait for room. A non-blocking session will return + /// immediately without writing anything. + pub fn set_blocking(&mut self, blocking: bool) { + unsafe { + raw::libssh2_session_set_blocking(self.raw, blocking as libc::c_int) + } + } + + /// Returns the timeout, in milliseconds, for how long blocking calls may + /// wait until they time out. + /// + /// A timeout of 0 signifies no timeout. + pub fn timeout(&self) -> uint { + unsafe { raw::libssh2_session_get_timeout(self.raw) as uint } + } + + /// Set timeout for blocking functions. + /// + /// Set the timeout in milliseconds for how long a blocking the libssh2 + /// function calls may wait until they consider the situation an error and + /// return an error. + /// + /// By default or if you set the timeout to zero, libssh2 has no timeout + /// for blocking functions. + pub fn set_timeout(&mut self, timeout_ms: uint) { + let timeout_ms = timeout_ms as libc::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)> { + let mut len = 0; + let mut kind = 0; + unsafe { + let ret = raw::libssh2_session_hostkey(self.raw, &mut len, &mut kind); + if ret.is_null() { return None } + let data: &[u8] = mem::transmute(stdraw::Slice { + data: ret as *const u8, + len: len as uint, + }); + let kind = match kind { + raw::LIBSSH2_HOSTKEY_TYPE_RSA => ::TypeRsa, + raw::LIBSSH2_HOSTKEY_TYPE_DSS => ::TypeDss, + _ => ::TypeUnknown, + }; + Some((data, kind)) + } + } + + /// Set preferred key exchange method + /// + /// The preferences provided are a comma delimited list of preferred methods + /// to use with the most preferred listed first and the least preferred + /// listed last. If a method is listed which is not supported by libssh2 it + /// will be ignored and not sent to the remote host during protocol + /// negotiation. + pub fn method_pref(&mut self, + method_type: MethodType, + prefs: &str) -> Result<(), Error> { + let prefs = prefs.to_c_str(); + unsafe { + self.rc(raw::libssh2_session_method_pref(self.raw, + method_type as libc::c_int, + prefs.as_ptr())) + } + } + + /// Return the currently active algorithms. + /// + /// Returns the actual method negotiated for a particular transport + /// parameter. May return `None` if the session has not yet been started. + pub fn methods(&self, method_type: MethodType) -> Option<&str> { + unsafe { + let ptr = raw::libssh2_session_methods(self.raw, + method_type as libc::c_int); + ::opt_bytes(self, ptr).and_then(str::from_utf8) + } + } + + /// 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 mut ret = Vec::new(); + unsafe { + let mut ptr = 0 as *mut _; + let rc = raw::libssh2_session_supported_algs(self.raw, method_type, + &mut ptr); + if rc <= 0 { try!(self.rc(rc)) } + 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); + } + Ok(ret) + } + + /// Gain access to the underlying raw libssh2 session pointer. + pub fn raw(&self) -> *mut raw::LIBSSH2_SESSION { self.raw } + + fn rc(&self, rc: libc::c_int) -> Result<(), Error> { + if rc == 0 { + Ok(()) + } else { + Err(Error(rc as int)) + } + } +} + +impl Drop for Session { + fn drop(&mut self) { + unsafe { + assert_eq!(raw::libssh2_session_free(self.raw), 0); + } + } +} + +#[cfg(test)] +mod tests { + use Session; + + #[test] + fn smoke() { + let mut 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(); + } +} |