Skip to content

Commit

Permalink
Add split tunnel test for windows
Browse files Browse the repository at this point in the history
  • Loading branch information
hulthe committed Mar 11, 2024
1 parent f70993f commit cdb8306
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 30 deletions.
9 changes: 4 additions & 5 deletions mullvad-management-interface/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -632,7 +631,7 @@ impl MullvadProxyClient {
Ok(())
}

#[cfg(target_os = "windows")]
//#[cfg(target_os = "windows")]
pub async fn add_split_tunnel_app<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let path = path.as_ref().to_str().ok_or(Error::PathMustBeUtf8)?;
self.0
Expand All @@ -642,7 +641,7 @@ impl MullvadProxyClient {
Ok(())
}

#[cfg(target_os = "windows")]
//#[cfg(target_os = "windows")]
pub async fn remove_split_tunnel_app<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let path = path.as_ref().to_str().ok_or(Error::PathMustBeUtf8)?;
self.0
Expand All @@ -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(())
Expand All @@ -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)
Expand Down
121 changes: 96 additions & 25 deletions test/test-manager/src/tests/split_tunnel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool> {
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<bool> {
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;
}
Expand All @@ -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<bool> {
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:?}")
})
}

0 comments on commit cdb8306

Please sign in to comment.