Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix connectivity tests ios 873 #7140

Merged
merged 8 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ios/Configurations/Api.xcconfig.template
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ API_HOST_NAME[config=Release] = api.$(HOST_NAME)
API_HOST_NAME[config=MockRelease] = api.$(HOST_NAME)
API_HOST_NAME[config=Staging] = api.$(HOST_NAME)

ENCRYPTED_DNS_HOST_NAME[config=Debug] = frakta.eu
ENCRYPTED_DNS_HOST_NAME[config=Release] = frakta.eu
ENCRYPTED_DNS_HOST_NAME[config=MockRelease] = frakta.eu
ENCRYPTED_DNS_HOST_NAME[config=Staging] = stagemole.frakta.eu

API_ENDPOINT[config=Debug] = 45.83.223.196:443
API_ENDPOINT[config=Release] = 45.83.223.196:443
API_ENDPOINT[config=MockRelease] = 45.83.223.196:443
Expand Down
2 changes: 2 additions & 0 deletions ios/MullvadREST/ApiHandlers/RESTDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ extension REST {
/// Default API endpoint.
public static let defaultAPIEndpoint = AnyIPEndpoint(string: infoDictionary["ApiEndpoint"] as! String)!

public static let encryptedDNSHostname = infoDictionary["EncryptedDnsHostName"] as! String

/// Disables API IP address cache when in staging environment and sticks to using default API endpoint instead.
public static let isStagingEnvironment = false

Expand Down
2 changes: 2 additions & 0 deletions ios/MullvadREST/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@
<string>$(API_HOST_NAME)</string>
<key>ApiEndpoint</key>
<string>$(API_ENDPOINT)</string>
<key>EncryptedDnsHostName</key>
<string>$(ENCRYPTED_DNS_HOST_NAME)</string>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public final class EncryptedDNSTransport: RESTTransport {

public init(urlSession: URLSession) {
self.urlSession = urlSession
self.encryptedDnsProxy = EncryptedDNSProxy()
self.encryptedDnsProxy = EncryptedDNSProxy(domain: REST.encryptedDNSHostname)
}

public func stop() {
Expand Down
6 changes: 4 additions & 2 deletions ios/MullvadRustRuntime/EncryptedDNSProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ public class EncryptedDNSProxy {
private var stateLock = NSLock()
private var didStart = false
private let state: OpaquePointer
private let domain: String

public init() {
state = encrypted_dns_proxy_init()
public init(domain: String) {
self.domain = domain
state = encrypted_dns_proxy_init(domain)
proxyConfig = ProxyHandle(context: nil, port: 0)
}

Expand Down
11 changes: 10 additions & 1 deletion ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,17 @@ extern const uint16_t CONFIG_SERVICE_PORT;

/**
* Initializes a valid pointer to an instance of `EncryptedDnsProxyState`.
*
* # Safety
*
* * [domain_name] must not be non-null.
*
* * [domain_name] pointer must be [valid](core::ptr#safety)
*
* * The caller must ensure that the pointer to the [domain_name] string contains a nul terminator
* at the end of the string.
*/
struct EncryptedDnsProxyState *encrypted_dns_proxy_init(void);
struct EncryptedDnsProxyState *encrypted_dns_proxy_init(const char *domain_name);

/**
* This must be called only once to deallocate `EncryptedDnsProxyState`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ class EditAccessMethodViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()

view.accessibilityIdentifier = .editAccessMethodView
view.backgroundColor = .secondaryColor

tableView.accessibilityIdentifier = .editAccessMethodView
tableView.backgroundColor = .secondaryColor
tableView.delegate = self

Expand Down
7 changes: 5 additions & 2 deletions ios/MullvadVPN/TransportMonitor/TransportMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,19 @@ final class TransportMonitor: RESTTransportProvider {
tunnel.status == .connecting || tunnel.status == .reasserting || tunnel.status == .connected
}

if let tunnel, shouldByPassVPN(tunnel: tunnel) {
if let tunnel, shouldBypassVPN(tunnel: tunnel) {
return PacketTunnelTransport(tunnel: tunnel)
} else {
return transportProvider.makeTransport()
}
}

private func shouldByPassVPN(tunnel: any TunnelProtocol) -> Bool {
private func shouldBypassVPN(tunnel: any TunnelProtocol) -> Bool {
switch tunnel.status {
case .connected:
if case .error = tunnelManager.tunnelStatus.state {
return true
}
return tunnelManager.isConfigurationLoaded && tunnelManager.deviceState == .revoked

case .connecting, .reasserting:
Expand Down
2 changes: 2 additions & 0 deletions ios/MullvadVPNTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>EncryptedDnsHostName</key>
<string>$(ENCRYPTED_DNS_HOST_NAME)</string>
<key>ApiHostName</key>
<string>$(API_HOST_NAME)</string>
<key>ApiEndpoint</key>
Expand Down
11 changes: 11 additions & 0 deletions ios/MullvadVPNUITests/ConnectivityTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ class ConnectivityTests: LoggedOutUITestCase {

/// Verifies that the app still functions when API has been blocked
func testAPIConnectionViaBridges() throws {
let skipReason = """
This test is currently skipped because shadowsocks bridges cannot be reached
from the staging environment
"""
try XCTSkipIf(true, skipReason)
firewallAPIClient.removeRules()
let hasTimeAccountNumber = getAccountWithTime()

Expand Down Expand Up @@ -138,6 +143,12 @@ class ConnectivityTests: LoggedOutUITestCase {
// swiftlint:disable function_body_length
/// Test that the app is functioning when API is down. To simulate API being down we create a dummy access method
func testAppStillFunctioningWhenAPIDown() throws {
let skipReason = """
This test is currently skipped due to a bug in iOS 18 where ATS shuts down the
connection to the API in the blocked state, despite being explicitly disabled,
and after the checks in SSLPinningURLSessionDelegate return no error.
"""
try XCTSkipIf(true, skipReason)
let hasTimeAccountNumber = getAccountWithTime()

addTeardownBlock {
Expand Down
2 changes: 2 additions & 0 deletions ios/MullvadVPNUITests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<string>$(API_ENDPOINT)</string>
<key>ApiHostName</key>
<string>$(API_HOST_NAME)</string>
<key>EncryptedDnsHostName</key>
<string>$(ENCRYPTED_DNS_HOST_NAME)</string>
<key>AttachAppLogsOnFailure</key>
<string>$(ATTACH_APP_LOGS_ON_FAILURE)</string>
<key>DisplayName</key>
Expand Down
2 changes: 1 addition & 1 deletion mullvad-daemon/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ impl AccessModeSelector {
ApiConnectionMode::Proxied(ProxyConfig::from(proxy))
}
AccessMethod::BuiltIn(BuiltInAccessMethod::EncryptedDnsProxy) => {
if let Err(error) = encrypted_dns_proxy_cache.fetch_configs().await {
if let Err(error) = encrypted_dns_proxy_cache.fetch_configs("frakta.eu").await {
log::warn!("Failed to fetch new Encrypted DNS Proxy configurations");
log::debug!("{error:#?}");
}
Expand Down
7 changes: 4 additions & 3 deletions mullvad-encrypted-dns-proxy/src/config_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,12 @@ pub fn default_resolvers() -> Vec<Nameserver> {
]
}

pub async fn resolve_default_config() -> Result<Vec<config::ProxyConfig>, Error> {
resolve_configs(&default_resolvers(), "frakta.eu").await
/// Calls [resolve_configs] with a given `domain` using known DoH resolvers provided by [default_resolvers]
pub async fn resolve_default_config(domain: &str) -> Result<Vec<config::ProxyConfig>, Error> {
resolve_configs(&default_resolvers(), domain).await
}

/// Look up the `domain` towards the given `resolvers`, and try to deserialize all the returned
/// Looks up the `domain` towards the given `resolvers`, and try to deserialize all the returned
/// AAAA records into [`ProxyConfig`](config::ProxyConfig)s.
pub async fn resolve_configs(
resolvers: &[Nameserver],
Expand Down
6 changes: 3 additions & 3 deletions mullvad-encrypted-dns-proxy/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ impl EncryptedDnsProxyState {
Some(selected_config)
}

/// Fetch a config, but error out only when no existing configuration was there.
pub async fn fetch_configs(&mut self) -> Result<(), FetchConfigError> {
match resolve_default_config().await {
/// Fetch a config from `domain`, but error out only when no existing configuration was there.
pub async fn fetch_configs(&mut self, domain: &str) -> Result<(), FetchConfigError> {
match resolve_default_config(domain).await {
Ok(new_configs) => {
self.configurations = HashSet::from_iter(new_configs.into_iter());
}
Expand Down
26 changes: 24 additions & 2 deletions mullvad-ios/src/encrypted_dns_proxy.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::ProxyHandle;

use libc::c_char;
use mullvad_encrypted_dns_proxy::state::{EncryptedDnsProxyState as State, FetchConfigError};
use mullvad_encrypted_dns_proxy::Forwarder;
use std::{
Expand All @@ -9,10 +10,13 @@ use std::{
};
use tokio::{net::TcpListener, task::JoinHandle};

use std::ffi::CStr;

/// A thin wrapper around [`mullvad_encrypted_dns_proxy::state::EncryptedDnsProxyState`] that
/// can start a local forwarder (see [`Self::start`]).
pub struct EncryptedDnsProxyState {
state: State,
domain: String,
}

#[derive(Debug)]
Expand Down Expand Up @@ -47,7 +51,7 @@ impl From<Error> for i32 {
impl EncryptedDnsProxyState {
async fn start(&mut self) -> Result<ProxyHandle, Error> {
self.state
.fetch_configs()
.fetch_configs(&self.domain)
.await
.map_err(Error::FetchConfig)?;
let proxy_configuration = self.state.next_configuration().ok_or(Error::NoConfigs)?;
Expand Down Expand Up @@ -78,10 +82,28 @@ impl EncryptedDnsProxyState {
}

/// Initializes a valid pointer to an instance of `EncryptedDnsProxyState`.
///
/// # Safety
///
/// * [domain_name] must not be non-null.
///
/// * [domain_name] pointer must be [valid](core::ptr#safety)
///
/// * The caller must ensure that the pointer to the [domain_name] string contains a nul terminator
/// at the end of the string.
#[no_mangle]
pub unsafe extern "C" fn encrypted_dns_proxy_init() -> *mut EncryptedDnsProxyState {
pub unsafe extern "C" fn encrypted_dns_proxy_init(
domain_name: *const c_char,
) -> *mut EncryptedDnsProxyState {
let domain = {
// SAFETY: domain_name points to a valid region of memory and contains a nul terminator.
let c_str = unsafe { CStr::from_ptr(domain_name) };
String::from_utf8_lossy(c_str.to_bytes())
};

let state = Box::new(EncryptedDnsProxyState {
state: State::default(),
domain: domain.into_owned(),
});
Box::into_raw(state)
}
Expand Down
Loading