use std::io::Write;
use std::path::Path;
use anyhow::Context;
use log::warn;
use serde::{Deserialize, Serialize};
use which::which;
use crate::network::{netns::NetworkNamespace, wireguard_config::WireguardPeer};
use super::{TrojanHost, get_cert, trojan_config::TrojanConfig};
#[derive(Debug, Serialize, Deserialize)]
pub struct Trojan {
pub(crate) pid: u32,
pub config: TrojanConfig,
#[serde(skip)]
_temporary_files: Vec<tempfile::NamedTempFile>,
#[serde(skip)]
cleanup_enabled: bool,
}
impl Trojan {
pub fn run_in_netns(
netns: &NetworkNamespace,
host: Option<TrojanHost>,
password: Option<&str>,
config_path: Option<&Path>,
no_verify: bool,
peer: Option<WireguardPeer>,
) -> anyhow::Result<Trojan> {
let mut config = TrojanConfig::new(config_path)?;
let config_path_buf;
let mut temporary_files = Vec::new();
if let Some(cpath) = config_path {
warn!("Using custom Trojan config file: {cpath:?}");
config_path_buf = config_path.unwrap().to_path_buf();
} else {
let h = host
.clone()
.expect("Host must be provided if no config path is given");
if h.is_ip() {
log::error!(
"IP address provided for trojan host, but SSL verification is not disabled. Disabling SSL verification."
);
} else {
let cert = get_cert::get_cert(h.host(), h.port())?;
temporary_files.push(config.set_cert(&cert)?);
}
config.set_verify_fields(!no_verify);
config.set_remote_fields(&host.unwrap());
config.set_password(password.unwrap());
config.set_wg_forwarding_fields(peer.as_ref().unwrap());
let mut config_file = tempfile::NamedTempFile::new()
.context("Failed to create temporary Trojan config")?;
config_file.write_all(serde_json::to_string(&config)?.as_bytes())?;
config_path_buf = config_file.path().to_path_buf();
temporary_files.push(config_file);
}
let trojan_exec = which("trojan").with_context(|| {
"trojan executable not found in PATH. Please install trojan and ensure it is in your PATH."
})?;
let handle = NetworkNamespace::exec_no_block(
&netns.name,
&[
trojan_exec.to_str().unwrap(),
"-c",
config_path_buf.to_str().unwrap(),
],
None, None,
false,
false,
false,
None,
)?;
Ok(Trojan {
pid: handle.id(),
config,
_temporary_files: temporary_files,
cleanup_enabled: true,
})
}
}
impl Drop for Trojan {
fn drop(&mut self) {
if !self.cleanup_enabled {
return;
}
nix::sys::signal::kill(
nix::unistd::Pid::from_raw(self.pid as i32),
nix::sys::signal::Signal::SIGTERM,
)
.expect("Failed to kill trojan process");
}
}
impl Trojan {
pub(crate) fn set_cleanup_enabled(&mut self, enabled: bool) {
self.cleanup_enabled = enabled;
}
}