Coder Social home page Coder Social logo

ssh-rs's Introduction

风急天高猿啸哀,渚清沙白鸟飞回。无边落木萧萧下,不尽长江滚滚来。
万里悲秋常作客,百年多病独登台。艰难苦恨繁霜鬓,潦倒新停浊酒杯。

					—— 杜甫〔唐代〕[登高]

ssh-rs's People

Contributors

1148118271 avatar andresv avatar cchance27 avatar hsujv avatar moskalyka avatar nitronplus avatar r3dlight avatar sheshowered avatar xijaja avatar xuguangnian avatar zachs18 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

ssh-rs's Issues

How to obtain SshError

Sending commands returns a SshResult<()> object. Calling get_result() afterwards will obtain the result of the command. Fine and working so far.

But what is the best way to check for an error or better obtain the error message?

In error.rs I can see the following mapping: pub type SshResult<I> = Result<I, SshError>;

Thank you for every answer or suggestion.

Scp: path-related bugs on windows

Somewhat related to #94 but fairly distinct issue: there appear to be bugs around interpreting paths when uploading via scp from a windows localhost to a linux remote host. I haven't narrowed down the root cause but I can see that slashes are sometimes being dropped and resulting in the remote path being incorrect.

e.g. an attempt to upload to /tmp/foo/bar.txt from windows resulted in the file actually being created in /tmp/foobar.txt.

sftp

支持sftp 这种二进制 信息流吗

Looping ssh connection Timeout

Hey, i was wondoring if it could possible to do that, because i'm encounring difficuties.
The first connection connects successfully but all subsequent connections return an error on session.connect()

if i do session.connect().unwrap() :
thread 'main' panicked at 'called "Result::unwrap()" on an "Err" value: Error: { Kind(Timeout), Message(time out.) }

{ Kind(Timeout) }

loop {
  let mut session: Session = ssh::create_session();
  session.set_timeout(3);
  session.set_user_and_password("user", "password");
  match session.connect("127.0.0.1:22") {
         Ok(_res) => {
              //Do something
         },
         Err(_err) => {
              //handle error
        }
  }
session.close().unwrap();
}

Thank you

Error when connecting to older ExtremeXOS switch

I've enabled all the deprecated features, but when i use the following connection

        .username(values[4])
        .password(values[5])
        .add_pubkey_algorithms(algorithm::PubKey::SshRsa)
        .add_kex_algorithms(algorithm::Kex::DiffieHellmanGroup1Sha1)
        .add_mac_algortihms(algorithm::Mac::HmacSha1)
        .connect(format!("{}:22", values[3]))
        .expect("Unable to connect to SSH")
        .run_local();

I get the following, which is odd it seems like the server has no response? I can connect to it with old versions of putty.

I think this might be because of an outdated pubkey algorithm that isn't supported "ssh-dss"?

ERROR ssh::config::algorithm: err_msg: "Key_agreement: the signature algorithm fails to match, algorithms supported by the server: ,algorithms supported by the client: rsa-sha2-512,rsa-sha2-256,ssh-rsa"

Can you make an example of tokio?

My code:

async fn ws_handler(
    ws: WebSocketUpgrade,
    user_agent: Option<TypedHeader<headers::UserAgent>>,
) -> impl IntoResponse {
    if let Some(TypedHeader(user_agent)) = user_agent {
        println!("`{}` connected", user_agent.as_str());
    }

    ws.on_upgrade(handle_socket)
}

