From cdb8306fae89b76b2f6b4cbe239de629c3c198bb Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Wed, 6 Mar 2024 14:05:11 +0100 Subject: [PATCH] Add split tunnel test for windows --- mullvad-management-interface/src/client.rs | 9 +- test/test-manager/src/tests/split_tunnel.rs | 121 ++++++++++++++++---- 2 files changed, 100 insertions(+), 30 deletions(-) diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index f30b61317146..0020ca696989 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -16,7 +16,6 @@ use mullvad_types::{ version::AppVersionInfo, wireguard::{PublicKey, QuantumResistantState, RotationInterval}, }; -#[cfg(target_os = "windows")] use std::path::Path; use std::str::FromStr; #[cfg(target_os = "windows")] @@ -632,7 +631,7 @@ impl MullvadProxyClient { Ok(()) } - #[cfg(target_os = "windows")] + //#[cfg(target_os = "windows")] pub async fn add_split_tunnel_app>(&mut self, path: P) -> Result<()> { let path = path.as_ref().to_str().ok_or(Error::PathMustBeUtf8)?; self.0 @@ -642,7 +641,7 @@ impl MullvadProxyClient { Ok(()) } - #[cfg(target_os = "windows")] + //#[cfg(target_os = "windows")] pub async fn remove_split_tunnel_app>(&mut self, path: P) -> Result<()> { let path = path.as_ref().to_str().ok_or(Error::PathMustBeUtf8)?; self.0 @@ -652,7 +651,7 @@ impl MullvadProxyClient { Ok(()) } - #[cfg(target_os = "windows")] + //#[cfg(target_os = "windows")] pub async fn clear_split_tunnel_apps(&mut self) -> Result<()> { self.0 .clear_split_tunnel_apps(()) @@ -661,7 +660,7 @@ impl MullvadProxyClient { Ok(()) } - #[cfg(target_os = "windows")] + //#[cfg(target_os = "windows")] pub async fn set_split_tunnel_state(&mut self, state: bool) -> Result<()> { self.0 .set_split_tunnel_state(state) diff --git a/test/test-manager/src/tests/split_tunnel.rs b/test/test-manager/src/tests/split_tunnel.rs index 522af854863b..cdb96a5dd2c1 100644 --- a/test/test-manager/src/tests/split_tunnel.rs +++ b/test/test-manager/src/tests/split_tunnel.rs @@ -5,52 +5,110 @@ use test_rpc::{ExecResult, ServiceClient}; use super::{helpers, TestContext}; -const AM_I_MULLVAD: &str = "https://am.i.mullvad.net/connected"; - -#[test_function] -#[cfg(any(target_os = "linux", target_os = "windows"))] -pub async fn test_split_tunnel( +#[test_function(target_os = "windows")] +pub async fn test_split_tunnel_windows( _: TestContext, rpc: ServiceClient, mut mullvad_client: MullvadProxyClient, ) -> anyhow::Result<()> { + const AM_I_MULLVAD_EXE: &str = "E:\\am-i-mullvad.exe"; + + async fn am_i_mullvad(rpc: &ServiceClient) -> anyhow::Result { + parse_am_i_mullvad(rpc.exec(AM_I_MULLVAD_EXE, []).await?) + } + let mut errored = false; - let parse_am_i_mullvad = |result: ExecResult| { - let stdout = str::from_utf8(&result.stdout).expect("curl output is UTF-8"); - Ok(if stdout.contains("You are connected") { - true - } else if stdout.contains("You are not connected") { - false + helpers::disconnect_and_wait(&mut mullvad_client).await?; + + if am_i_mullvad(&rpc).await? { + log::error!("We should be disconnected, but `{AM_I_MULLVAD_EXE}` reported that it was connected to Mullvad."); + log::error!("Host machine is probably connected to Mullvad, this will throw off results"); + errored = true + } + + helpers::connect_and_wait(&mut mullvad_client).await?; + + if !am_i_mullvad(&rpc).await? { + log::error!( + "We should be connected, but `{AM_I_MULLVAD_EXE}` reported no connection to Mullvad." + ); + errored = true + } + + mullvad_client + .add_split_tunnel_app(AM_I_MULLVAD_EXE) + .await?; + mullvad_client.set_split_tunnel_state(true).await?; + + if am_i_mullvad(&rpc).await? { + log::error!( + "`{AM_I_MULLVAD_EXE}` should have been split, but it reported a connection to Mullvad" + ); + errored = true + } + + helpers::disconnect_and_wait(&mut mullvad_client).await?; + + if am_i_mullvad(&rpc).await? { + log::error!( + "`{AM_I_MULLVAD_EXE}` reported a connection to Mullvad while split and disconnected" + ); + errored = true + } + + mullvad_client.set_split_tunnel_state(false).await?; + mullvad_client + .remove_split_tunnel_app(AM_I_MULLVAD_EXE) + .await?; + + if errored { + anyhow::bail!("test_split_tunnel failed, see log output for details."); + } + + Ok(()) +} + +#[test_function(target_os = "linux")] +pub async fn test_split_tunnel_linux( + _: TestContext, + rpc: ServiceClient, + mut mullvad_client: MullvadProxyClient, +) -> anyhow::Result<()> { + const AM_I_MULLVAD_URL: &str = "https://am.i.mullvad.net/connected"; + + async fn am_i_mullvad(rpc: &ServiceClient, split_tunnel: bool) -> anyhow::Result { + let result = if split_tunnel { + rpc.exec("mullvad-exclude", ["curl", AM_I_MULLVAD_URL]) + .await? } else { - anyhow::bail!("Unexpected output from `curl {AM_I_MULLVAD}`: {stdout}") - }) - }; + rpc.exec("curl", [AM_I_MULLVAD_URL]).await? + }; + + parse_am_i_mullvad(result) + } + + let mut errored = false; helpers::connect_and_wait(&mut mullvad_client).await?; - let i_am_mullvad = parse_am_i_mullvad(rpc.exec("curl", [AM_I_MULLVAD]).await?)?; - if !i_am_mullvad { + if !am_i_mullvad(&rpc, false).await? { log::error!("We should be connected, but `am.i.mullvad` reported that it was not connected to Mullvad."); errored = true; } - let i_am_mullvad_while_split = - parse_am_i_mullvad(rpc.exec("mullvad-exclude", ["curl", AM_I_MULLVAD]).await?)?; - if i_am_mullvad_while_split { + if am_i_mullvad(&rpc, true).await? { log::error!( - "`mullvad-exclude curl {AM_I_MULLVAD}` reported that it was connected to Mullvad." + "`mullvad-exclude curl {AM_I_MULLVAD_URL}` reported that it was connected to Mullvad." ); - log::error!("`am-i-mullvad` does not appear to have been split correctly."); + log::error!("`curl` does not appear to have been split correctly."); errored = true; } helpers::disconnect_and_wait(&mut mullvad_client).await?; - let i_am_mullvad_while_disconnected = - parse_am_i_mullvad(rpc.exec("curl", [AM_I_MULLVAD]).await?)?; - if i_am_mullvad_while_disconnected { - log::error!("We should be disconnected, but `curl {AM_I_MULLVAD}` reported that it was connected to Mullvad."); + if am_i_mullvad(&rpc, false).await? { + log::error!("We should be disconnected, but `curl {AM_I_MULLVAD_URL}` reported that it was connected to Mullvad."); log::error!("Host machine is probably connected to Mullvad. This may affect test results."); errored = true; } @@ -61,3 +119,16 @@ pub async fn test_split_tunnel( Ok(()) } + +/// Parse output from am-i-mullvad. Returns true if connected to Mullvad. +fn parse_am_i_mullvad(result: ExecResult) -> anyhow::Result { + let stdout = str::from_utf8(&result.stdout).expect("curl output is UTF-8"); + + Ok(if stdout.contains("You are connected") { + true + } else if stdout.contains("You are not connected") { + false + } else { + anyhow::bail!("Unexpected output from am-i-mullvad: {stdout:?}") + }) +}