Files
cli/vendor/russh/examples/sftp_server.rs

202 lines
5.4 KiB
Rust

use async_trait::async_trait;
use log::{error, info, LevelFilter};
use russh::{
server::{Auth, Msg, Server as _, Session},
Channel, ChannelId,
};
use russh_keys::key::KeyPair;
use russh_sftp::protocol::{File, FileAttributes, Handle, Name, Status, StatusCode, Version};
use std::{collections::HashMap, net::SocketAddr, sync::Arc, time::Duration};
use tokio::sync::Mutex;
#[derive(Clone)]
struct Server;
impl russh::server::Server for Server {
type Handler = SshSession;
fn new_client(&mut self, _: Option<SocketAddr>) -> Self::Handler {
SshSession::default()
}
}
struct SshSession {
clients: Arc<Mutex<HashMap<ChannelId, Channel<Msg>>>>,
}
impl Default for SshSession {
fn default() -> Self {
Self {
clients: Arc::new(Mutex::new(HashMap::new())),
}
}
}
impl SshSession {
pub async fn get_channel(&mut self, channel_id: ChannelId) -> Channel<Msg> {
let mut clients = self.clients.lock().await;
clients.remove(&channel_id).unwrap()
}
}
#[async_trait]
impl russh::server::Handler for SshSession {
type Error = anyhow::Error;
async fn auth_password(&mut self, user: &str, password: &str) -> Result<Auth, Self::Error> {
info!("credentials: {}, {}", user, password);
Ok(Auth::Accept)
}
async fn auth_publickey(
&mut self,
user: &str,
public_key: &russh_keys::key::PublicKey,
) -> Result<Auth, Self::Error> {
info!("credentials: {}, {:?}", user, public_key);
Ok(Auth::Accept)
}
async fn channel_open_session(
&mut self,
channel: Channel<Msg>,
_session: &mut Session,
) -> Result<bool, Self::Error> {
{
let mut clients = self.clients.lock().await;
clients.insert(channel.id(), channel);
}
Ok(true)
}
async fn channel_eof(
&mut self,
channel: ChannelId,
session: &mut Session,
) -> Result<(), Self::Error> {
// After a client has sent an EOF, indicating that they don't want
// to send more data in this session, the channel can be closed.
session.close(channel);
Ok(())
}
async fn subsystem_request(
&mut self,
channel_id: ChannelId,
name: &str,
session: &mut Session,
) -> Result<(), Self::Error> {
info!("subsystem: {}", name);
if name == "sftp" {
let channel = self.get_channel(channel_id).await;
let sftp = SftpSession::default();
session.channel_success(channel_id);
russh_sftp::server::run(channel.into_stream(), sftp).await;
} else {
session.channel_failure(channel_id);
}
Ok(())
}
}
#[derive(Default)]
struct SftpSession {
version: Option<u32>,
root_dir_read_done: bool,
}
#[async_trait]
impl russh_sftp::server::Handler for SftpSession {
type Error = StatusCode;
fn unimplemented(&self) -> Self::Error {
StatusCode::OpUnsupported
}
async fn init(
&mut self,
version: u32,
extensions: HashMap<String, String>,
) -> Result<Version, Self::Error> {
if self.version.is_some() {
error!("duplicate SSH_FXP_VERSION packet");
return Err(StatusCode::ConnectionLost);
}
self.version = Some(version);
info!("version: {:?}, extensions: {:?}", self.version, extensions);
Ok(Version::new())
}
async fn close(&mut self, id: u32, _handle: String) -> Result<Status, Self::Error> {
Ok(Status {
id,
status_code: StatusCode::Ok,
error_message: "Ok".to_string(),
language_tag: "en-US".to_string(),
})
}
async fn opendir(&mut self, id: u32, path: String) -> Result<Handle, Self::Error> {
info!("opendir: {}", path);
self.root_dir_read_done = false;
Ok(Handle { id, handle: path })
}
async fn readdir(&mut self, id: u32, handle: String) -> Result<Name, Self::Error> {
info!("readdir handle: {}", handle);
if handle == "/" && !self.root_dir_read_done {
self.root_dir_read_done = true;
return Ok(Name {
id,
files: vec![
File::new("foo", FileAttributes::default()),
File::new("bar", FileAttributes::default()),
],
});
}
// If all files have been sent to the client, respond with an EOF
Err(StatusCode::Eof)
}
async fn realpath(&mut self, id: u32, path: String) -> Result<Name, Self::Error> {
info!("realpath: {}", path);
Ok(Name {
id,
files: vec![File::dummy("/")],
})
}
}
#[tokio::main]
async fn main() {
env_logger::builder()
.filter_level(LevelFilter::Debug)
.init();
let config = russh::server::Config {
auth_rejection_time: Duration::from_secs(3),
auth_rejection_time_initial: Some(Duration::from_secs(0)),
keys: vec![KeyPair::generate_ed25519()],
..Default::default()
};
let mut server = Server;
server
.run_on_address(
Arc::new(config),
(
"0.0.0.0",
std::env::var("PORT")
.unwrap_or("22".to_string())
.parse()
.unwrap(),
),
)
.await
.unwrap();
}