Skip to content

Commit 7c03dd9

Browse files
AspectUnkEugeny
authored andcommitted
bumb toolchain & add sftp client example
1 parent 8fa6892 commit 7c03dd9

File tree

5 files changed

+108
-4
lines changed

5 files changed

+108
-4
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ This is a fork of [Thrussh](https://nest.pijul.com/pijul/thrussh) by Pierre-Éti
6060

6161
## Ecosystem
6262

63-
* [russh-sftp](https://crates.io/crates/russh-sftp) - server-side SFTP subsystem support for `russh` - see `russh/examples/sftp_server.rs`.
63+
* [russh-sftp](https://crates.io/crates/russh-sftp) - server-side and client-side SFTP subsystem support for `russh` - see `russh/examples/sftp_server.rs` or `russh/examples/sftp_client.rs`.
6464
* [async-ssh2-tokio](https://crates.io/crates/async-ssh2-tokio) - simple high-level API for running commands over SSH.
6565

6666
## Contributors ✨

russh/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ tokio = { version = "1.17.0", features = [
6666
"sync",
6767
"macros",
6868
] }
69-
russh-sftp = "1.1"
69+
russh-sftp = "2.0.0-beta.1"
7070

7171
[package.metadata.docs.rs]
7272
features = ["openssl"]

russh/examples/sftp_client.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use async_trait::async_trait;
2+
use log::{error, info, LevelFilter};
3+
use russh::*;
4+
use russh_keys::*;
5+
use russh_sftp::client::SftpSession;
6+
use std::sync::Arc;
7+
use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
8+
9+
struct Client;
10+
11+
#[async_trait]
12+
impl client::Handler for Client {
13+
type Error = anyhow::Error;
14+
15+
async fn check_server_key(
16+
self,
17+
server_public_key: &key::PublicKey,
18+
) -> Result<(Self, bool), Self::Error> {
19+
info!("check_server_key: {:?}", server_public_key);
20+
Ok((self, true))
21+
}
22+
23+
async fn data(
24+
self,
25+
channel: ChannelId,
26+
data: &[u8],
27+
session: client::Session,
28+
) -> Result<(Self, client::Session), Self::Error> {
29+
info!("data on channel {:?}: {}", channel, data.len());
30+
Ok((self, session))
31+
}
32+
}
33+
34+
#[tokio::main]
35+
async fn main() {
36+
env_logger::builder()
37+
.filter_level(LevelFilter::Debug)
38+
.init();
39+
40+
let config = russh::client::Config::default();
41+
let sh = Client {};
42+
let mut session = russh::client::connect(Arc::new(config), ("localhost", 22), sh)
43+
.await
44+
.unwrap();
45+
if session.authenticate_password("root", "pass").await.unwrap() {
46+
let mut channel = session.channel_open_session().await.unwrap();
47+
channel.request_subsystem(true, "sftp").await.unwrap();
48+
let sftp = SftpSession::new(channel.into_stream()).await.unwrap();
49+
info!("current path: {:?}", sftp.canonicalize(".").await.unwrap());
50+
51+
// create dir and symlink
52+
let path = "./some_kind_of_dir";
53+
let symlink = "./symlink";
54+
55+
sftp.create_dir(path).await.unwrap();
56+
sftp.symlink(path, symlink).await.unwrap();
57+
58+
info!("dir info: {:?}", sftp.metadata(path).await.unwrap());
59+
info!(
60+
"symlink info: {:?}",
61+
sftp.symlink_metadata(path).await.unwrap()
62+
);
63+
64+
// scanning directory
65+
for entry in sftp.read_dir(".").await.unwrap() {
66+
info!("file in directory: {:?}", entry.file_name());
67+
}
68+
69+
sftp.remove_file(symlink).await.unwrap();
70+
sftp.remove_dir(path).await.unwrap();
71+
72+
// interaction with i/o
73+
let filename = "test_new.txt";
74+
let mut file = sftp.create(filename).await.unwrap();
75+
info!("metadata by handle: {:?}", file.metadata().await.unwrap());
76+
77+
file.write_all(b"magic text").await.unwrap();
78+
info!("flush: {:?}", file.flush().await); // or file.sync_all()
79+
info!(
80+
"current cursor position: {:?}",
81+
file.stream_position().await
82+
);
83+
84+
let mut str = String::new();
85+
86+
file.rewind().await.unwrap();
87+
file.read_to_string(&mut str).await.unwrap();
88+
file.rewind().await.unwrap();
89+
90+
info!(
91+
"our magical contents: {}, after rewind: {:?}",
92+
str,
93+
file.stream_position().await
94+
);
95+
96+
file.shutdown().await.unwrap();
97+
sftp.remove_file(filename).await.unwrap();
98+
99+
// should fail because handle was closed
100+
error!("should fail: {:?}", file.read_u8().await);
101+
}
102+
}

russh/examples/sftp_server.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,12 @@ impl russh_sftp::server::Handler for SftpSession {
151151
files: vec![
152152
File {
153153
filename: "foo".to_string(),
154+
longname: "".to_string(),
154155
attrs: FileAttributes::default(),
155156
},
156157
File {
157158
filename: "bar".to_string(),
159+
longname: "".to_string(),
158160
attrs: FileAttributes::default(),
159161
},
160162
],
@@ -169,6 +171,7 @@ impl russh_sftp::server::Handler for SftpSession {
169171
id,
170172
files: vec![File {
171173
filename: "/".to_string(),
174+
longname: "".to_string(),
172175
attrs: FileAttributes::default(),
173176
}],
174177
})
@@ -185,7 +188,6 @@ async fn main() {
185188
auth_rejection_time: Duration::from_secs(3),
186189
auth_rejection_time_initial: Some(Duration::from_secs(0)),
187190
keys: vec![KeyPair::generate_ed25519().unwrap()],
188-
inactivity_timeout: Some(Duration::from_secs(3600)),
189191
..Default::default()
190192
};
191193

rust-toolchain.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[toolchain]
2-
channel = "1.65.0"
2+
channel = "1.70.0"

0 commit comments

Comments
 (0)