Pure-Rust asynchronous SSH library, both client and server

#38 Usage of server example forwarding input of a client to other clients

Opened by looran on May 28, 2021
looran on May 28, 2021

I’m using the example server in src/lib.rs, that is supposed to send input data from a client to all other clients using server::session::Handle::data(), but i can’t make it work. I’m using 2 ssh clients, that do authenticate successfully with password authentication, but data sent from client 1 does not end up in client 2. Using debug prints i can see the data() function in the server::Handler is getting called when sending data from client 1, and i see in client 1 the data being sent back to itself.

For both clients i use:

ssh -S none  -v -p 2222 user@127.0.0.1

Any idea why the forwarding does not work ? Isn’t what is typed on client 1 supposed to be forwarded to client 2 output, based on the code at src/lib.rs#111 s.data(*channel, CryptoVec::from_slice(data)); ?

In order to understand deeper what is happening, I’m trying to follow the server example comments “It doesn’t handle errors when s.data returns an error”. But, I can’t seem to find a way to detect and print these potential errors on Future properly, anyone willing to help me here with a little example code ?

Sorry for the noobish questions i’m very new to rust ! Thanks for your work on thrussh, it is actually this library that is the starting point of my interest in this language.

looran on May 31, 2021

It works by using an ugly method, entering the tokio runtime manually to be able to await on the Handle::data() function from a non-async method.

So from the server example in tokio source, I add the following code at src/lib.rs#111:

futures::executor::block_on(s.data(*chan, CryptoVec::from_slice(data))).expect("error blocking on s.data()");

I imagine refactoring of the Server Handler trait to have asyncronous functions would be needed to accomplish this in a clean way ? I don’t realize how much work it represents.

fadedbee on June 5, 2021

After changing increasing the timeouts to 30s and adding a little printf debugging:

    fn data(self, channel: ChannelId, data: &[u8], mut session: Session) -> Self::FutureUnit {
        println!("rx data: {:?}", data);

I can now see what I type being received on the server, after 10 or 20 seconds.

looran on June 21, 2021

I found a more-reliable-but-still-disgusting way to send messages to a thrussh server session from outside the async loop, see bellow.

Note that a single send should be done at a time, as a new task will be spawned every time messages would likely arrive out-of-order.

    asyncsim_send(&server_handle, channel, b"hey you", false);

    fn asyncsim_send(handle: &thrussh::server::Handle, channel: thrussh::ChannelId, bytes: &[u8], mut close_after_send: bool) {
        let tokiohandle = tokio::runtime::Handle::current();
        let mut sess = handle.clone();
        let buf = bytes.to_owned();
        tokiohandle.spawn(async move {
            match sess.data(channel, thrussh::CryptoVec::from_slice(&buf)).await {
                Ok(_) => (),
                Err(_) => {
                    debug!("detected error while send, closing connection");
                    close_after_send = true;
                },
            }
            if close_after_send {
                match sess.close(channel).await {
                    Ok(_) => (),
                    Err(_) => warn!("detected error while close"),
                }
            }
        });
    }
urkle on July 8, 2022

Here is what I changed line 111 to.

                let r =
                    futures::executor::block_on(s.data(*channel, CryptoVec::from_slice(data)));
                if let Err(_) = r {
                    return futures::future::ready(Err(anyhow!(
                        "Error sending data to client {}",
                        *id
                    )));
                }

It would be nice to switch the trait to use async_trait as that would make the traits MUCH easier to write using async/await style code.

junon on January 29, 2024

Sorry to gravedig but yes, please use async fn in traits now that it’s stabilized. Having to name every single future as a trait type makes certain things very hard to code for. Especially since the server example doesn’t work as one would expect.