async fn handle_socket(mut socket: WebSocket) {
    let session = ssh::create_session()
    .username("root")
    .password("123456")
    .connect("192.168.1.9:22").unwrap();

    let a_exec = Arc::new(RefCell::new(Mutex::new(session.run_local())));
    
    loop {
        let m_exec = a_exec.clone();
        if let Some(msg) = socket.recv().await {
            if let Ok(msg) = msg {
                match msg {
                    Message::Text(t) => {
                        println!("client sent str: {:?}", t);
                        let b_exec_m = m_exec.borrow_mut();
                        let b_exec = b_exec_m.lock().await.open_exec();
                        let vec = b_exec.unwrap().send_command(&t).unwrap();
                        
    
                        // let vec = exec.send_command(&t).unwrap();
                        if socket
                            .send(Message::Text(String::from_utf8(vec).unwrap()))
                            .await
                            .is_err()
                        {
                            println!("client disconnected");
                        };
                    }
                    Message::Binary(_) => {
                        println!("client sent binary data");
                    }
                    Message::Ping(_) => {
                        println!("socket ping");
                    }
                    Message::Pong(_) => {
                        println!("socket pong");
                    }
                    Message::Close(_) => {
                        println!("client disconnected");
                        return;
                    }
                }
            } else {
                println!("client disconnected");
                return;
            }
        } else {
            println!("client disconnected");
            return;
        }
    }
// loop {
//     if socket
//         .send(Message::Text(String::from("Hi!")))
//         .await
//         .is_err()
//     {
//         println!("client disconnected");
//         return;
//     }
//     tokio::time::sleep(std::time::Duration::from_secs(3)).await;
// }

}

Error:

error: future cannot be sent between threads safely
   --> src\main.rs:93:8
    |
93  |     ws.on_upgrade(handle_socket)
    |        ^^^^^^^^^^ future returned by `handle_socket` is not `Send`
    |
    = help: the trait `Sync` is not implemented for `RefCell<tauri::async_runtime::Mutex<LocalSession<TcpStream>>>`
note: future is not `Send` as this value is used across an await
   --> src\main.rs:106:41
    |
102 |     let a_exec = Arc::new(RefCell::new(Mutex::new(session.run_local())));
    |         ------ has type `Arc<RefCell<tauri::async_runtime::Mutex<LocalSession<TcpStream>>>>` which is not `Send`
...
106 |         if let Some(msg) = socket.recv().await {
    |                                         ^^^^^^ await occurs here, with `a_exec` maybe used later
...
161 | }
    | - `a_exec` is later dropped here
note: required by a bound in `WebSocketUpgrade::on_upgrade`
   --> C:\Users\fullee\.cargo\registry\src\github.com-1ecc6299db9ec823\axum-0.5.17\src\extract\ws.rs:236:36
    |
236 |         Fut: Future<Output = ()> + Send + 'static,
    |                                    ^^^^ required by this bound in `WebSocketUpgrade::on_upgrade`

error: future cannot be sent between threads safely
   --> src\main.rs:93:8
    |
93  |     ws.on_upgrade(handle_socket)
    |        ^^^^^^^^^^ future returned by `handle_socket` is not `Send`
    |
    = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `NonNull<tauri::async_runtime::Mutex<LocalSession<TcpStream>>>`
note: future is not `Send` as this value is used across an await
   --> src\main.rs:112:53
    |
111 |                         let b_exec_m = m_exec.borrow_mut();
    |                             -------- has type `RefMut<'_, tauri::async_runtime::Mutex<LocalSession<TcpStream>>>` which is not `Send`
112 |                         let b_exec = b_exec_m.lock().await.open_exec();
    |                                                     ^^^^^^ await occurs here, with `b_exec_m` maybe used later
...
124 |                     }
    |                     - `b_exec_m` is later dropped here
note: required by a bound in `WebSocketUpgrade::on_upgrade`
   --> C:\Users\fullee\.cargo\registry\src\github.com-1ecc6299db9ec823\axum-0.5.17\src\extract\ws.rs:236:36
    |
236 |         Fut: Future<Output = ()> + Send + 'static,
    |                                    ^^^^ required by this bound in `WebSocketUpgrade::on_upgrade`

error: future cannot be sent between threads safely
   --> src\main.rs:93:8
    |
93  |     ws.on_upgrade(handle_socket)
    |        ^^^^^^^^^^ future returned by `handle_socket` is not `Send`
    |
    = help: the trait `Sync` is not implemented for `std::cell::Cell<isize>`
note: future is not `Send` as this value is used across an await
   --> src\main.rs:112:53
    |
111 |                         let b_exec_m = m_exec.borrow_mut();
    |                             -------- has type `RefMut<'_, tauri::async_runtime::Mutex<LocalSession<TcpStream>>>` which is not `Send`
112 |                         let b_exec = b_exec_m.lock().await.open_exec();
    |                                                     ^^^^^^ await occurs here, with `b_exec_m` maybe used later
...
124 |                     }
    |                     - `b_exec_m` is later dropped here
