distant-ssh2 is compiling

pull/146/head
Chip Senkbeil 2 years ago
parent 7baf2b5092
commit 920c3c5578
No known key found for this signature in database
GPG Key ID: 35EF1F8EC72A4131

@ -1,11 +1,9 @@
mod authenticated;
mod authenticator;
mod handler;
mod keychain;
mod methods;
pub mod msg;
pub use authenticated::*;
pub use authenticator::*;
pub use handler::*;
pub use keychain::*;

@ -1,38 +0,0 @@
use std::ops::{Deref, DerefMut};
/// Wrapper type around `T` that provides compile-time confirmation of being authenticated
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Authenticated<T>(T);
impl<T> Authenticated<T> {
/// Consumes authenticated wrapper and returns the inner value
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> AsRef<T> for Authenticated<T> {
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T> AsMut<T> for Authenticated<T> {
fn as_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T> Deref for Authenticated<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Authenticated<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

@ -197,9 +197,81 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::common::authentication::AuthMethodHandler;
use test_log::test;
use tokio::sync::mpsc;
#[async_trait]
trait TestAuthHandler {
async fn on_initialization(
&mut self,
_: Initialization,
) -> io::Result<InitializationResponse> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_start_method(&mut self, _: StartMethod) -> io::Result<()> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_finished(&mut self) -> io::Result<()> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_challenge(&mut self, _: Challenge) -> io::Result<ChallengeResponse> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_verification(&mut self, _: Verification) -> io::Result<VerificationResponse> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_info(&mut self, _: Info) -> io::Result<()> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_error(&mut self, _: Error) -> io::Result<()> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
}
#[async_trait]
impl<T: TestAuthHandler + Send> AuthHandler for T {
async fn on_initialization(
&mut self,
x: Initialization,
) -> io::Result<InitializationResponse> {
TestAuthHandler::on_initialization(self, x).await
}
async fn on_start_method(&mut self, x: StartMethod) -> io::Result<()> {
TestAuthHandler::on_start_method(self, x).await
}
async fn on_finished(&mut self) -> io::Result<()> {
TestAuthHandler::on_finished(self).await
}
}
#[async_trait]
impl<T: TestAuthHandler + Send> AuthMethodHandler for T {
async fn on_challenge(&mut self, x: Challenge) -> io::Result<ChallengeResponse> {
TestAuthHandler::on_challenge(self, x).await
}
async fn on_verification(&mut self, x: Verification) -> io::Result<VerificationResponse> {
TestAuthHandler::on_verification(self, x).await
}
async fn on_info(&mut self, x: Info) -> io::Result<()> {
TestAuthHandler::on_info(self, x).await
}
async fn on_error(&mut self, x: Error) -> io::Result<()> {
TestAuthHandler::on_error(self, x).await
}
}
macro_rules! auth_handler {
(@no_challenge @no_verification @tx($tx:ident, $ty:ty) $($methods:item)*) => {
auth_handler! {
@ -251,7 +323,7 @@ mod tests {
}
#[async_trait]
impl AuthHandler for __InlineAuthHandler {
impl TestAuthHandler for __InlineAuthHandler {
$($methods)*
}

@ -1,23 +1,17 @@
use super::msg::*;
use crate::common::HeapSecretKey;
use async_trait::async_trait;
use log::*;
use std::collections::HashMap;
use std::io;
/// Interface for a handler of authentication requests.
#[async_trait]
pub trait AuthHandler {
/// Callback when a challenge is received, returning answers to the given questions.
async fn on_challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse>;
/// Callback when a verification request is received, returning true if approvided or false if
/// unapproved.
async fn on_verification(
&mut self,
verification: Verification,
) -> io::Result<VerificationResponse>;
mod methods;
pub use methods::*;
/// Interface for a handler of authentication requests for all methods.
#[async_trait]
pub trait AuthHandler: AuthMethodHandler {
/// Callback when authentication is beginning, providing available authentication methods and
/// returning selected authentication methods to pursue
/// returning selected authentication methods to pursue.
async fn on_initialization(
&mut self,
initialization: Initialization,
@ -27,30 +21,16 @@ pub trait AuthHandler {
})
}
/// Callback when authentication starts for a specific method
/// Callback when authentication starts for a specific method.
#[allow(unused_variables)]
async fn on_start_method(&mut self, start_method: StartMethod) -> io::Result<()> {
Ok(())
}
/// Callback when authentication is finished and no more requests will be received
/// Callback when authentication is finished and no more requests will be received.
async fn on_finished(&mut self) -> io::Result<()> {
Ok(())
}
/// Callback when information is received. To fail, return an error from this function.
#[allow(unused_variables)]
async fn on_info(&mut self, info: Info) -> io::Result<()> {
Ok(())
}
/// Callback when an error is received. Regardless of the result returned, this will terminate
/// the authenticator. In the situation where a custom error would be preferred, have this
/// callback return an error.
#[allow(unused_variables)]
async fn on_error(&mut self, error: Error) -> io::Result<()> {
Ok(())
}
}
/// Dummy implementation of [`AuthHandler`] where any challenge or verification request will
@ -58,7 +38,10 @@ pub trait AuthHandler {
pub struct DummyAuthHandler;
#[async_trait]
impl AuthHandler for DummyAuthHandler {
impl AuthHandler for DummyAuthHandler {}
#[async_trait]
impl AuthMethodHandler for DummyAuthHandler {
async fn on_challenge(&mut self, _: Challenge) -> io::Result<ChallengeResponse> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
@ -66,85 +49,153 @@ impl AuthHandler for DummyAuthHandler {
async fn on_verification(&mut self, _: Verification) -> io::Result<VerificationResponse> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_info(&mut self, _: Info) -> io::Result<()> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_error(&mut self, _: Error) -> io::Result<()> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
}
/// Blocking implementation of [`AuthHandler`] that uses prompts to communicate challenge &
/// verification requests, receiving responses to relay back.
pub struct PromptAuthHandler<T, U> {
text_prompt: T,
password_prompt: U,
/// Implementation of [`AuthHandler`] that maintains a map of [`AuthMethodHandler`] implementations
/// for specific methods, invoking [`on_challenge`], [`on_verification`], [`on_info`], and
/// [`on_error`] for a specific handler based on an associated id.
///
/// [`on_challenge`]: AuthMethodHandler::on_challenge
/// [`on_verification`]: AuthMethodHandler::on_verification
/// [`on_info`]: AuthMethodHandler::on_info
/// [`on_error`]: AuthMethodHandler::on_error
pub struct AuthHandlerMap {
active: String,
map: HashMap<&'static str, Box<dyn AuthMethodHandler + Send>>,
}
impl<T, U> PromptAuthHandler<T, U> {
pub fn new(text_prompt: T, password_prompt: U) -> Self {
impl AuthHandlerMap {
/// Creates a new, empty map of auth method handlers.
pub fn new() -> Self {
Self {
text_prompt,
password_prompt,
active: String::new(),
map: HashMap::new(),
}
}
/// Returns the `id` of the active [`AuthMethodHandler`].
pub fn active_id(&self) -> &str {
&self.active
}
/// Sets the active [`AuthMethodHandler`] by its `id`.
pub fn set_active_id(&mut self, id: impl Into<String>) {
self.active = id.into();
}
/// Inserts the specified `handler` into the map, associating it with `id` for determining the
/// method that would trigger this handler.
pub fn insert_method_handler<T: AuthMethodHandler + Send + 'static>(
&mut self,
id: &'static str,
handler: T,
) -> Option<Box<dyn AuthMethodHandler + Send>> {
self.map.insert(id, Box::new(handler))
}
/// Removes a handler with the associated `id`.
pub fn remove_method_handler(
&mut self,
id: &'static str,
) -> Option<Box<dyn AuthMethodHandler + Send>> {
self.map.remove(id)
}
/// Retrieves a mutable reference to the active [`AuthMethodHandler`] with the specified `id`,
/// returning an error if no handler for the active id is found.
pub fn get_mut_active_method_handler_or_error(
&mut self,
) -> io::Result<&mut (dyn AuthMethodHandler + Send + 'static)> {
self.get_mut_active_method_handler()
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "No active handler for id"))
}
/// Retrieves a mutable reference to the active [`AuthMethodHandler`] with the specified `id`.
pub fn get_mut_active_method_handler(
&mut self,
) -> Option<&mut (dyn AuthMethodHandler + Send + 'static)> {
// TODO: Optimize this
self.get_mut_method_handler(&self.active.clone())
}
/// Retrieves a mutable reference to the [`AuthMethodHandler`] with the specified `id`.
pub fn get_mut_method_handler(
&mut self,
id: &str,
) -> Option<&mut (dyn AuthMethodHandler + Send + 'static)> {
self.map.get_mut(id).map(|h| h.as_mut())
}
}
impl AuthHandlerMap {
/// Consumes the map, returning a new map that supports the `static_key` method.
pub fn with_static_key(mut self, key: impl Into<HeapSecretKey>) -> Self {
self.insert_method_handler("static_key", StaticKeyAuthMethodHandler::simple(key));
self
}
}
impl Default for AuthHandlerMap {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl<T, U> AuthHandler for PromptAuthHandler<T, U>
where
T: Fn(&str) -> io::Result<String> + Send + Sync + 'static,
U: Fn(&str) -> io::Result<String> + Send + Sync + 'static,
{
async fn on_challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse> {
trace!("on_challenge({challenge:?})");
let mut answers = Vec::new();
for question in challenge.questions.iter() {
// Contains all prompt lines including same line
let mut lines = question.text.split('\n').collect::<Vec<_>>();
impl AuthHandler for AuthHandlerMap {
async fn on_initialization(
&mut self,
initialization: Initialization,
) -> io::Result<InitializationResponse> {
let methods = initialization
.methods
.into_iter()
.filter(|method| self.map.contains_key(method.as_str()))
.collect();
// Line that is prompt on same line as answer
let line = lines.pop().unwrap();
Ok(InitializationResponse { methods })
}
// Go ahead and display all other lines
for line in lines.into_iter() {
eprintln!("{}", line);
}
async fn on_start_method(&mut self, start_method: StartMethod) -> io::Result<()> {
self.set_active_id(start_method.method);
Ok(())
}
// Get an answer from user input, or use a blank string as an answer
// if we fail to get input from the user
let answer = (self.password_prompt)(line).unwrap_or_default();
async fn on_finished(&mut self) -> io::Result<()> {
Ok(())
}
}
answers.push(answer);
}
Ok(ChallengeResponse { answers })
#[async_trait]
impl AuthMethodHandler for AuthHandlerMap {
async fn on_challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse> {
let handler = self.get_mut_active_method_handler_or_error()?;
handler.on_challenge(challenge).await
}
async fn on_verification(
&mut self,
verification: Verification,
) -> io::Result<VerificationResponse> {
trace!("on_verify({verification:?})");
match verification.kind {
VerificationKind::Host => {
eprintln!("{}", verification.text);
let answer = (self.text_prompt)("Enter [y/N]> ")?;
trace!("Verify? Answer = '{answer}'");
Ok(VerificationResponse {
valid: matches!(answer.trim(), "y" | "Y" | "yes" | "YES"),
})
}
x => {
error!("Unsupported verify kind: {x}");
Ok(VerificationResponse { valid: false })
}
}
let handler = self.get_mut_active_method_handler_or_error()?;
handler.on_verification(verification).await
}
async fn on_info(&mut self, info: Info) -> io::Result<()> {
trace!("on_info({info:?})");
println!("{}", info.text);
Ok(())
let handler = self.get_mut_active_method_handler_or_error()?;
handler.on_info(info).await
}
async fn on_error(&mut self, error: Error) -> io::Result<()> {
trace!("on_error({error:?})");
eprintln!("{}: {}", error.kind, error.text);
Ok(())
let handler = self.get_mut_active_method_handler_or_error()?;
handler.on_error(error).await
}
}

@ -0,0 +1,33 @@
use super::{
Challenge, ChallengeResponse, Error, Info, Verification, VerificationKind, VerificationResponse,
};
use async_trait::async_trait;
use std::io;
/// Interface for a handler of authentication requests for a specific authentication method.
#[async_trait]
pub trait AuthMethodHandler {
/// Callback when a challenge is received, returning answers to the given questions.
async fn on_challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse>;
/// Callback when a verification request is received, returning true if approvided or false if
/// unapproved.
async fn on_verification(
&mut self,
verification: Verification,
) -> io::Result<VerificationResponse>;
/// Callback when information is received. To fail, return an error from this function.
async fn on_info(&mut self, info: Info) -> io::Result<()>;
/// Callback when an error is received. Regardless of the result returned, this will terminate
/// the authenticator. In the situation where a custom error would be preferred, have this
/// callback return an error.
async fn on_error(&mut self, error: Error) -> io::Result<()>;
}
mod prompt;
pub use prompt::*;
mod static_key;
pub use static_key::*;

@ -0,0 +1,88 @@
use super::{
AuthMethodHandler, Challenge, ChallengeResponse, Error, Info, Verification, VerificationKind,
VerificationResponse,
};
use async_trait::async_trait;
use log::*;
use std::io;
/// Blocking implementation of [`AuthMethodHandler`] that uses prompts to communicate challenge &
/// verification requests, receiving responses to relay back.
pub struct PromptAuthMethodHandler<T, U> {
text_prompt: T,
password_prompt: U,
}
impl<T, U> PromptAuthMethodHandler<T, U> {
pub fn new(text_prompt: T, password_prompt: U) -> Self {
Self {
text_prompt,
password_prompt,
}
}
}
#[async_trait]
impl<T, U> AuthMethodHandler for PromptAuthMethodHandler<T, U>
where
T: Fn(&str) -> io::Result<String> + Send + Sync + 'static,
U: Fn(&str) -> io::Result<String> + Send + Sync + 'static,
{
async fn on_challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse> {
trace!("on_challenge({challenge:?})");
let mut answers = Vec::new();
for question in challenge.questions.iter() {
// Contains all prompt lines including same line
let mut lines = question.text.split('\n').collect::<Vec<_>>();
// Line that is prompt on same line as answer
let line = lines.pop().unwrap();
// Go ahead and display all other lines
for line in lines.into_iter() {
eprintln!("{}", line);
}
// Get an answer from user input, or use a blank string as an answer
// if we fail to get input from the user
let answer = (self.password_prompt)(line).unwrap_or_default();
answers.push(answer);
}
Ok(ChallengeResponse { answers })
}
async fn on_verification(
&mut self,
verification: Verification,
) -> io::Result<VerificationResponse> {
trace!("on_verify({verification:?})");
match verification.kind {
VerificationKind::Host => {
eprintln!("{}", verification.text);
let answer = (self.text_prompt)("Enter [y/N]> ")?;
trace!("Verify? Answer = '{answer}'");
Ok(VerificationResponse {
valid: matches!(answer.trim(), "y" | "Y" | "yes" | "YES"),
})
}
x => {
error!("Unsupported verify kind: {x}");
Ok(VerificationResponse { valid: false })
}
}
}
async fn on_info(&mut self, info: Info) -> io::Result<()> {
trace!("on_info({info:?})");
println!("{}", info.text);
Ok(())
}
async fn on_error(&mut self, error: Error) -> io::Result<()> {
trace!("on_error({error:?})");
eprintln!("{}: {}", error.kind, error.text);
Ok(())
}
}

@ -0,0 +1,174 @@
use super::{
AuthMethodHandler, Challenge, ChallengeResponse, Error, Info, Verification,
VerificationResponse,
};
use crate::common::HeapSecretKey;
use async_trait::async_trait;
use log::*;
use std::io;
/// Implementation of [`AuthMethodHandler`] that answers challenge requests using a static
/// [`HeapSecretKey`]. All other portions of method authentication are handled by another
/// [`AuthMethodHandler`].
pub struct StaticKeyAuthMethodHandler {
key: HeapSecretKey,
handler: Box<dyn AuthMethodHandler + Send>,
}
impl StaticKeyAuthMethodHandler {
/// Creates a new [`StaticKeyAuthMethodHandler`] that responds to challenges using a static
/// `key`. All other requests are passed to the `handler`.
pub fn new<T: AuthMethodHandler + Send + 'static>(
key: impl Into<HeapSecretKey>,
handler: T,
) -> Self {
Self {
key: key.into(),
handler: Box::new(handler),
}
}
/// Creates a new [`StaticKeyAuthMethodHandler`] that responds to challenges using a static
/// `key`. All other requests are passed automatically, meaning that verification is always
/// approvide and info/errors are ignored.
pub fn simple(key: impl Into<HeapSecretKey>) -> Self {
Self::new(key, {
struct __AuthMethodHandler;
#[async_trait]
impl AuthMethodHandler for __AuthMethodHandler {
async fn on_challenge(&mut self, _: Challenge) -> io::Result<ChallengeResponse> {
unreachable!("on_challenge should be handled by StaticKeyAuthMethodHandler");
}
async fn on_verification(
&mut self,
_: Verification,
) -> io::Result<VerificationResponse> {
Ok(VerificationResponse { valid: true })
}
async fn on_info(&mut self, _: Info) -> io::Result<()> {
Ok(())
}
async fn on_error(&mut self, _: Error) -> io::Result<()> {
Ok(())
}
}
__AuthMethodHandler
})
}
}
#[async_trait]
impl AuthMethodHandler for StaticKeyAuthMethodHandler {
async fn on_challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse> {
trace!("on_challenge({challenge:?})");
let mut answers = Vec::new();
for question in challenge.questions.iter() {
// Only challenges with a "key" label are allowed, all else will fail
if question.label != "key" {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Only 'key' challenges are supported",
));
}
answers.push(self.key.to_string());
}
Ok(ChallengeResponse { answers })
}
async fn on_verification(
&mut self,
verification: Verification,
) -> io::Result<VerificationResponse> {
trace!("on_verify({verification:?})");
self.handler.on_verification(verification).await
}
async fn on_info(&mut self, info: Info) -> io::Result<()> {
trace!("on_info({info:?})");
self.handler.on_info(info).await
}
async fn on_error(&mut self, error: Error) -> io::Result<()> {
trace!("on_error({error:?})");
self.handler.on_error(error).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::authentication::msg::{ErrorKind, Question, VerificationKind};
use test_log::test;
#[test(tokio::test)]
async fn on_challenge_should_fail_if_non_key_question_received() {
let mut handler = StaticKeyAuthMethodHandler::simple(HeapSecretKey::generate(32).unwrap());
handler
.on_challenge(Challenge {
questions: vec![Question::new("test")],
options: Default::default(),
})
.await
.unwrap_err();
}
#[test(tokio::test)]
async fn on_challenge_should_answer_with_stringified_key_for_key_questions() {
let mut handler = StaticKeyAuthMethodHandler::simple(HeapSecretKey::generate(32).unwrap());
let response = handler
.on_challenge(Challenge {
questions: vec![Question::new("key")],
options: Default::default(),
})
.await
.unwrap();
assert_eq!(response.answers.len(), 1, "Wrong answer set received");
assert!(!response.answers[0].is_empty(), "Empty answer being sent");
}
#[test(tokio::test)]
async fn on_verification_should_leverage_fallback_handler() {
let mut handler = StaticKeyAuthMethodHandler::simple(HeapSecretKey::generate(32).unwrap());
let response = handler
.on_verification(Verification {
kind: VerificationKind::Host,
text: "host".to_string(),
})
.await
.unwrap();
assert!(response.valid, "Unexpected result from fallback handler");
}
#[test(tokio::test)]
async fn on_info_should_leverage_fallback_handler() {
let mut handler = StaticKeyAuthMethodHandler::simple(HeapSecretKey::generate(32).unwrap());
handler
.on_info(Info {
text: "info".to_string(),
})
.await
.unwrap();
}
#[test(tokio::test)]
async fn on_error_should_leverage_fallback_handler() {
let mut handler = StaticKeyAuthMethodHandler::simple(HeapSecretKey::generate(32).unwrap());
handler
.on_error(Error {
kind: ErrorKind::Error,
text: "text".to_string(),
})
.await
.unwrap();
}
}

@ -25,7 +25,11 @@ impl AuthenticationMethod for StaticKeyAuthenticationMethod {
async fn authenticate(&self, authenticator: &mut dyn Authenticator) -> io::Result<()> {
let response = authenticator
.challenge(Challenge {
questions: vec![Question::new("key")],
questions: vec![Question {
label: "key".to_string(),
text: "Provide a key: ".to_string(),
options: Default::default(),
}],
options: Default::default(),
})
.await?;

@ -191,6 +191,12 @@ impl<const N: usize> From<[u8; N]> for HeapSecretKey {
}
}
impl<const N: usize> From<SecretKey<N>> for HeapSecretKey {
fn from(key: SecretKey<N>) -> Self {
key.into_heap_secret_key()
}
}
impl FromStr for HeapSecretKey {
type Err = SecretKeyError;

@ -10,6 +10,7 @@ use distant_core::{
Capabilities, CapabilityKind, DirEntry, Environment, FileType, Metadata, ProcessId,
PtySize, SystemInfo, UnixMetadata,
},
net::server::ConnectionCtx,
DistantApi, DistantCtx,
};
use log::*;
@ -75,8 +76,9 @@ impl SshDistantApi {
impl DistantApi for SshDistantApi {
type LocalData = ConnectionState;
async fn on_accept(&self, local_data: &mut Self::LocalData) {
local_data.global_processes = Arc::downgrade(&self.processes);
async fn on_accept(&self, ctx: ConnectionCtx<'_, Self::LocalData>) -> io::Result<()> {
ctx.local_data.global_processes = Arc::downgrade(&self.processes);
Ok(())
}
async fn capabilities(&self, ctx: DistantCtx<Self::LocalData>) -> io::Result<Capabilities> {

@ -7,11 +7,12 @@ use async_trait::async_trait;
use distant_core::{
data::Environment,
net::{
FramedTransport, IntoSplit, OneshotListener, ServerExt, ServerRef, TcpClientExt,
XChaCha20Poly1305Codec,
client::{Client, ReconnectStrategy},
common::authentication::{AuthHandlerMap, Verifier},
common::{FramedTransport, OneshotListener},
server::{Server, ServerRef},
},
BoxedDistantReader, BoxedDistantWriter, BoxedDistantWriterReader, DistantApiServerHandler,
DistantChannelExt, DistantClient, DistantSingleKeyCredentials,
DistantApiServerHandler, DistantChannelExt, DistantClient, DistantSingleKeyCredentials,
};
use log::*;
use smol::channel::Receiver as SmolReceiver;
@ -565,14 +566,18 @@ impl Ssh {
let credentials = self.launch(opts).await?;
let key = credentials.key;
let codec = XChaCha20Poly1305Codec::from(key);
// Try each IP address with the same port to see if one works
let mut err = None;
for ip in candidate_ips {
let addr = SocketAddr::new(ip, credentials.port);
debug!("Attempting to connect to distant server @ {}", addr);
match DistantClient::connect_timeout(addr, codec.clone(), timeout).await {
match Client::tcp(addr)
.auth_handler(AuthHandlerMap::new().with_static_key(key.clone()))
.timeout(timeout)
.connect()
.await
{
Ok(client) => return Ok(client),
Err(x) => err = Some(x),
}
@ -684,28 +689,13 @@ impl Ssh {
}
/// Consume [`Ssh`] and produce a [`DistantClient`] that is powered by an ssh client
/// underneath
/// underneath.
pub async fn into_distant_client(self) -> io::Result<DistantClient> {
Ok(self.into_distant_pair().await?.0)
}
/// Consume [`Ssh`] and produce a [`BoxedDistantWriterReader`] that is powered by an ssh client
/// underneath
pub async fn into_distant_writer_reader(self) -> io::Result<BoxedDistantWriterReader> {
Ok(self.into_writer_reader_and_server().await?.0)
}
/// Consumes [`Ssh`] and produces a [`DistantClient`] and [`DistantApiServer`] pair
/// Consumes [`Ssh`] and produces a [`DistantClient`] and [`ServerRef`] pair.
pub async fn into_distant_pair(self) -> io::Result<(DistantClient, Box<dyn ServerRef>)> {
let ((writer, reader), server) = self.into_writer_reader_and_server().await?;
let client = DistantClient::new(writer, reader)?;
Ok((client, server))
}
/// Consumes [`Ssh`] and produces a [`DistantClient`] and [`DistantApiServer`] pair
async fn into_writer_reader_and_server(
self,
) -> io::Result<(BoxedDistantWriterReader, Box<dyn ServerRef>)> {
// Exit early if not authenticated as this is a requirement
if !self.authenticated {
return Err(io::Error::new(
@ -714,24 +704,19 @@ impl Ssh {
));
}
let (t1, t2) = FramedTransport::pair(1);
// Spawn a bridge client that is directly connected to our server
let (writer, reader) = t1.into_split();
let writer: BoxedDistantWriter = Box::new(writer);
let reader: BoxedDistantReader = Box::new(reader);
// Spawn a bridge server that is directly connected to our client
let server = {
let Self {
session: wez_session,
..
} = self;
let (writer, reader) = t2.into_split();
DistantApiServerHandler::new(SshDistantApi::new(wez_session))
.start(OneshotListener::from_value((writer, reader)))?
};
let Self {
session: wez_session,
..
} = self;
Ok(((writer, reader), server))
let (t1, t2) = FramedTransport::pair(1);
let server = Server::new()
.handler(DistantApiServerHandler::new(SshDistantApi::new(
wez_session,
)))
.verifier(Verifier::none())
.start(OneshotListener::from_value(t2.into_inner()))?;
let client = Client::spawn_inmemory(t1, ReconnectStrategy::Fail);
Ok((client, server))
}
}

Loading…
Cancel
Save