use crate::{
Result, QsshError,
crypto::PqKeyExchange,
transport::{Transport, Message, ChannelMessage, ChannelType},
handshake::ServerHandshake,
shell_handler_thread::ShellSessionThread,
};
use tokio::net::{TcpListener, TcpStream};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
#[derive(Clone)]
pub struct QsshServerConfig {
pub listen_addr: String,
pub host_key: Arc<PqKeyExchange>,
pub max_connections: usize,
pub authorized_keys: HashMap<String, Vec<u8>>, pub qkd_enabled: bool,
pub qkd_endpoint: Option<String>,
}
impl QsshServerConfig {
pub fn new(listen_addr: &str) -> Result<Self> {
let host_key = PqKeyExchange::new()?;
Ok(Self {
listen_addr: listen_addr.to_string(),
host_key: Arc::new(host_key),
max_connections: 100,
authorized_keys: HashMap::new(),
qkd_enabled: false,
qkd_endpoint: None,
})
}
pub fn add_authorized_key(&mut self, username: &str, public_key: Vec<u8>) {
self.authorized_keys.insert(username.to_string(), public_key);
}
}
pub struct QsshServer {
config: QsshServerConfig,
connections: Arc<Mutex<HashMap<String, ClientConnection>>>,
}
impl QsshServer {
pub fn new(config: QsshServerConfig) -> Self {
Self {
config,
connections: Arc::new(Mutex::new(HashMap::new())),
}
}
pub async fn start(&self) -> Result<()> {
let listener = TcpListener::bind(&self.config.listen_addr).await
.map_err(|e| QsshError::Connection(format!("Failed to bind: {}", e)))?;
log::info!("QSSH server listening on {}", self.config.listen_addr);
loop {
let (stream, addr) = listener.accept().await
.map_err(|e| QsshError::Connection(format!("Accept failed: {}", e)))?;
log::info!("New connection from {}", addr);
{
let connections = self.connections.lock().await;
if connections.len() >= self.config.max_connections {
log::warn!("Connection limit reached, rejecting {}", addr);
continue;
}
}
let config = self.config.clone();
let connections = self.connections.clone();
tokio::spawn(async move {
if let Err(e) = handle_connection(stream, config, connections).await {
log::error!("Connection error: {}", e);
}
});
}
}
}
struct ClientConnection {
username: String,
transport: Transport,
channels: HashMap<u32, Channel>,
}
struct Channel {
id: u32,
channel_type: ChannelType,
}
async fn handle_connection(
stream: TcpStream,
config: QsshServerConfig,
connections: Arc<Mutex<HashMap<String, ClientConnection>>>,
) -> Result<()> {
let host_key = PqKeyExchange::new()?;
let handshake = ServerHandshake::new(stream, host_key)
.with_qkd_endpoint(config.qkd_endpoint.clone());
let (transport, username) = handshake.perform().await?;
log::info!("User {} authenticated successfully", username);
let connection = ClientConnection {
username: username.clone(),
transport: transport.clone(),
channels: HashMap::new(),
};
{
let mut conns = connections.lock().await;
conns.insert(username.clone(), connection);
}
loop {
log::debug!("Main loop waiting for message...");
match transport.receive_message::<Message>().await {
Ok(msg) => {
log::debug!("Main loop received message: {:?}",
match &msg {
Message::Channel(ChannelMessage::ShellRequest { .. }) => "ShellRequest",
Message::Channel(ChannelMessage::Data { .. }) => "Data",
_ => "Other"
});
if let Err(e) = handle_client_message(msg, &transport, &username, &connections).await {
if let QsshError::Protocol(ref msg) = e {
if msg == "SHELL_SESSION_COMPLETE" {
log::info!("Shell session completed normally");
break;
}
}
log::error!("Error handling message: {}", e);
break;
}
}
Err(e) => {
log::error!("Transport error: {}", e);
break;
}
}
}
{
let mut conns = connections.lock().await;
conns.remove(&username);
}
log::info!("User {} disconnected", username);
Ok(())
}
async fn handle_client_message(
msg: Message,
transport: &Transport,
username: &str,
connections: &Arc<Mutex<HashMap<String, ClientConnection>>>,
) -> Result<()> {
match msg {
Message::Channel(channel_msg) => {
handle_channel_message(channel_msg, transport, username, connections).await?;
}
Message::Disconnect(d) => {
log::info!("Client {} disconnecting: {}", username, d.description);
return Err(QsshError::Connection("Client disconnected".into()));
}
Message::Ping(nonce) => {
transport.send_message(&Message::Pong(nonce)).await?;
}
Message::Rekey(rekey) => {
log::info!("Client {} requesting rekey", username);
}
_ => {
log::debug!("Unhandled message from {}", username);
}
}
Ok(())
}
async fn handle_channel_message(
msg: ChannelMessage,
transport: &Transport,
username: &str,
connections: &Arc<Mutex<HashMap<String, ClientConnection>>>,
) -> Result<()> {
match msg {
ChannelMessage::Open { channel_id, channel_type, window_size, max_packet_size } => {
log::info!("User {} opening channel {} ({:?})", username, channel_id, channel_type);
let accept = Message::Channel(ChannelMessage::Accept {
channel_id,
sender_channel: channel_id,
window_size,
max_packet_size,
});
transport.send_message(&accept).await?;
let mut conns = connections.lock().await;
if let Some(conn) = conns.get_mut(username) {
conn.channels.insert(channel_id, Channel {
id: channel_id,
channel_type,
});
}
}
ChannelMessage::Data { channel_id, data } => {
log::debug!("User {} sent {} bytes on channel {}", username, data.len(), channel_id);
}
ChannelMessage::Close { channel_id } => {
log::info!("User {} closing channel {}", username, channel_id);
let mut conns = connections.lock().await;
if let Some(conn) = conns.get_mut(username) {
conn.channels.remove(&channel_id);
}
}
ChannelMessage::PtyRequest { channel_id, term, width_chars, height_chars, .. } => {
log::info!("User {} requesting PTY on channel {} ({}x{} {})",
username, channel_id, width_chars, height_chars, term);
let success = Message::Channel(ChannelMessage::Data {
channel_id,
data: vec![0], });
transport.send_message(&success).await?;
}
ChannelMessage::ShellRequest { channel_id } => {
log::info!("User {} requesting shell on channel {}", username, channel_id);
let (width, height) = (80u16, 24u16);
match ShellSessionThread::new(
channel_id,
transport.clone(),
username.to_string(),
Some("xterm-256color".to_string()),
width,
height,
).await {
Ok(session) => {
log::info!("Starting shell session for user {} on channel {}", username, channel_id);
log::info!("Shell handler taking over transport - running inline");
if let Err(e) = session.run().await {
log::error!("Shell session error: {}", e);
}
log::info!("Shell session ended - returning special error to signal shell completion");
return Err(QsshError::Protocol("SHELL_SESSION_COMPLETE".into()));
}
Err(e) => {
log::error!("Failed to spawn shell: {}", e);
let error_msg = format!("Failed to spawn shell: {}\n", e).into_bytes();
let response = Message::Channel(ChannelMessage::Data {
channel_id,
data: error_msg,
});
transport.send_message(&response).await?;
}
}
}
ChannelMessage::ExecRequest { channel_id, command } => {
log::info!("User {} exec on channel {}: {}", username, channel_id, command);
handle_exec_request(channel_id, command, transport, username).await?;
}
ChannelMessage::SubsystemRequest { channel_id, subsystem } => {
log::info!("User {} requesting subsystem '{}' on channel {}", username, subsystem, channel_id);
handle_subsystem_request(channel_id, subsystem, transport, username).await?;
}
_ => {
log::debug!("Unhandled channel message from {}", username);
}
}
Ok(())
}
async fn handle_subsystem_request(
channel_id: u32,
subsystem: String,
transport: &Transport,
username: &str,
) -> Result<()> {
match subsystem.as_str() {
"sftp" => {
log::info!("Starting SFTP subsystem for user {}", username);
let mut sftp = crate::subsystems::sftp::SftpSubsystem::new_for_user(username.to_string());
if let Err(e) = sftp.run(channel_id, transport.clone()).await {
log::error!("SFTP subsystem error: {}", e);
let error_msg = format!("SFTP subsystem failed: {}\n", e).into_bytes();
let response = Message::Channel(ChannelMessage::Data {
channel_id,
data: error_msg,
});
transport.send_message(&response).await?;
}
let eof = Message::Channel(ChannelMessage::Eof { channel_id });
transport.send_message(&eof).await?;
}
_ => {
log::warn!("Unknown subsystem requested: {}", subsystem);
let error_msg = format!("Subsystem '{}' not supported\n", subsystem).into_bytes();
let response = Message::Channel(ChannelMessage::Data {
channel_id,
data: error_msg,
});
transport.send_message(&response).await?;
}
}
Ok(())
}
async fn handle_exec_request(
channel_id: u32,
command: String,
transport: &Transport,
username: &str,
) -> Result<()> {
log::info!("User {} executing: {}", username, command);
match tokio::process::Command::new("sh")
.arg("-c")
.arg(&command)
.output()
.await
{
Ok(output) => {
if !output.stdout.is_empty() {
let response = Message::Channel(ChannelMessage::Data {
channel_id,
data: output.stdout,
});
transport.send_message(&response).await?;
}
if !output.stderr.is_empty() {
let error_response = Message::Channel(ChannelMessage::Data {
channel_id,
data: output.stderr,
});
transport.send_message(&error_response).await?;
}
}
Err(e) => {
let error_msg = format!("Command failed: {}\n", e).into_bytes();
let response = Message::Channel(ChannelMessage::Data {
channel_id,
data: error_msg,
});
transport.send_message(&response).await?;
}
}
let eof = Message::Channel(ChannelMessage::Eof { channel_id });
transport.send_message(&eof).await?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_server_config() {
let config = QsshServerConfig::new("127.0.0.1:22222").expect("Failed to create server config");
assert_eq!(config.listen_addr, "127.0.0.1:22222");
assert_eq!(config.max_connections, 100);
}
}