use crate::{
Result, QsshError,
audit::AuditLogger,
crypto::PqKeyExchange,
transport::{Transport, Message, ChannelMessage, ChannelType,
GlobalRequestMessage, GlobalRequestType, GlobalRequestSuccessMessage},
handshake::ServerHandshake,
shell_handler_thread::ShellSessionThread,
port_forward::ForwardedChannelRouter,
};
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::{Mutex, mpsc};
#[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>,
pub quantum_native: bool,
pub qkd_cert_path: Option<String>,
pub qkd_key_path: Option<String>,
pub qkd_ca_path: Option<String>,
pub audit: AuditLogger,
}
impl QsshServerConfig {
pub fn new(listen_addr: &str) -> Result<Self> {
let host_key = PqKeyExchange::new()?;
let audit_path = std::env::var("QSSH_AUDIT_LOG")
.unwrap_or_else(|_| "/var/log/qssh/audit.jsonl".to_string());
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,
quantum_native: true, qkd_cert_path: None,
qkd_key_path: None,
qkd_ca_path: None,
audit: AuditLogger::new(std::path::PathBuf::from(audit_path)),
})
}
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();
let addr_str = addr.to_string();
tokio::spawn(async move {
if let Err(e) = handle_connection(stream, config, connections, &addr_str).await {
log::error!("Connection error: {}", e);
}
});
}
}
}
struct ClientConnection {
_username: String,
_transport: Transport,
channels: HashMap<u32, Channel>,
}
struct Channel {
_id: u32,
_channel_type: ChannelType,
pty: Option<PtyInfo>,
x11_display: Option<X11Display>,
}
#[derive(Clone)]
struct PtyInfo {
term: String,
width: u16,
height: u16,
}
#[derive(Clone)]
#[allow(dead_code)]
struct X11Display {
display: String,
}
#[derive(Clone)]
#[allow(dead_code)]
struct X11ForwardState {
display_number: u32,
auth_protocol: String,
auth_cookie: String,
single_connection: bool,
}
struct AgentForwardState {
socket_path: String,
listener_handle: Option<tokio::task::JoinHandle<()>>,
}
impl AgentForwardState {
fn cleanup(&mut self) {
if let Some(handle) = self.listener_handle.take() {
handle.abort();
}
let _ = std::fs::remove_file(&self.socket_path);
log::debug!("Cleaned up agent socket: {}", self.socket_path);
}
}
impl Drop for AgentForwardState {
fn drop(&mut self) {
self.cleanup();
}
}
struct RemoteForwardState {
listeners: HashMap<(String, u16), tokio::task::JoinHandle<()>>,
}
impl RemoteForwardState {
fn new() -> Self {
Self {
listeners: HashMap::new(),
}
}
fn abort_all(&mut self) {
for ((host, port), handle) in self.listeners.drain() {
log::info!("Cancelling remote forward listener on {}:{}", host, port);
handle.abort();
}
}
}
async fn handle_connection(
stream: TcpStream,
config: QsshServerConfig,
connections: Arc<Mutex<HashMap<String, ClientConnection>>>,
remote_addr: &str,
) -> Result<()> {
let audit = &config.audit;
audit.log("connect", None, Some(remote_addr), None, true).await;
let host_key = PqKeyExchange::new()?;
let handshake = ServerHandshake::new(stream, host_key)
.with_qkd_endpoint(config.qkd_endpoint.clone());
let (transport, username) = match handshake.perform().await {
Ok(result) => {
audit.log("auth", Some(&result.1), Some(remote_addr), Some("handshake success"), true).await;
result
}
Err(e) => {
audit.log("auth", None, Some(remote_addr), Some(&format!("handshake failed: {}", e)), false).await;
return Err(e);
}
};
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);
}
let remote_forward_state = Arc::new(Mutex::new(RemoteForwardState::new()));
let channel_router = ForwardedChannelRouter::new();
let x11_state: Arc<Mutex<Option<X11ForwardState>>> = Arc::new(Mutex::new(None));
let agent_state: Arc<Mutex<Option<AgentForwardState>>> = Arc::new(Mutex::new(None));
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,
&remote_forward_state, &channel_router,
&x11_state, &agent_state, audit,
).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 rfs = remote_forward_state.lock().await;
rfs.abort_all();
}
{
let mut afs = agent_state.lock().await;
if let Some(ref mut state) = *afs {
state.cleanup();
}
}
{
let mut conns = connections.lock().await;
conns.remove(&username);
}
audit.log("disconnect", Some(&username), Some(remote_addr), None, true).await;
log::info!("User {} disconnected", username);
Ok(())
}
#[allow(clippy::too_many_arguments)]
async fn handle_client_message(
msg: Message,
transport: &Transport,
username: &str,
connections: &Arc<Mutex<HashMap<String, ClientConnection>>>,
remote_forward_state: &Arc<Mutex<RemoteForwardState>>,
channel_router: &ForwardedChannelRouter,
x11_state: &Arc<Mutex<Option<X11ForwardState>>>,
agent_state: &Arc<Mutex<Option<AgentForwardState>>>,
audit: &AuditLogger,
) -> Result<()> {
match msg {
Message::Channel(channel_msg) => {
handle_channel_message(channel_msg, transport, username, connections, channel_router, x11_state, agent_state, audit).await?;
}
Message::GlobalRequest(req) => {
handle_global_request(req, transport, username, remote_forward_state, channel_router).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(_) => {
log::warn!("Client {} sent a legacy (0.1) rekey; refused", username);
return Err(QsshError::Protocol("Legacy rekey is not supported".into()));
}
_ => {
log::debug!("Unhandled message from {}", username);
}
}
Ok(())
}
async fn handle_global_request(
req: GlobalRequestMessage,
transport: &Transport,
username: &str,
remote_forward_state: &Arc<Mutex<RemoteForwardState>>,
channel_router: &ForwardedChannelRouter,
) -> Result<()> {
match req.request_type {
GlobalRequestType::TcpipForward { bind_host, bind_port } => {
log::info!("User {} requesting tcpip-forward on {}:{}", username, bind_host, bind_port);
let bind_addr = if bind_host.is_empty() || bind_host == "0.0.0.0" {
format!("0.0.0.0:{}", bind_port)
} else if bind_host == "localhost" || bind_host == "127.0.0.1" {
format!("127.0.0.1:{}", bind_port)
} else {
format!("{}:{}", bind_host, bind_port)
};
match TcpListener::bind(&bind_addr).await {
Ok(listener) => {
let actual_port = listener.local_addr()
.map(|a| a.port())
.unwrap_or(bind_port);
log::info!("Remote forward listener bound on {} (actual port {})", bind_addr, actual_port);
if req.want_reply {
transport.send_message(&Message::GlobalRequestSuccess(
GlobalRequestSuccessMessage { bound_port: actual_port }
)).await?;
}
let transport_clone = transport.clone();
let router_clone = channel_router.clone();
let bind_host_clone = bind_host.clone();
let handle = tokio::spawn(async move {
handle_remote_forward_listener(
listener, transport_clone, router_clone,
bind_host_clone, actual_port,
).await;
});
let mut rfs = remote_forward_state.lock().await;
rfs.listeners.insert((bind_host, actual_port), handle);
}
Err(e) => {
log::error!("Failed to bind remote forward on {}: {}", bind_addr, e);
if req.want_reply {
transport.send_message(&Message::GlobalRequestFailure).await?;
}
}
}
}
GlobalRequestType::CancelTcpipForward { bind_host, bind_port } => {
log::info!("User {} cancelling tcpip-forward on {}:{}", username, bind_host, bind_port);
let mut rfs = remote_forward_state.lock().await;
if let Some(handle) = rfs.listeners.remove(&(bind_host.clone(), bind_port)) {
handle.abort();
log::info!("Cancelled remote forward on {}:{}", bind_host, bind_port);
if req.want_reply {
transport.send_message(&Message::GlobalRequestSuccess(
GlobalRequestSuccessMessage { bound_port: bind_port }
)).await?;
}
} else {
log::warn!("No active remote forward on {}:{}", bind_host, bind_port);
if req.want_reply {
transport.send_message(&Message::GlobalRequestFailure).await?;
}
}
}
}
Ok(())
}
async fn handle_remote_forward_listener(
listener: TcpListener,
transport: Transport,
channel_router: ForwardedChannelRouter,
bind_host: String,
bind_port: u16,
) {
loop {
match listener.accept().await {
Ok((stream, peer_addr)) => {
log::info!("Remote forward connection from {} on {}:{}", peer_addr, bind_host, bind_port);
let transport = transport.clone();
let router = channel_router.clone();
let bind_host = bind_host.clone();
let originator_host = peer_addr.ip().to_string();
let originator_port = peer_addr.port();
tokio::spawn(async move {
if let Err(e) = handle_remote_forward_connection(
stream, transport, router, bind_host, bind_port,
originator_host, originator_port,
).await {
log::error!("Remote forward connection error: {}", e);
}
});
}
Err(e) => {
log::error!("Remote forward accept error on {}:{}: {}", bind_host, bind_port, e);
break;
}
}
}
}
async fn handle_remote_forward_connection(
tcp_stream: TcpStream,
transport: Transport,
channel_router: ForwardedChannelRouter,
bind_host: String,
bind_port: u16,
originator_host: String,
originator_port: u16,
) -> Result<()> {
let channel_id = rand::random::<u32>() % 65536;
let (data_tx, mut data_rx) = mpsc::channel::<Vec<u8>>(256);
channel_router.register(channel_id, data_tx).await;
let open_msg = Message::Channel(ChannelMessage::Open {
channel_id,
channel_type: ChannelType::ForwardedTcpip {
connected_host: bind_host.clone(),
connected_port: bind_port,
originator_host: originator_host.clone(),
originator_port,
},
window_size: 1024 * 1024,
max_packet_size: 32768,
});
transport.send_message(&open_msg).await?;
let accept_timeout = tokio::time::timeout(
std::time::Duration::from_secs(10),
data_rx.recv(),
).await;
match accept_timeout {
Ok(Some(data)) if data.is_empty() => {
log::debug!("Client accepted forwarded channel {}", channel_id);
}
Ok(Some(_)) => {
log::warn!("Got data before accept on channel {}", channel_id);
}
_ => {
channel_router.remove(channel_id).await;
return Err(QsshError::Protocol("Timeout waiting for channel accept from client".into()));
}
}
let (mut tcp_read, mut tcp_write) = tcp_stream.into_split();
let transport_send = transport.clone();
let router_clone = channel_router.clone();
let tcp_to_channel = tokio::spawn(async move {
let mut buffer = vec![0u8; 8192];
loop {
match tcp_read.read(&mut buffer).await {
Ok(0) => break,
Ok(n) => {
let data_msg = Message::Channel(ChannelMessage::Data {
channel_id,
data: buffer[..n].to_vec(),
});
if transport_send.send_message(&data_msg).await.is_err() {
break;
}
}
Err(_) => break,
}
}
router_clone.remove(channel_id).await;
});
let channel_to_tcp = tokio::spawn(async move {
while let Some(data) = data_rx.recv().await {
if data.is_empty() {
continue; }
if tcp_write.write_all(&data).await.is_err() {
break;
}
}
});
tokio::select! {
_ = tcp_to_channel => {}
_ = channel_to_tcp => {}
}
channel_router.remove(channel_id).await;
Ok(())
}
#[allow(clippy::too_many_arguments)]
async fn handle_channel_message(
msg: ChannelMessage,
transport: &Transport,
username: &str,
connections: &Arc<Mutex<HashMap<String, ClientConnection>>>,
channel_router: &ForwardedChannelRouter,
x11_state: &Arc<Mutex<Option<X11ForwardState>>>,
agent_state: &Arc<Mutex<Option<AgentForwardState>>>,
audit: &AuditLogger,
) -> Result<()> {
match msg {
ChannelMessage::Open { channel_id, channel_type, window_size, max_packet_size } => {
log::info!("User {} opening channel {} ({:?})", username, channel_id, channel_type);
if let ChannelType::DirectTcpip { ref host, port, .. } = channel_type {
let target_host = host.clone();
let target_port = port;
let target_addr = format!("{}:{}", target_host, target_port);
log::info!("DirectTcpip forward: connecting to {}", target_addr);
match TcpStream::connect(&target_addr).await {
Ok(tcp_stream) => {
let accept = Message::Channel(ChannelMessage::Accept {
channel_id,
sender_channel: channel_id,
window_size,
max_packet_size,
});
transport.send_message(&accept).await?;
let (data_tx, mut data_rx) = mpsc::channel::<Vec<u8>>(256);
channel_router.register(channel_id, data_tx).await;
let transport_bridge = transport.clone();
let channel_router_bridge = channel_router.clone();
tokio::spawn(async move {
let (mut tcp_read, mut tcp_write) = tcp_stream.into_split();
let transport_out = transport_bridge.clone();
let tcp_to_channel = tokio::spawn(async move {
let mut buf = vec![0u8; 8192];
loop {
match tcp_read.read(&mut buf).await {
Ok(0) => break,
Ok(n) => {
let msg = Message::Channel(ChannelMessage::Data {
channel_id,
data: buf[..n].to_vec(),
});
if transport_out.send_message(&msg).await.is_err() {
break;
}
}
Err(_) => break,
}
}
});
let channel_to_tcp = tokio::spawn(async move {
while let Some(data) = data_rx.recv().await {
if data.is_empty() { continue; }
if tcp_write.write_all(&data).await.is_err() {
break;
}
}
});
tokio::select! {
_ = tcp_to_channel => {}
_ = channel_to_tcp => {}
}
channel_router_bridge.remove(channel_id).await;
let eof = Message::Channel(ChannelMessage::Eof { channel_id });
let _ = transport_bridge.send_message(&eof).await;
log::debug!("DirectTcpip bridge ended for channel {}", channel_id);
});
}
Err(e) => {
log::error!("Failed to connect to {}: {}", target_addr, e);
let close = Message::Channel(ChannelMessage::Close { channel_id });
transport.send_message(&close).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: channel_type,
pty: None,
x11_display: None,
});
}
return Ok(());
}
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: channel_type,
pty: None,
x11_display: None,
});
}
}
ChannelMessage::Accept { channel_id, .. } => {
if channel_router.has_channel(channel_id).await {
log::debug!("Routing Accept for forwarded channel {} via router", channel_id);
channel_router.route_data(channel_id, Vec::new()).await;
}
}
ChannelMessage::Data { channel_id, data } => {
if channel_router.has_channel(channel_id).await {
channel_router.route_data(channel_id, data).await;
} else {
log::debug!("User {} sent {} bytes on channel {}", username, data.len(), channel_id);
}
}
ChannelMessage::Eof { channel_id } => {
if channel_router.has_channel(channel_id).await {
channel_router.remove(channel_id).await;
}
}
ChannelMessage::Close { channel_id } => {
log::info!("User {} closing channel {}", username, channel_id);
if channel_router.has_channel(channel_id).await {
channel_router.remove(channel_id).await;
}
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 mut conns = connections.lock().await;
if let Some(conn) = conns.get_mut(username) {
if let Some(ch) = conn.channels.get_mut(&channel_id) {
ch.pty = Some(PtyInfo {
term: term.clone(),
width: width_chars as u16,
height: height_chars as u16,
});
}
}
}
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 pty_info = {
let conns = connections.lock().await;
conns.get(username)
.and_then(|conn| conn.channels.get(&channel_id))
.and_then(|ch| ch.pty.clone())
};
let (term, width, height) = match pty_info {
Some(info) => (info.term, info.width, info.height),
None => ("xterm-256color".to_string(), 80, 24),
};
match ShellSessionThread::new(
channel_id,
transport.clone(),
username.to_string(),
Some(term),
width,
height,
).await {
Ok(mut session) => {
log::info!("Starting shell session for user {} on channel {}", username, channel_id);
log::info!("Shell handler taking over transport - running inline");
{
let afs = agent_state.lock().await;
if let Some(ref state) = *afs {
session.set_env("SSH_AUTH_SOCK", &state.socket_path);
log::info!("Agent forwarding: SSH_AUTH_SOCK={}", state.socket_path);
}
}
let (_fwd_tx, _fwd_rx) = mpsc::channel(16);
session.set_channel_router(channel_router.clone(), _fwd_tx);
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);
audit.log("exec", Some(username), None, Some(&command), true).await;
let transport_exec = transport.clone();
let username_exec = username.to_string();
let audit_exec = audit.clone();
tokio::spawn(async move {
if let Err(e) = handle_exec_request(channel_id, command, &transport_exec, &username_exec, &audit_exec).await {
log::error!("Exec error for {}: {}", username_exec, e);
}
});
}
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?;
}
ChannelMessage::X11Request { channel_id, single_connection, auth_protocol, auth_cookie, screen_number } => {
log::info!("User {} requesting X11 forwarding on channel {} (screen {})",
username, channel_id, screen_number);
let mut display_number = 10u32;
for dn in 10u32..100 {
let port = 6000 + dn as u16;
if TcpListener::bind(format!("127.0.0.1:{}", port)).await.is_ok() {
display_number = dn;
break;
}
}
{
let mut state = x11_state.lock().await;
*state = Some(X11ForwardState {
display_number,
auth_protocol: auth_protocol.clone(),
auth_cookie: auth_cookie.clone(),
single_connection,
});
}
{
let mut conns = connections.lock().await;
if let Some(conn) = conns.get_mut(username) {
if let Some(ch) = conn.channels.get_mut(&channel_id) {
ch.x11_display = Some(X11Display {
display: format!("localhost:{}.0", display_number),
});
}
}
}
let x11_port = 6000 + display_number as u16;
match TcpListener::bind(format!("127.0.0.1:{}", x11_port)).await {
Ok(listener) => {
log::info!("X11 forwarding: listening on 127.0.0.1:{} (DISPLAY=:{}.{})",
x11_port, display_number, screen_number);
let transport_x11 = transport.clone();
let single = single_connection;
tokio::spawn(async move {
loop {
match listener.accept().await {
Ok((stream, peer)) => {
log::debug!("X11 connection from {} on display :{}", peer, display_number);
let transport_conn = transport_x11.clone();
tokio::spawn(async move {
if let Err(e) = handle_x11_server_connection(
stream, transport_conn,
).await {
log::debug!("X11 connection ended: {}", e);
}
});
if single {
log::info!("X11 single-connection mode — stopping listener");
break;
}
}
Err(e) => {
log::error!("X11 accept error: {}", e);
break;
}
}
}
});
let ack = Message::Channel(ChannelMessage::Data {
channel_id,
data: vec![0], });
transport.send_message(&ack).await?;
}
Err(e) => {
log::error!("Failed to bind X11 listener on port {}: {}", x11_port, e);
}
}
}
ChannelMessage::ForwardRequest { channel_id, remote_host, remote_port } => {
let target_addr = format!("{}:{}", remote_host, remote_port);
log::info!("User {} ForwardRequest on channel {}: {}", username, channel_id, target_addr);
match TcpStream::connect(&target_addr).await {
Ok(tcp_stream) => {
let (data_tx, mut data_rx) = mpsc::channel::<Vec<u8>>(256);
channel_router.register(channel_id, data_tx).await;
let transport_bridge = transport.clone();
let channel_router_bridge = channel_router.clone();
tokio::spawn(async move {
let (mut tcp_read, mut tcp_write) = tcp_stream.into_split();
let transport_out = transport_bridge.clone();
let tcp_to_channel = tokio::spawn(async move {
let mut buf = vec![0u8; 8192];
loop {
match tcp_read.read(&mut buf).await {
Ok(0) => break,
Ok(n) => {
let msg = Message::Channel(ChannelMessage::Data {
channel_id,
data: buf[..n].to_vec(),
});
if transport_out.send_message(&msg).await.is_err() {
break;
}
}
Err(_) => break,
}
}
});
let channel_to_tcp = tokio::spawn(async move {
while let Some(data) = data_rx.recv().await {
if data.is_empty() { continue; }
if tcp_write.write_all(&data).await.is_err() {
break;
}
}
});
tokio::select! {
_ = tcp_to_channel => {}
_ = channel_to_tcp => {}
}
channel_router_bridge.remove(channel_id).await;
let eof = Message::Channel(ChannelMessage::Eof { channel_id });
let _ = transport_bridge.send_message(&eof).await;
log::debug!("ForwardRequest bridge ended for channel {}", channel_id);
});
}
Err(e) => {
log::error!("ForwardRequest: failed to connect to {}: {}", target_addr, e);
let close = Message::Channel(ChannelMessage::Close { channel_id });
transport.send_message(&close).await?;
}
}
}
ChannelMessage::AgentForwardRequest { channel_id } => {
log::info!("User {} requesting agent forwarding on channel {}", username, channel_id);
audit.log("agent_forward", Some(username), None, None, true).await;
let socket_dir = format!("/tmp/qssh-agent-{}", std::process::id());
let _ = std::fs::create_dir_all(&socket_dir);
let socket_path = format!("{}/agent.{}", socket_dir, channel_id);
let _ = std::fs::remove_file(&socket_path);
match tokio::net::UnixListener::bind(&socket_path) {
Ok(listener) => {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let _ = std::fs::set_permissions(
&socket_path,
std::fs::Permissions::from_mode(0o600),
);
}
log::info!("Agent forwarding socket created: {}", socket_path);
let success = Message::Channel(ChannelMessage::AgentForwardSuccess {
channel_id,
socket_path: socket_path.clone(),
});
transport.send_message(&success).await?;
let transport_agent = transport.clone();
let socket_path_clone = socket_path.clone();
let handle = tokio::spawn(async move {
let mut next_agent_channel = 10000u32; loop {
match listener.accept().await {
Ok((stream, _)) => {
let agent_ch = next_agent_channel;
next_agent_channel += 1;
log::debug!("Agent socket connection -> channel {}", agent_ch);
let open_msg = Message::Channel(ChannelMessage::Open {
channel_id: agent_ch,
channel_type: ChannelType::AgentForward,
window_size: 1048576,
max_packet_size: 32768,
});
if transport_agent.send_message(&open_msg).await.is_err() {
break;
}
let t = transport_agent.clone();
tokio::spawn(async move {
if let Err(e) = bridge_agent_connection(agent_ch, stream, t).await {
log::debug!("Agent bridge {} ended: {}", agent_ch, e);
}
});
}
Err(e) => {
log::debug!("Agent listener ended: {}", e);
break;
}
}
}
let _ = std::fs::remove_file(&socket_path_clone);
});
let mut afs = agent_state.lock().await;
*afs = Some(AgentForwardState {
socket_path: socket_path.clone(),
listener_handle: Some(handle),
});
}
Err(e) => {
log::error!("Failed to create agent socket: {}", e);
}
}
}
_ => {
log::debug!("Unhandled channel message from {}", username);
}
}
Ok(())
}
async fn handle_x11_server_connection(
stream: TcpStream,
transport: Transport,
) -> Result<()> {
let channel_id = rand::random::<u32>() % 65536;
let open_msg = Message::Channel(ChannelMessage::Open {
channel_id,
channel_type: ChannelType::X11,
window_size: 1024 * 1024,
max_packet_size: 32768,
});
transport.send_message(&open_msg).await?;
let (mut tcp_read, mut tcp_write) = stream.into_split();
let transport_send = transport.clone();
let tcp_to_channel = tokio::spawn(async move {
let mut buf = vec![0u8; 8192];
loop {
match tcp_read.read(&mut buf).await {
Ok(0) => break,
Ok(n) => {
let msg = Message::Channel(ChannelMessage::Data {
channel_id,
data: buf[..n].to_vec(),
});
if transport_send.send_message(&msg).await.is_err() {
break;
}
}
Err(_) => break,
}
}
});
let channel_to_tcp = tokio::spawn(async move {
loop {
match transport.receive_message::<Message>().await {
Ok(Message::Channel(ChannelMessage::Data { channel_id: ch_id, data }))
if ch_id == channel_id =>
{
if tcp_write.write_all(&data).await.is_err() {
break;
}
}
Ok(Message::Channel(ChannelMessage::Eof { channel_id: ch_id }))
| Ok(Message::Channel(ChannelMessage::Close { channel_id: ch_id }))
if ch_id == channel_id =>
{
break;
}
Ok(Message::Disconnect(_)) | Err(_) => break,
Ok(_) => continue,
}
}
});
tokio::select! {
_ = tcp_to_channel => {}
_ = channel_to_tcp => {}
}
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,
audit: &AuditLogger,
) -> 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?;
}
let exit_code = output.status.code().unwrap_or(255) as u32;
audit.log("exec_exit", Some(username), None,
Some(&format!("exit_code={}", exit_code)), exit_code == 0).await;
let exit_msg = Message::Channel(ChannelMessage::ExitStatus {
channel_id,
exit_code,
});
transport.send_message(&exit_msg).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 exit_msg = Message::Channel(ChannelMessage::ExitStatus {
channel_id,
exit_code: 255,
});
transport.send_message(&exit_msg).await?;
}
}
let eof = Message::Channel(ChannelMessage::Eof { channel_id });
transport.send_message(&eof).await?;
Ok(())
}
async fn bridge_agent_connection(
channel_id: u32,
stream: tokio::net::UnixStream,
transport: Transport,
) -> Result<()> {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let (mut read_half, mut write_half) = tokio::io::split(stream);
let transport_read = transport.clone();
let transport_close = transport.clone();
let sock_to_chan = tokio::spawn(async move {
let mut buf = vec![0u8; 32768];
loop {
match read_half.read(&mut buf).await {
Ok(0) => break,
Ok(n) => {
let msg = Message::Channel(ChannelMessage::Data {
channel_id,
data: buf[..n].to_vec(),
});
if transport_read.send_message(&msg).await.is_err() {
break;
}
}
Err(_) => break,
}
}
});
let chan_to_sock = tokio::spawn(async move {
loop {
match transport.receive_message::<Message>().await {
Ok(Message::Channel(ChannelMessage::Data { channel_id: ch, data })) if ch == channel_id => {
if write_half.write_all(&data).await.is_err() {
break;
}
}
Ok(Message::Channel(ChannelMessage::Close { channel_id: ch })) if ch == channel_id => {
break;
}
Ok(Message::Channel(ChannelMessage::Eof { channel_id: ch })) if ch == channel_id => {
break;
}
Err(_) => break,
_ => {} }
}
});
tokio::select! {
_ = sock_to_chan => {}
_ = chan_to_sock => {}
}
let close = Message::Channel(ChannelMessage::Close { channel_id });
let _ = transport_close.send_message(&close).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);
}
}