note: required by a bound in `WebSocketUpgrade::on_upgrade`
   --> C:\Users\fullee\.cargo\registry\src\github.com-1ecc6299db9ec823\axum-0.5.17\src\extract\ws.rs:236:36
    |
236 |         Fut: Future<Output = ()> + Send + 'static,
    |                                    ^^^^ required by this bound in `WebSocketUpgrade::on_upgrade`

error: future cannot be sent between threads safely
   --> src\main.rs:93:8
    |
93  |     ws.on_upgrade(handle_socket)
    |        ^^^^^^^^^^ future returned by `handle_socket` is not `Send`
    |
    = help: within `LocalSession<TcpStream>`, the trait `Send` is not implemented for `Rc<RefCell<ssh_rs::client::client::Client>>`
note: future is not `Send` as this value is used across an await
   --> src\main.rs:112:53
    |
112 |                         let b_exec = b_exec_m.lock().await.open_exec();
    |                                      --------       ^^^^^^ await occurs here, with `b_exec_m` maybe used later
    |                                      |
    |                                      has type `&tauri::async_runtime::Mutex<LocalSession<TcpStream>>` which is not `Send`
note: `b_exec_m` is later dropped here
   --> src\main.rs:112:71
    |
112 |                         let b_exec = b_exec_m.lock().await.open_exec();
    |                                                                       ^
help: consider moving this into a `let` binding to create a shorter lived borrow
   --> src\main.rs:112:38
    |
112 |                         let b_exec = b_exec_m.lock().await.open_exec();
    |                                      ^^^^^^^^^^^^^^^
note: required by a bound in `WebSocketUpgrade::on_upgrade`
   --> C:\Users\fullee\.cargo\registry\src\github.com-1ecc6299db9ec823\axum-0.5.17\src\extract\ws.rs:236:36
    |
236 |         Fut: Future<Output = ()> + Send + 'static,
    |                                    ^^^^ required by this bound in `WebSocketUpgrade::on_upgrade`

error: future cannot be sent between threads safely
   --> src\main.rs:93:8
    |
93  |     ws.on_upgrade(handle_socket)
    |        ^^^^^^^^^^ future returned by `handle_socket` is not `Send`
    |
    = help: within `LocalSession<TcpStream>`, the trait `Send` is not implemented for `Rc<RefCell<TcpStream>>`
note: future is not `Send` as this value is used across an await
   --> src\main.rs:112:53
    |
112 |                         let b_exec = b_exec_m.lock().await.open_exec();
    |                                      --------       ^^^^^^ await occurs here, with `b_exec_m` maybe used later
    |                                      |
    |                                      has type `&tauri::async_runtime::Mutex<LocalSession<TcpStream>>` which is not `Send`
note: `b_exec_m` is later dropped here
   --> src\main.rs:112:71
112 |                         let b_exec = b_exec_m.lock().await.open_exec();
    |                                                                       ^
help: consider moving this into a `let` binding to create a shorter lived borrow
   --> src\main.rs:112:38
    |
112 |                         let b_exec = b_exec_m.lock().await.open_exec();
    |                                      ^^^^^^^^^^^^^^^
note: required by a bound in `WebSocketUpgrade::on_upgrade`
   --> C:\Users\fullee\.cargo\registry\src\github.com-1ecc6299db9ec823\axum-0.5.17\src\extract\ws.rs:236:36
    |
236 |         Fut: Future<Output = ()> + Send + 'static,
    |                                    ^^^^ required by this bound in `WebSocketUpgrade::on_upgrade`

warning: `tauri-app` (bin "tauri-app") generated 3 warnings
error: could not compile `tauri-app` due to 5 previous errors; 3 warnings emitted

add connect_with_timeout to set connection timeout

I need set the connection timeout with shorter Duration and R/W timeout with longer Duration, so add new method to set connection timeout instead of the global timeout.

fn main() {
    let mut session = ssh::create_session()
        .username("root")
        .password("root")
        .timeout(Some(Duration::from_millis(5000)))
        // .connect("192.168.220.134:22")
        .connect_with_timeout("192.168.220.134:22", Some(Duration::from_millis(1000)))
        .unwrap()
        .run_local();
    // Usage 1
    let mut shell = session.open_shell().unwrap();
    run_shell(&mut shell);

    // Close channel.
    shell.close().unwrap();
    // Close session.
    session.close();
}

