use clap::Parser;
use qssh::{QsshConfig, PortForward, PqAlgorithm, QsshClient};
use qssh::config::ConfigParser;
use qssh::port_forward::{PortForwardManager, ForwardType};
use log::info;
use std::process;
#[derive(Parser, Debug)]
#[clap(name = "qssh")]
#[clap(about = "Quantum-Secure Shell - Connect to remote systems with post-quantum security")]
#[clap(version = "0.1.0")]
struct Args {
destination: String,
#[clap(short = 'p', long, default_value = "22222")]
port: u16,
#[clap(long)]
qkd: bool,
#[clap(long)]
qkd_endpoint: Option<String>,
#[clap(long)]
qkd_cert: Option<String>,
#[clap(long)]
qkd_key: Option<String>,
#[clap(long)]
qkd_ca: Option<String>,
#[clap(short = 'L', long)]
local: Vec<String>,
#[clap(short = 'R', long)]
remote: Vec<String>,
#[clap(long, default_value = "sphincs")]
pq_algo: String,
#[clap(short = 'c', long)]
command: Option<String>,
#[clap(short = 'P', long)]
use_password: bool,
#[clap(short = 'X', long)]
x11: bool,
#[clap(short = 'Y', long)]
trusted_x11: bool,
#[clap(short, long)]
verbose: bool,
}
#[tokio::main]
async fn main() {
let args = Args::parse();
if args.verbose {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug"))
.target(env_logger::Target::Stderr)
.init();
} else {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn"))
.target(env_logger::Target::Stderr)
.init();
}
let config_parser = ConfigParser::load_default().unwrap_or_else(|e| {
if args.verbose {
eprintln!("Warning: Failed to load config file: {}", e);
}
ConfigParser::parse("").unwrap()
});
let (username, hostname) = if args.destination.contains('@') {
let parts: Vec<&str> = args.destination.split('@').collect();
if parts.len() != 2 {
eprintln!("Error: Invalid destination format. Use user@host or host");
process::exit(1);
}
(Some(parts[0].to_string()), parts[1].to_string())
} else {
(None, args.destination.clone())
};
let host_config = config_parser.get_host_config(&hostname);
let username = username.or(host_config.user).unwrap_or_else(|| {
eprintln!("Error: Username not specified. Use user@host or configure in ~/.qssh/config");
process::exit(1);
});
let port = if args.port != 22222 {
args.port
} else {
host_config.port.unwrap_or(22222)
};
let host = if let Some(h) = host_config.hostname {
format!("{}:{}", h, port)
} else {
format!("{}:{}", hostname, port)
};
let mut port_forwards = Vec::new();
for forward in args.local {
if let Some(pf) = parse_port_forward(&forward) {
port_forwards.push(pf);
} else {
eprintln!("Error: Invalid port forward format: {}", forward);
process::exit(1);
}
}
let pq_algorithm = match args.pq_algo.as_str() {
"sphincs" => PqAlgorithm::SphincsPlus,
"falcon" | "falcon512" => PqAlgorithm::Falcon512,
"falcon1024" => PqAlgorithm::Falcon1024,
"kyber" | "kyber512" | "kyber768" | "kyber1024" => {
eprintln!("Warning: Kyber algorithms are vulnerable to KyberSlash attacks.");
eprintln!("Using Falcon-512 instead for quantum security.");
PqAlgorithm::Falcon512
},
_ => {
eprintln!("Error: Unknown PQ algorithm: {}", args.pq_algo);
eprintln!("Available algorithms: sphincs, falcon512, falcon1024");
process::exit(1);
}
};
let password = if args.use_password {
eprint!("Password: ");
rpassword::read_password().ok()
} else {
None
};
let config = QsshConfig {
server: host.clone(),
username: username.clone(),
password,
port_forwards,
use_qkd: args.qkd,
qkd_endpoint: args.qkd_endpoint.clone(),
qkd_cert_path: args.qkd_cert.clone(),
qkd_key_path: args.qkd_key.clone(),
qkd_ca_path: args.qkd_ca.clone(),
pq_algorithm,
key_rotation_interval: 3600, };
info!("Connecting to {} as {}...", host, username);
info!("Using post-quantum algorithm: {:?}", pq_algorithm);
if args.qkd {
info!("QKD enabled");
if let Some(endpoint) = args.qkd_endpoint {
info!("QKD endpoint: {}", endpoint);
}
}
let mut client = QsshClient::new(config);
match client.connect().await {
Ok(()) => {
info!("Connected successfully!");
if args.x11 || args.trusted_x11 {
match client.enable_x11(args.trusted_x11).await {
Ok(()) => {
info!("X11 forwarding enabled");
}
Err(e) => {
eprintln!("Warning: Failed to enable X11 forwarding: {}", e);
}
}
}
if let Some(command) = args.command {
match client.exec(&command).await {
Ok(output) => {
println!("{}", output);
}
Err(e) => {
eprintln!("Command execution failed: {}", e);
process::exit(1);
}
}
} else {
match client.shell().await {
Ok(_) => {
info!("Shell session terminated normally");
}
Err(e) => {
eprintln!("Shell error: {}", e);
eprintln!("Error details: {:?}", e);
}
}
}
if let Err(e) = client.disconnect().await {
eprintln!("Error disconnecting: {}", e);
}
}
Err(e) => {
eprintln!("Connection failed: {}", e);
process::exit(1);
}
}
}
fn parse_port_forward(spec: &str) -> Option<PortForward> {
match PortForwardManager::parse_forward_spec(spec, "local") {
Ok(ForwardType::Local { bind_addr, remote_host, remote_port }) => {
Some(PortForward {
local_port: bind_addr.port(),
remote_host,
remote_port,
})
}
_ => None,
}
}