diff --git a/rust/connlib/shared/src/tun_device_manager/linux.rs b/rust/connlib/shared/src/tun_device_manager/linux.rs index abb49bbc2..cd35a10eb 100644 --- a/rust/connlib/shared/src/tun_device_manager/linux.rs +++ b/rust/connlib/shared/src/tun_device_manager/linux.rs @@ -164,7 +164,7 @@ impl TunDeviceManager { } for route in self.routes.difference(&new_routes) { - delete_route(route, index, handle).await?; + remove_route(route, index, handle).await?; } self.routes = new_routes; @@ -232,7 +232,7 @@ async fn add_route(route: &IpNetwork, idx: u32, handle: &Handle) -> Result<()> { Ok(()) } -async fn delete_route(route: &IpNetwork, idx: u32, handle: &Handle) -> Result<()> { +async fn remove_route(route: &IpNetwork, idx: u32, handle: &Handle) -> Result<()> { let message = match route { IpNetwork::V4(ipnet) => make_route_v4(idx, handle, *ipnet).message_mut().clone(), IpNetwork::V6(ipnet) => make_route_v6(idx, handle, *ipnet).message_mut().clone(), diff --git a/rust/connlib/tunnel/src/device_channel/tun_windows.rs b/rust/connlib/tunnel/src/device_channel/tun_windows.rs index 3c908cfa2..ea761cfd2 100644 --- a/rust/connlib/tunnel/src/device_channel/tun_windows.rs +++ b/rust/connlib/tunnel/src/device_channel/tun_windows.rs @@ -134,8 +134,6 @@ impl Tun { self.remove_route(*old_route)?; } - // TODO: Might be calling this more often than it needs - flush_dns().expect("Should be able to flush Windows' DNS cache"); self.routes = new_routes; Ok(()) } @@ -245,16 +243,6 @@ impl Tun { } } -/// Flush Windows' system-wide DNS cache -pub(crate) fn flush_dns() -> Result<()> { - tracing::info!("Flushing Windows DNS cache"); - Command::new("powershell") - .creation_flags(CREATE_NO_WINDOW) - .args(["-Command", "Clear-DnsClientCache"]) - .status()?; - Ok(()) -} - // Moves packets from the user towards the Internet fn start_recv_thread( packet_tx: mpsc::Sender, diff --git a/rust/headless-client/src/dns_control/linux.rs b/rust/headless-client/src/dns_control/linux.rs index 49b9d10d1..fa41aceb3 100644 --- a/rust/headless-client/src/dns_control/linux.rs +++ b/rust/headless-client/src/dns_control/linux.rs @@ -1,6 +1,6 @@ use anyhow::{bail, Context as _, Result}; use connlib_shared::tun_device_manager::linux::IFACE_NAME; -use std::{net::IpAddr, str::FromStr}; +use std::{net::IpAddr, process::Command, str::FromStr}; mod etc_resolv_conf; @@ -63,6 +63,19 @@ impl DnsController { } .context("Failed to control DNS") } + + /// Flush systemd-resolved's system-wide DNS cache + /// + /// Does nothing if we're using other DNS control methods or none at all + pub(crate) fn flush(&self) -> Result<()> { + // Flushing is only implemented for systemd-resolved + if matches!(self.dns_control_method, Some(DnsControlMethod::Systemd)) { + tracing::debug!("Flushing systemd-resolved DNS cache..."); + Command::new("resolvectl").arg("flush-caches").status()?; + tracing::debug!("Flushed DNS."); + } + Ok(()) + } } /// Reads FIREZONE_DNS_CONTROL. Returns None if invalid or not set diff --git a/rust/headless-client/src/dns_control/windows.rs b/rust/headless-client/src/dns_control/windows.rs index 19048f83f..b8a8958a9 100644 --- a/rust/headless-client/src/dns_control/windows.rs +++ b/rust/headless-client/src/dns_control/windows.rs @@ -51,6 +51,19 @@ impl DnsController { activate(dns_config).context("Failed to activate DNS control")?; Ok(()) } + + /// Flush Windows' system-wide DNS cache + /// + /// `&self` is needed to match the Linux signature + pub(crate) fn flush(&self) -> Result<()> { + tracing::debug!("Flushing Windows DNS cache..."); + Command::new("powershell") + .creation_flags(CREATE_NO_WINDOW) + .args(["-Command", "Clear-DnsClientCache"]) + .status()?; + tracing::debug!("Flushed DNS."); + Ok(()) + } } pub(crate) fn system_resolvers() -> Result> { diff --git a/rust/headless-client/src/ipc_service.rs b/rust/headless-client/src/ipc_service.rs index 88ba7ee98..055488241 100644 --- a/rust/headless-client/src/ipc_service.rs +++ b/rust/headless-client/src/ipc_service.rs @@ -209,6 +209,9 @@ impl Handler { let dur = instant.elapsed(); tracing::info!(?dur, "Connlib started"); } + + // On every resources update, flush DNS to mitigate + self.dns_controller.flush()?; } self.ipc_tx .send(&msg) diff --git a/rust/headless-client/src/lib.rs b/rust/headless-client/src/lib.rs index 6309d2d01..5d3c418ca 100644 --- a/rust/headless-client/src/lib.rs +++ b/rust/headless-client/src/lib.rs @@ -78,6 +78,7 @@ struct CliCommon { max_partition_time: Option, } +/// Messages we get from connlib, including ones that aren't sent to IPC clients enum InternalServerMsg { Ipc(IpcServerMsg), OnSetInterfaceConfig { @@ -91,6 +92,7 @@ enum InternalServerMsg { }, } +/// Messages that we can send to IPC clients #[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)] pub enum IpcServerMsg { Ok, diff --git a/rust/headless-client/src/standalone.rs b/rust/headless-client/src/standalone.rs index 23964ada7..5d0a70575 100644 --- a/rust/headless-client/src/standalone.rs +++ b/rust/headless-client/src/standalone.rs @@ -192,7 +192,10 @@ pub fn run_only_headless_client() -> Result<()> { }) => return Err(anyhow!(error_msg).context("Firezone disconnected")), InternalServerMsg::Ipc(IpcServerMsg::Ok) | InternalServerMsg::Ipc(IpcServerMsg::OnTunnelReady) - | InternalServerMsg::Ipc(IpcServerMsg::OnUpdateResources(_)) => {} + | InternalServerMsg::Ipc(IpcServerMsg::OnUpdateResources(_)) => { + // On every resources update, flush DNS to mitigate + dns_controller.flush()?; + } InternalServerMsg::OnSetInterfaceConfig { ipv4, ipv6, dns } => { tun_device.set_ips(ipv4, ipv6).await?; dns_controller.set_dns(&dns).await?;