Add timeout support for read operation

I'v read your sample code, the read operation seems not non-blocking, so the code will stuck there. You could have to add timeout support for it, hence the code can be executed until the end.

增加任务状态的建议

exec和scp的执行过程中,可能需要增加一些控制点:

  • 中断:执行过程可以中断,特别是长时间执行exec或上传下载时
  • scp传输完成率:需要有传输总字节数和已完成传输的字节数,方便计算出完成百分比
  • 异步:接口调用和结果需要分开,避免长时间的阻塞

建议用mpsc::channel()搭建跨线程的共享缓存,利用缓存记录任务状态,实现以上控制点功能。

标准输出和错误输出

首先感谢有了这么好用的ssh-rs库,对我的工作帮助很大。

在使用中我发现let vec: Vec<u8> = exec.send_command("ls -all").unwrap();只能收到进程的标准输出,但有些情境中我需要错误输出的信息,比如ls命令的路径不存在或无权限等。

是未提供错误输出呢,还是我没有找到具体的接口?

Can't use example to connect to server

Try to use example to my server, get error as blow.

thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 5',/home/zj/.cargo/registry/src/mirrors.sjtug.sjtu.edu.cn-4f7dbcce21e258a2/ssh-rs-0.1.2/src/key_agreement.rs:105:36

[Bug] Can't use example to connect to sshd server: key exchange error.

Hi,
I used example to connect to sshd server. but it show "key exchange error."

code (home/workspace/ssh-rs/examples/exec):
`use ssh_rs::ssh;

fn main() {
let mut session = ssh::create_session();
session.set_user_and_password("root", "P@ssw0rd");
session.connect("192.168.101.136:22").unwrap();
// Usage 1
let exec = session.open_exec().unwrap();
let vec: Vec = exec.send_command("ls -all").unwrap();
println!("{}", String::from_utf8(vec).unwrap());
// Usage 2
let channel = session.open_channel().unwrap();
let exec = channel.open_exec().unwrap();
let vec: Vec = exec.send_command("ls -all").unwrap();
println!("{}", String::from_utf8(vec).unwrap());
// Close session.
session.close().unwrap();
}`

[root@codeworkspace exec]#
[root@codeworkspace src]# cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.09s
Running /home/workspace/ssh-rs/examples/exec/target/debug/exec
thread 'main' panicked at 'called Result::unwrap() on an Err value: Error: { Kind(SshError("key exchange error.")), Message(key exchange error.) }', src/main.rs:6:43
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace

sshd server info:
[root@localhost ~]# cat /etc/*release CentOS release 6.5 (Final) LSB_VERSION=base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-n CentOS release 6.5 (Final) CentOS release 6.5 (Final) [root@localhost ~]# rpm -qa | grep ssh libssh2-1.4.2-1.el6.x86_64 openssh-askpass-5.3p1-94.el6.x86_64 openssh-clients-5.3p1-94.el6.x86_64 openssh-5.3p1-94.el6.x86_64 openssh-server-5.3p1-94.el6.x86_64

i capture pcap package in sshd server,it will attach in this web。 filter rule: ip.addr == 192.168.101.136

Shell command execution

Shell command execution does not work when my command takes longer than 500 milliseconds if the timeout is 500 milliseconds

`tracing` dependency is not new enough

ssh-rs depends on tracing = "^0.1", but uses tracing-core's impl Value for String at (at least) line 133 of src/client/client_kex.rs, but this impl was only added in [email protected]

(possibly discovered on discord)

Possible fixes:

  • Bump tracing dependency to 0.1.36 (which is the lowest tracing version that depends on tracing-core>=0.1.28)
  • Change to error!(err_msg.as_str()); or similar (impl Value for &str has existed since [email protected], via impl Value for str and impl Value for &T where T: Value + ?Sized) (there may be other places that need to change also)

Scp: track progress for larger transfers

