diff options
Diffstat (limited to 'src/linux/mod.rs')
| -rw-r--r-- | src/linux/mod.rs | 53 |
1 files changed, 44 insertions, 9 deletions
diff --git a/src/linux/mod.rs b/src/linux/mod.rs index 68bb3a8..fd9c471 100644 --- a/src/linux/mod.rs +++ b/src/linux/mod.rs @@ -9,12 +9,14 @@ use std::{path::Path, time::Duration}; pub struct LinuxRuntime { routing: String, applied: Option<Trojan>, + armed: bool, } impl Default for LinuxRuntime { fn default() -> Self { Self { routing: "unknown".into(), applied: None, + armed: false, } } } @@ -25,17 +27,39 @@ impl Runtime for LinuxRuntime { xray::preflight().await?; firewall::preflight()?; routes::preflight().await?; + anyhow::ensure!( + self.applied.is_some() || routes::tun_index().await?.is_none(), + "roentgen0 already exists outside the active owned configuration; resolve the interface conflict before enabling" + ); let (uid, _) = xray::identity()?; firewall::apply(true, uid)?; // Persist the boot guard input before recording enabled intent in SQLite. write_guard(true)?; self.routing = "blocked".into(); + self.armed = true; + Ok(()) + } + async fn block(&mut self) -> Result<()> { + firewall::apply(true, xray::identity()?.0)?; + write_guard(true)?; + self.routing = "blocked".into(); + self.applied = None; + xray::systemctl(&["stop", "roentgen-xray.service"]).await?; + routes::remove().await?; Ok(()) } async fn apply(&mut self, connection: Option<&Trojan>, _: u64) -> Result<()> { - if self.applied.as_ref() == connection { + if connection.is_none() { + return self.block().await; + } + anyhow::ensure!( + self.armed, + "Activation requires successful compatibility checks and an armed guard" + ); + if connection.is_some() && self.applied.as_ref() == connection { if let Some(index) = routes::tun_index().await? { routes::capture(index).await?; + firewall::forward(xray::identity()?.0)?; self.routing = "tun".into(); return Ok(()); } @@ -45,14 +69,22 @@ impl Runtime for LinuxRuntime { xray::systemctl(&["stop", "roentgen-xray.service"]).await?; routes::remove().await?; if let Some(connection) = connection { + anyhow::ensure!( + routes::tun_index().await?.is_none(), + "roentgen0 remained after stopping the owned Xray unit; refusing to take over another interface" + ); + xray::preflight().await?; + firewall::preflight()?; + routes::preflight().await?; xray::write_config( Path::new("/run/roentgen/xray.json"), - &xray::configuration(connection, true, None), + &xray::configuration(connection, true), )?; xray::systemctl(&["start", "roentgen-xray.service"]).await?; for _ in 0..100 { if let Some(index) = routes::tun_index().await? { routes::capture(index).await?; + firewall::forward(xray::identity()?.0)?; self.routing = "tun".into(); self.applied = Some(connection.clone()); return Ok(()); @@ -64,14 +96,16 @@ impl Runtime for LinuxRuntime { Ok(()) } async fn maintain(&mut self, connection: Option<&Trojan>, generation: u64) -> Result<()> { - match routes::tun_index().await? { - Some(index) if connection.is_some() => { - routes::capture(index).await?; - self.routing = "tun".into(); - Ok(()) - } - _ => self.apply(connection, generation).await, + if connection.is_none() { + return self.block().await; + } + if !self.armed { + self.arm().await?; } + self.apply(connection, generation).await + } + async fn probe(&mut self, connection: &Trojan, duration: u64) -> Result<xray::Probe> { + xray::Probe::start(connection, duration).await } async fn disable(&mut self) -> Result<()> { xray::systemctl(&["stop", "roentgen-xray.service"]).await?; @@ -80,6 +114,7 @@ impl Runtime for LinuxRuntime { write_guard(false)?; firewall::apply(false, xray::identity()?.0)?; self.routing = "direct".into(); + self.armed = false; Ok(()) } fn routing(&self) -> &str { |