Hi, just a feature request around scp. I used this project as I ran into difficulties cross-building with bindings to libssh2 (alexcrichton/ssh2-rs#22). The APIs are very similar so it wasn't a lot of effort to swap to using ssh-rs instead, but I did notice the scp API is higher-level in this library.

I definitely appreciate the higher-level API because it allowed me to throw out 60-odd lines of low-level data-streaming code I had had to write myself with ssh2-rs and instead just invoke scp.upload(src, dest), but in my case I'm uploading relatively large files which may take a few minutes to finish. To give a progress indicator to the user I used indicatif to render a nice progress bar, transfer speed, etc. by sending updates as I processed chunks. Because ssh-rs takes care of all that and doesn't provide any updates on how it's going, I can no longer do that.

So a potential improvement to the scp API would be to either expose some of the internals of the upload as a separate lower-level API to allow hooking in like this if desired, or potentially add a version of the API which sends updates to a channel with some simple events the user can react to, e.g. details of start and finish writing each file and writing a chunk of X bytes for a given file.

Use Rust std TCP set_read_TimeOut instead of a custum one ?

Hey,
I would like to talk about the way you handle the timeout when reading stuff on the tcp connection.

I was wondering if it would be better to use the standard implementation for read with a timeout on TCP rather than a custum one.
https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.set_read_timeout

I m asking my self if your implementation can cause an unnecessary hight CPU usage if the server respond slowly.

fn read_with_timeout<S>(stream: &mut S, tm: Option<Duration>, buf: &mut [u8]) -> SshResult<()>

The tcp connection is set by default to non blocking :

tcp.set_nonblocking(true).unwrap();

So when you read

match stream.read(&mut buf[offset..]) {

The read call terminates immediately if nothing is available at the moment, returning the wouldBlock error, then the loop checks the timeout and loops indefinitely until something is available in the buffer.

It might be more efficient to read only once and let the system manage the timeout ?

What do you think about ?
Because when i connect to my server, i notice an hight cpu usage from my program who use only your lib.

Ssh-rs panic on a thread when using in multi threading

Hello,
I had an issue with your library.
One thread panic when i use more threads than i have physical cores.
When i use Available parallelism from the rust standard library, i got 12 cores.
I precise that my cpu have 8 cores without hyper threading and 2 physical cores with Hyper Threading.
So 10 physical core without hyper threading.

When i set the number of threads to 10, no crash happen but when i increase the number of threads to 11 or 12 then it crash.

i m just running n threads and running ssh inside of each one.

thread '<unnamed>' panicked at C:\Users\Username\.cargo\registry\src\index.crates.io-6f17d22bba15001f\ssh-rs-0.4.1\src\config\version.rs:36:17:
assertion `left == right` failed
  left: [69, 120, 99, 101]
 right: [83, 83, 72, 45]
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Tunnel without executing commands

ssh -N -p 2222 -L6116:<tunnel-host>:<tunnel-port> -o StrictHostKeychecking=no tunnel@<ssh-server>

Can this be done with your lib?

Add hmac-sha256 and hmac-512

Hi Kang,

Would it be possible to add hmac-sha256 and hmac-sha512 support ? As far as I see, you are only supporting hmac-sha1 for now.

Thank you !

`connect(..)` does not timeout

When connecting to a host that is not responding connect(..) never times out.
Tried with this: https://github.com/1148118271/ssh-rs/blob/main/examples/exec/src/main.rs also added timeout(1000).

use ssh_rs::ssh;

fn main() {
    ssh::enable_log();

    let mut session = ssh::create_session()
        .username("ubuntu")
        .password("password")
        .timeout(1000)
        .connect("192.168.1.52:22")
        .unwrap()
        .run_local();

    let exec = session.open_exec().unwrap();
    let vec: Vec<u8> = exec.send_command("ls -all").unwrap();
    println!("{}", String::from_utf8(vec).unwrap());
    // Close session.
    session.close();
}

add more encryption algorithms

my ssh server only support the below encryption algorithms, but the library do not support,
could you please add these implementation or give me an example for one of them.
["aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"]

Exec commands that need sudo

How to execute commands that need sudo, like:

let vec: Vec<u8> = exec.send_command("sudo systemctl stop some.service").unwrap();
println!("{}", String::from_utf8(vec).unwrap());

channel_scp_u 文件的错误

第24行,

log::info!("start to upload files, \ local [{}] files will be synchronized to the remote [{}] folder.", remote_path_str,local_path_str);

local_path_str,remote_path_str 位置写反了。

Only captures stdout

Maybe I haven't found the way how to do this internally, but it certainly is a bit puzzling. When a command executed with exec.send_command("ls -al").unwrap(); like in the exec example succeeds, the send_command() will return the Result containing output.
However, if it doesn't succeed on the remote machine, the remote will print the error to stderr. For most commands at least. Then the returned value from send_command() will still be Ok() but without any contained data.

I mitigated this with 2>&1 after every command to redirect stderr it to stdout on the remote machine. But I wonder if this shouldn't be built in.

Problem with minimum libc version

Hey, i got this error with your library, could we do something to make my program work on my relatively old computer ?

It seems that i only have libc 2.23

i precise that i cant perform upgarde on my machine

./client: /lib/i386-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by ./client)
./client: /lib/i386-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by ./client)
./client: /lib/i386-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by ./client)
./client: /lib/i386-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./client)

Thank you

Arch Linux 打包问题

准备将 ssh-rs 打包到 AUR 仓库
编写了 PKGBUILD 如下

# Maintainer: taotieren <[email protected]>

pkgname=ssh-rs
pkgver=0.1.2
pkgrel=1
pkgdesc="In addition to encryption library, pure RUST implementation of SSH-2.0 client protocol"
arch=('any')
url="https://github.com/1148118271/ssh-rs"
license=('Apache-2.0')
provides=(${pkgname})
conflicts=(${pkgname} ${pkgname}-git)
#replaces=(${pkgname})
depends=('cargo')
makedepends=('make' 'git' 'cmake' 'gcc' 'rust')
backup=()
options=('!strip')
#install=${pkgname}.install
source=("${pkgname}-${pkgver}.tar.gz::https://ghproxy.com/${url}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('8559f1a5dab8c0a2740da562feec85f967fbb39aa2797e3c0590213b898e64e3')
build() {
# build crm
    cd "${srcdir}/${pkgname}-${pkgver}/"
    cargo build --frozen --release --all-features
}

check() {
    cd "${srcdir}/${pkgname}-${pkgver}/"
    cargo test  --frozen --release --all-features
}

package() {
    cd "${srcdir}/${pkgname}-${pkgver}/"
    export RUSTUP_TOOLCHAIN=stable
    cargo install --no-track --frozen --all-features --root "$pkgdir/usr/" --path .
}

本地编译日志如下:

➜  ssh-rs git:(master) makepkg -sf                                    
==> 正在创建软件包:ssh-rs 0.1.2-1 (Tue 11 Jan 2022 06:22:49 PM CST)
==> 正在检查运行时依赖关系...
==> 正在检查编译时依赖关系
==> 获取源代码...
  -> 找到 ssh-rs-0.1.2.tar.gz
==> 正在验证 source 文件,使用sha256sums...
    ssh-rs-0.1.2.tar.gz ... 通过
==> 正在释放源码...
  -> 正在解压缩 ssh-rs-0.1.2.tar.gz,使用 bsdtar
==> 正在删除现存的 $pkgdir/ 目录...
==> 正在开始 build()...
   Compiling cc v1.0.72
   Compiling libc v0.2.112
   Compiling getrandom v0.1.16
   Compiling pkg-config v0.3.24
   Compiling cfg-if v1.0.0
   Compiling autocfg v1.0.1
   Compiling ppv-lite86 v0.2.15
   Compiling once_cell v1.8.0
   Compiling foreign-types-shared v0.1.1
   Compiling openssl v0.10.38
   Compiling spin v0.5.2
   Compiling bitflags v1.3.2
   Compiling untrusted v0.7.1
   Compiling foreign-types v0.3.2
   Compiling openssl-src v111.17.0+1.1.1m
   Compiling openssl-sys v0.9.72
   Compiling ring v0.16.20
   Compiling rand_core v0.5.1
   Compiling rand_chacha v0.2.2
   Compiling rand v0.7.3
   Compiling ssh-rs v0.1.2 (/home/taotieren/git_clone/aur/ssh-rs/src/ssh-rs-0.1.2)
warning: use of deprecated associated function `std::error::Error::description`: use the Display impl or to_string()
   --> src/error.rs:110:45
    |
110 |             SshErrorKind::IoError(io) => io.description(),
    |                                             ^^^^^^^^^^^
    |
    = note: `#[warn(deprecated)]` on by default

warning: variable `crypt` is assigned to, but never used
  --> src/key_agreement.rs:25:17
   |
25 |         let mut crypt: String = String::new();
   |                 ^^^^^
   |
   = note: `#[warn(unused_variables)]` on by default
   = note: consider using `_crypt` instead

warning: value assigned to `crypt` is never read
  --> src/key_agreement.rs:49:29
   |
49 | ...                   crypt = String::from(a);
   |                       ^^^^^
   |
   = note: `#[warn(unused_assignments)]` on by default
   = help: maybe it is overwritten before being read?

warning: associated function is never used: `new`
  --> src/packet.rs:21:19
   |
21 |     pub(crate) fn new() -> Packet {
   |                   ^^^
   |
   = note: `#[warn(dead_code)]` on by default

warning: associated function is never used: `put_data`
  --> src/packet.rs:25:19
   |
25 |     pub(crate) fn put_data(&mut self, d: Data) {
   |                   ^^^^^^^^

warning: function is never used: `get_error`
   --> src/error.rs:143:4
    |
143 | fn get_error() -> Result<(), SshError> {
    |    ^^^^^^^^^

warning: `ssh-rs` (lib) generated 6 warnings
    Finished release [optimized] target(s) in 30.71s
==> 正在开始 check()...
warning: use of deprecated associated function `std::error::Error::description`: use the Display impl or to_string()
   --> src/error.rs:110:45
    |
110 |             SshErrorKind::IoError(io) => io.description(),
    |                                             ^^^^^^^^^^^
    |
    = note: `#[warn(deprecated)]` on by default

warning: variable `crypt` is assigned to, but never used
  --> src/key_agreement.rs:25:17
   |
25 |         let mut crypt: String = String::new();
   |                 ^^^^^
   |
   = note: `#[warn(unused_variables)]` on by default
   = note: consider using `_crypt` instead

warning: value assigned to `crypt` is never read
  --> src/key_agreement.rs:49:29
   |
49 | ...                   crypt = String::from(a);
   |                       ^^^^^
   |
   = note: `#[warn(unused_assignments)]` on by default
   = help: maybe it is overwritten before being read?

warning: associated function is never used: `new`
  --> src/packet.rs:21:19
   |
21 |     pub(crate) fn new() -> Packet {
   |                   ^^^
   |
   = note: `#[warn(dead_code)]` on by default

warning: associated function is never used: `put_data`
  --> src/packet.rs:25:19
   |
25 |     pub(crate) fn put_data(&mut self, d: Data) {
   |                   ^^^^^^^^

warning: function is never used: `get_error`
   --> src/error.rs:143:4
    |
143 | fn get_error() -> Result<(), SshError> {
    |    ^^^^^^^^^

   Compiling ssh-rs v0.1.2 (/home/taotieren/git_clone/aur/ssh-rs/src/ssh-rs-0.1.2)
warning: `ssh-rs` (lib) generated 6 warnings
warning: unused import: `std::sync::atomic::Ordering::Relaxed`
 --> src/tests.rs:7:9
  |
7 |     use std::sync::atomic::Ordering::Relaxed;
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: unused import: `global_variable`
 --> src/tests.rs:9:17
  |
9 |     use crate::{global_variable, message, SSH, strings};
  |                 ^^^^^^^^^^^^^^^

error[E0599]: no method named `write` found for struct `Arc<Mutex<Client>>` in the current scope
  --> src/tests.rs:40:24
   |
40 |         channel.stream.write(packet.as_slice()).unwrap();
   |                        ^^^^^ method not found in `Arc<Mutex<Client>>`

error[E0599]: no method named `read` found for struct `Arc<Mutex<Client>>` in the current scope
  --> src/tests.rs:43:46
   |
43 |                 let results = channel.stream.read().unwrap();
   |                                              ^^^^ method not found in `Arc<Mutex<Client>>`

error[E0599]: no method named `write` found for struct `Arc<Mutex<Client>>` in the current scope
  --> src/tests.rs:73:24
   |
73 |         channel.stream.write(packet.as_slice()).unwrap();
   |                        ^^^^^ method not found in `Arc<Mutex<Client>>`

error[E0599]: no method named `read` found for struct `Arc<Mutex<Client>>` in the current scope
  --> src/tests.rs:76:46
   |
76 |                 let results = channel.stream.read().unwrap();
   |                                              ^^^^ method not found in `Arc<Mutex<Client>>`

error[E0599]: no method named `write` found for struct `Arc<Mutex<Client>>` in the current scope
   --> src/tests.rs:110:28
    |
110 |             channel.stream.write(packet.as_slice()).unwrap();
    |                            ^^^^^ method not found in `Arc<Mutex<Client>>`

error[E0599]: no method named `write` found for struct `Arc<Mutex<Client>>` in the current scope
   --> src/tests.rs:118:24
    |
118 |         channel.stream.write(packet.as_slice()).unwrap();
    |                        ^^^^^ method not found in `Arc<Mutex<Client>>`

warning: unused import: `Write`
 --> src/tests.rs:4:25
  |
4 |     use std::io::{Read, Write};
  |                         ^^^^^

For more information about this error, try `rustc --explain E0599`.
warning: `ssh-rs` (lib test) generated 4 warnings (1 duplicate)
error: could not compile `ssh-rs` due to 6 previous errors; 4 warnings emitted
==> 错误: 在 check() 中发生一个错误。
    正在放弃...
➜  ssh-rs git:(master)

Scp: quoting errors with spaces in dest filename

When trying to upload individual files with scp, I ran into some unknown errors being reported. Looking at the source, it looks like this is the host sending a response packet which the library doesn't recognise, after sending the initial request to start an scp transfer.

I was transferring relatively large files and with spaces in filenames and directories so I did a few tests to narrow down the actual problem. Filesize isn't important, nor is local filename, but remote filename seems to have issues if there are spaces. I haven't managed to construct a very simple test case which produces unknown error, but I have identified several which produce ambiguous target which seem to be errors in the client:

        for dest in vec![
            "/tmp/foo.txt",
            "/tmp/foo?.txt",
            "/tmp/foo bar.txt",
            "/tmp/foo bar/foo.txt",
            "/tmp/foo bar/foo bar.txt",
            "/tmp/foo\\ bar/foo\\ bar.txt"
        ] {
            println!("DEST: {}", dest);
            let scp = self.session.open_scp()?;
            let res = scp.upload("/tmp/foo.txt", dest);

            match res {
                Ok(_) => println!("OK"),
                Err(e) => println!("ERR: {}", e)
            }
        }

For this test setup I created "/tmp/foo bar" on the remote host. I also tried this test with raw strings and by constructing paths with Path::new first to rule out issues there, but it behaves the same in both cases.

Results were:

DEST: /tmp/foo.txt
OK
DEST: /tmp/foo?.txt
OK
DEST: /tmp/foo bar.txt
ERR: Scp error: scp: ambiguous target

DEST: /tmp/foo bar/foo.txt
ERR: Scp error: scp: ambiguous target

DEST: /tmp/foo bar/foo bar.txt
ERR: Scp error: scp: ambiguous target

DEST: /tmp/foo\ bar/foo\ bar.txt
OK

You can see I can fix the issue manually by escaping any spaces in the remote path, but especially given I'm actually trying to pass it a Path I'd expect any necessary escaping to be handled by the library, as a Path like /tmp/foo bar correctly identifies the target.

I'm not very familiar with the low-level SSH / SCP protocol here but I'm assuming this library is failing to correctly quote it in the way the protocol desires and failing in these edge cases.

If you're open to PRs I might see if I can fix this issue myself and add my test cases above to the unit tests; I see you've tested recursive uploads, implicit target dir, etc. but not any edgecases around funny filenames. I ran into this while working on a little project to learn Rust so this seems like a straightforward issue for me to tackle while I'm here.

EDIT:

I identified how to get unknown error, as well; it's if the destination path contains brackets:

DEST: /tmp/(foo)/foo.txt
ERR: Scp error: unknown error.
DEST: /tmp/(foo).txt
ERR: Scp error: unknown error.
DEST: /tmp/\(foo\).txt
OK

...and again, backslashing fixes them.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.