diff options
| author | Emīls <emils@mullvad.net> | 2024-12-09 10:59:51 +0100 |
|---|---|---|
| committer | Emīls <emils@mullvad.net> | 2025-01-02 10:29:05 +0100 |
| commit | 3093408a057020fcd912976b892fbb6bc26e6293 (patch) | |
| tree | 56ac01255fdc7f0c29aa6a8c5a8cdd863ff4f496 | |
| parent | 58efb1004f8ca762fe6aa95f541c6dc329ed790e (diff) | |
| download | mullvadvpn-3093408a057020fcd912976b892fbb6bc26e6293.tar.xz mullvadvpn-3093408a057020fcd912976b892fbb6bc26e6293.zip | |
Remove global API endpoint
25 files changed, 310 insertions, 187 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt index f4a0777e3f..3b4a460fea 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt @@ -5,6 +5,9 @@ import java.io.File import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointFromIntentHolder +import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointOverride +import net.mullvad.mullvadvpn.service.BuildConfig const val PROBLEM_REPORT_LOGS_FILE = "problem_report.txt" @@ -21,7 +24,12 @@ sealed interface SendProblemReportResult { data class UserReport(val email: String?, val description: String) -class MullvadProblemReport(context: Context, val dispatcher: CoroutineDispatcher = Dispatchers.IO) { +class MullvadProblemReport( + context: Context, + private val apiEndpointOverride: ApiEndpointOverride?, + private val apiEndpointFromIntentHolder: ApiEndpointFromIntentHolder, + val dispatcher: CoroutineDispatcher = Dispatchers.IO, +) { private val cacheDirectory = File(context.cacheDir.toURI()) private val logDirectory = File(context.filesDir.toURI()) @@ -47,11 +55,20 @@ class MullvadProblemReport(context: Context, val dispatcher: CoroutineDispatcher val sentSuccessfully = withContext(dispatcher) { + val intentApiOverride = apiEndpointFromIntentHolder.apiEndpointOverride + val apiOverride = + if (BuildConfig.DEBUG && intentApiOverride != null) { + intentApiOverride + } else { + apiEndpointOverride + } + sendProblemReport( userReport.email ?: "", userReport.description, logsPath.absolutePath, cacheDirectory.absolutePath, + apiOverride, ) } @@ -89,5 +106,6 @@ class MullvadProblemReport(context: Context, val dispatcher: CoroutineDispatcher userMessage: String, reportPath: String, cacheDirectory: String, + apiEndpointOverride: ApiEndpointOverride?, ): Boolean } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt index 4acf52c7b0..bc236cc792 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt @@ -33,6 +33,7 @@ import net.mullvad.mullvadvpn.repository.UserPreferencesMigration import net.mullvad.mullvadvpn.repository.UserPreferencesRepository import net.mullvad.mullvadvpn.repository.UserPreferencesSerializer import net.mullvad.mullvadvpn.repository.WireguardConstraintsRepository +import net.mullvad.mullvadvpn.service.DaemonConfig import net.mullvad.mullvadvpn.ui.MainActivity import net.mullvad.mullvadvpn.ui.serviceconnection.AppVersionInfoRepository import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager @@ -129,7 +130,7 @@ val uiModule = module { single { ChangelogRepository(get()) } single { UserPreferencesRepository(get()) } single { SettingsRepository(get()) } - single { MullvadProblemReport(get()) } + single { MullvadProblemReport(get(), get<DaemonConfig>().apiEndpointOverride, get()) } single { RelayOverridesRepository(get()) } single { CustomListsRepository(get()) } single { RelayListRepository(get(), get()) } diff --git a/android/lib/endpoint/src/main/kotlin/net/mullvad/mullvadvpn/lib/endpoint/ApiEndpointOverride.kt b/android/lib/endpoint/src/main/kotlin/net/mullvad/mullvadvpn/lib/endpoint/ApiEndpointOverride.kt index 5201e86386..7350aa0d26 100644 --- a/android/lib/endpoint/src/main/kotlin/net/mullvad/mullvadvpn/lib/endpoint/ApiEndpointOverride.kt +++ b/android/lib/endpoint/src/main/kotlin/net/mullvad/mullvadvpn/lib/endpoint/ApiEndpointOverride.kt @@ -7,7 +7,6 @@ import kotlinx.parcelize.Parcelize data class ApiEndpointOverride( val hostname: String, val port: Int = CUSTOM_ENDPOINT_HTTPS_PORT, - val disableAddressCache: Boolean = true, val disableTls: Boolean = false, val forceDirectConnection: Boolean = true, ) : Parcelable { diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt index d3590fc056..c7cb8f0377 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt @@ -55,11 +55,6 @@ abstract class MockApiTest { } private fun createEndpoint(port: Int): ApiEndpointOverride { - return ApiEndpointOverride( - InetAddress.getLocalHost().hostName, - port, - disableAddressCache = true, - disableTls = true, - ) + return ApiEndpointOverride(InetAddress.getLocalHost().hostName, port, disableTls = true) } } diff --git a/ios/MullvadVPNUITests/MullvadApi.swift b/ios/MullvadVPNUITests/MullvadApi.swift index 6f84ac1976..18755b641b 100644 --- a/ios/MullvadVPNUITests/MullvadApi.swift +++ b/ios/MullvadVPNUITests/MullvadApi.swift @@ -56,7 +56,8 @@ class MullvadApi { let result = mullvad_api_client_initialize( &clientContext, apiAddress, - hostname + hostname, + false ) try ApiError(result).throwIfErr() } diff --git a/mullvad-api/Cargo.toml b/mullvad-api/Cargo.toml index fc9d7d899b..005d1a9505 100644 --- a/mullvad-api/Cargo.toml +++ b/mullvad-api/Cargo.toml @@ -33,6 +33,7 @@ tokio = { workspace = true, features = ["macros", "time", "rt-multi-thread", "ne tokio-rustls = { version = "0.26.0", features = ["logging", "tls12", "ring"], default-features = false} tokio-socks = "0.5.1" rustls-pemfile = "2.1.3" +uuid = { version = "1.4.1", features = ["v4"] } mullvad-encrypted-dns-proxy = { path = "../mullvad-encrypted-dns-proxy" } mullvad-fs = { path = "../mullvad-fs" } @@ -50,14 +51,6 @@ mockito = "1.6.1" [build-dependencies] cbindgen = { version = "0.24.3", default-features = false } -[target.'cfg(target_os = "ios")'.dependencies] -uuid = { version = "1.4.1", features = ["v4"] } - [lib] crate-type = [ "rlib", "staticlib" ] bench = false - -[[test]] -name = "ffi" -# required-features = [ "api-override" ] -features = [ "api-override" ] diff --git a/mullvad-api/include/mullvad-api.h b/mullvad-api/include/mullvad-api.h index e0295b20aa..4e5f78aef2 100644 --- a/mullvad-api/include/mullvad-api.h +++ b/mullvad-api/include/mullvad-api.h @@ -49,15 +49,16 @@ typedef struct MullvadApiDevice { * struct. * * * `api_address`: pointer to nul-terminated UTF-8 string containing a socket address - * representation - * ("143.32.4.32:9090"), the port is mandatory. + * representation ("143.32.4.32:9090"), the port is mandatory. * * * `hostname`: pointer to a null-terminated UTF-8 string representing the hostname that will be * used for TLS validation. + * * `disable_tls`: only valid when built for tests, can be ignored when consumed by Swift. */ struct MullvadApiError mullvad_api_client_initialize(struct MullvadApiClient *client_ptr, const char *api_address_ptr, - const char *hostname); + const char *hostname, + bool disable_tls); /** * Removes all devices from a given account @@ -98,8 +99,8 @@ struct MullvadApiError mullvad_api_get_expiry(struct MullvadApiClient client_ptr * * `account_str_ptr`: pointer to nul-terminated UTF-8 string containing the account number of the * account that will have all of it's devices removed. * - * * `device_iter_ptr`: a pointer to a `device::MullvadApiDeviceIterator`. If this function - * doesn't return an error, the pointer will be initialized with a valid instance of + * * `device_iter_ptr`: a pointer to a `device::MullvadApiDeviceIterator`. If this function doesn't + * return an error, the pointer will be initialized with a valid instance of * `device::MullvadApiDeviceIterator`, which can be used to iterate through the devices. */ struct MullvadApiError mullvad_api_list_devices(struct MullvadApiClient client_ptr, diff --git a/mullvad-api/src/address_cache.rs b/mullvad-api/src/address_cache.rs index 0898f8da1f..a6a60146b4 100644 --- a/mullvad-api/src/address_cache.rs +++ b/mullvad-api/src/address_cache.rs @@ -1,7 +1,6 @@ //! This module keeps track of the last known good API IP address and reads and stores it on disk. -use super::API; -use crate::DnsResolver; +use crate::{ApiEndpoint, DnsResolver}; use async_trait::async_trait; use std::{io, net::SocketAddr, path::Path, sync::Arc}; use tokio::{ @@ -38,42 +37,42 @@ impl DnsResolver for AddressCache { #[derive(Clone)] pub struct AddressCache { + hostname: String, inner: Arc<Mutex<AddressCacheInner>>, write_path: Option<Arc<Path>>, } impl AddressCache { /// Initialize cache using the hardcoded address, and write changes to `write_path`. - pub fn new(write_path: Option<Box<Path>>) -> Self { - Self::new_inner(API.address(), write_path) - } - - pub fn with_static_addr(address: SocketAddr) -> Self { - Self::new_inner(address, None) + pub fn new(endpoint: &ApiEndpoint, write_path: Option<Box<Path>>) -> Self { + Self::new_inner(endpoint.address(), endpoint.host().to_owned(), write_path) } /// Initialize cache using `read_path`, and write changes to `write_path`. - pub async fn from_file(read_path: &Path, write_path: Option<Box<Path>>) -> Result<Self, Error> { + pub async fn from_file( + read_path: &Path, + write_path: Option<Box<Path>>, + hostname: String, + ) -> Result<Self, Error> { log::debug!("Loading API addresses from {}", read_path.display()); - Ok(Self::new_inner( - read_address_file(read_path).await?, - write_path, - )) + let address = read_address_file(read_path).await?; + Ok(Self::new_inner(address, hostname, write_path)) } - fn new_inner(address: SocketAddr, write_path: Option<Box<Path>>) -> Self { + fn new_inner(address: SocketAddr, hostname: String, write_path: Option<Box<Path>>) -> Self { let cache = AddressCacheInner::from_address(address); log::debug!("Using API address: {}", cache.address); Self { inner: Arc::new(Mutex::new(cache)), write_path: write_path.map(Arc::from), + hostname, } } /// Returns the address if the hostname equals `API.host`. Otherwise, returns `None`. async fn resolve_hostname(&self, hostname: &str) -> Option<SocketAddr> { - if hostname.eq_ignore_ascii_case(API.host()) { + if hostname.eq_ignore_ascii_case(&self.hostname) { Some(self.get_address().await) } else { None diff --git a/mullvad-api/src/bin/relay_list.rs b/mullvad-api/src/bin/relay_list.rs index def32303ea..3ea771cc81 100644 --- a/mullvad-api/src/bin/relay_list.rs +++ b/mullvad-api/src/bin/relay_list.rs @@ -2,14 +2,18 @@ //! Used by the installer artifact packer to bundle the latest available //! relay list at the time of creating the installer. -use mullvad_api::{proxy::ApiConnectionMode, rest::Error as RestError, RelayListProxy}; +use mullvad_api::{ + proxy::ApiConnectionMode, rest::Error as RestError, ApiEndpoint, RelayListProxy, +}; use std::process; use talpid_types::ErrorExt; #[tokio::main] async fn main() { - let runtime = mullvad_api::Runtime::new(tokio::runtime::Handle::current()) - .expect("Failed to load runtime"); + let runtime = mullvad_api::Runtime::new( + tokio::runtime::Handle::current(), + &ApiEndpoint::from_env_vars(), + ); let relay_list_request = RelayListProxy::new(runtime.mullvad_rest_handle(ApiConnectionMode::Direct.into_provider())) diff --git a/mullvad-api/src/ffi/error.rs b/mullvad-api/src/ffi/error.rs index 539a6c23a0..66ffc01220 100644 --- a/mullvad-api/src/ffi/error.rs +++ b/mullvad-api/src/ffi/error.rs @@ -13,6 +13,7 @@ pub enum MullvadApiErrorKind { /// MullvadApiErrorKind contains a description and an error kind. If the error kind is /// `MullvadApiErrorKind` is NoError, the pointer will be nil. +#[derive(Debug)] #[repr(C)] pub struct MullvadApiError { description: *mut libc::c_char, @@ -47,6 +48,13 @@ impl MullvadApiError { } } + pub fn unwrap(&self) { + if !matches!(self.kind, MullvadApiErrorKind::NoError) { + let desc = unsafe { std::ffi::CStr::from_ptr(self.description) }; + panic!("API ERROR - {:?} - {}", self.kind, desc.to_str().unwrap()); + } + } + pub fn drop(self) { if self.description.is_null() { return; diff --git a/mullvad-api/src/ffi/mod.rs b/mullvad-api/src/ffi/mod.rs index a68ea40ed6..9677488257 100644 --- a/mullvad-api/src/ffi/mod.rs +++ b/mullvad-api/src/ffi/mod.rs @@ -1,3 +1,4 @@ +#![cfg(not(target_os = "android"))] use std::{ ffi::{CStr, CString}, net::SocketAddr, @@ -6,8 +7,9 @@ use std::{ }; use crate::{ + proxy::ApiConnectionMode, rest::{self, MullvadRestHandle}, - AccountsProxy, DevicesProxy, + AccountsProxy, ApiEndpoint, DevicesProxy, }; mod device; @@ -48,13 +50,13 @@ impl MullvadApiClient { struct FfiClient { tokio_runtime: tokio::runtime::Runtime, api_runtime: crate::Runtime, - api_hostname: String, } impl FfiClient { unsafe fn new( api_address_ptr: *const libc::c_char, hostname: *const libc::c_char, + #[cfg(any(feature = "api-override", test))] disable_tls: bool, ) -> Result<Self, MullvadApiError> { // SAFETY: addr_str must be a valid pointer to a null-terminated string. let addr_str = unsafe { string_from_raw_ptr(api_address_ptr)? }; @@ -68,12 +70,15 @@ impl FfiClient { ) })?; - // The call site guarantees that - // api_hostname and api_address will never change after the first call to new. - std::env::set_var(crate::env::API_HOST_VAR, &api_hostname); - std::env::set_var(crate::env::API_ADDR_VAR, &addr_str); - std::env::set_var(crate::env::API_FORCE_DIRECT_VAR, "0"); - std::env::set_var(crate::env::DISABLE_TLS_VAR, "0"); + let endpoint = ApiEndpoint { + host: Some(api_hostname.clone()), + address: Some(api_address), + #[cfg(feature = "api-override")] + force_direct: false, + #[cfg(any(feature = "api-override", test))] + disable_tls, + }; + let mut runtime_builder = tokio::runtime::Builder::new_multi_thread(); runtime_builder.worker_threads(2).enable_all(); @@ -83,14 +88,12 @@ impl FfiClient { // It is imperative that the REST runtime is created within an async context, otherwise // ApiAvailability panics. - let api_runtime = tokio_runtime.block_on(async { - crate::Runtime::with_static_addr(tokio_runtime.handle().clone(), api_address) - }); + let api_runtime = tokio_runtime + .block_on(async { crate::Runtime::new(tokio_runtime.handle().clone(), &endpoint) }); let context = FfiClient { tokio_runtime, api_runtime, - api_hostname, }; Ok(context) @@ -204,7 +207,7 @@ impl FfiClient { fn rest_handle(&self) -> MullvadRestHandle { self.tokio_handle().block_on(async { self.api_runtime - .static_mullvad_rest_handle(self.api_hostname.clone()) + .mullvad_rest_handle(ApiConnectionMode::Direct.into_provider()) }) } @@ -229,18 +232,31 @@ impl FfiClient { /// struct. /// /// * `api_address`: pointer to nul-terminated UTF-8 string containing a socket address -/// representation -/// ("143.32.4.32:9090"), the port is mandatory. +/// representation ("143.32.4.32:9090"), the port is mandatory. /// /// * `hostname`: pointer to a null-terminated UTF-8 string representing the hostname that will be /// used for TLS validation. +/// * `disable_tls`: only valid when built for tests, can be ignored when consumed by Swift. #[no_mangle] pub unsafe extern "C" fn mullvad_api_client_initialize( client_ptr: *mut MullvadApiClient, api_address_ptr: *const libc::c_char, hostname: *const libc::c_char, + disable_tls: bool, ) -> MullvadApiError { - match unsafe { FfiClient::new(api_address_ptr, hostname) } { + #[cfg(not(any(feature = "api-override", test)))] + if disable_tls { + log::error!("disable_tls has no effect when mullvad-api is built without api-override"); + } + + match unsafe { + FfiClient::new( + api_address_ptr, + hostname, + #[cfg(any(feature = "api-override", test))] + disable_tls, + ) + } { Ok(client) => { unsafe { std::ptr::write(client_ptr, MullvadApiClient::new(client)); @@ -306,8 +322,8 @@ pub unsafe extern "C" fn mullvad_api_get_expiry( /// * `account_str_ptr`: pointer to nul-terminated UTF-8 string containing the account number of the /// account that will have all of it's devices removed. /// -/// * `device_iter_ptr`: a pointer to a `device::MullvadApiDeviceIterator`. If this function -/// doesn't return an error, the pointer will be initialized with a valid instance of +/// * `device_iter_ptr`: a pointer to a `device::MullvadApiDeviceIterator`. If this function doesn't +/// return an error, the pointer will be initialized with a valid instance of /// `device::MullvadApiDeviceIterator`, which can be used to iterate through the devices. #[no_mangle] pub unsafe extern "C" fn mullvad_api_list_devices( @@ -443,3 +459,57 @@ unsafe fn string_from_raw_ptr(ptr: *const libc::c_char) -> Result<String, Mullva })? .to_owned()) } + +#[cfg(test)] +mod test { + use mockito::{Server, ServerGuard}; + use std::{mem::MaybeUninit, net::Ipv4Addr}; + + use super::*; + const STAGING_HOSTNAME: &[u8] = b"api-app.stagemole.eu\0"; + + #[test] + fn test_initialization() { + let _ = create_client(&SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 1)); + } + + fn create_client(addr: &SocketAddr) -> MullvadApiClient { + let mut client = MaybeUninit::<MullvadApiClient>::uninit(); + let cstr_address = CString::new(addr.to_string()).unwrap(); + unsafe { + mullvad_api_client_initialize( + client.as_mut_ptr(), + cstr_address.as_ptr().cast(), + STAGING_HOSTNAME.as_ptr().cast(), + true, + ) + .unwrap(); + }; + unsafe { client.assume_init() } + } + + #[test] + fn test_create_delete_account() { + let server = test_server(); + let client = create_client(&server.socket_address()); + + let mut account_buf = vec![0 as libc::c_char; 100]; + unsafe { mullvad_api_create_account(client, account_buf.as_mut_ptr().cast()).unwrap() }; + } + + fn test_server() -> ServerGuard { + let mut server = Server::new(); + let expected_create_account_response = br#"{"id":"085df870-0fc2-47cb-9e8c-cb43c1bdaac0","expiry":"2024-12-11T12:56:32+00:00","max_ports":0,"can_add_ports":false,"max_devices":5,"can_add_devices":true,"number":"6705749539195318"}"#; + server + .mock( + "POST", + &*("/".to_string() + crate::ACCOUNTS_URL_PREFIX + "/accounts"), + ) + .with_header("content-type", "application/json") + .with_status(201) + .with_body(expected_create_account_response) + .create(); + + server + } +} diff --git a/mullvad-api/src/https_client_with_sni.rs b/mullvad-api/src/https_client_with_sni.rs index 3dfd168a92..f86c538a67 100644 --- a/mullvad-api/src/https_client_with_sni.rs +++ b/mullvad-api/src/https_client_with_sni.rs @@ -41,8 +41,8 @@ use tokio::{ }; use tower::Service; -#[cfg(feature = "api-override")] -use crate::{proxy::ConnectionDecorator, API}; +#[cfg(any(feature = "api-override", test))] +use crate::proxy::ConnectionDecorator; const CONNECT_TIMEOUT: Duration = Duration::from_secs(5); @@ -89,6 +89,7 @@ impl InnerConnectionMode { hostname: &str, addr: &SocketAddr, #[cfg(target_os = "android")] socket_bypass_tx: Option<mpsc::Sender<SocketBypassRequest>>, + #[cfg(any(feature = "api-override", test))] disable_tls: bool, ) -> Result<ApiConnection, std::io::Error> { match self { // Set up a TCP-socket connection. @@ -101,6 +102,8 @@ impl InnerConnectionMode { make_proxy_stream, #[cfg(target_os = "android")] socket_bypass_tx, + #[cfg(any(feature = "api-override", test))] + disable_tls, ) .await } @@ -121,6 +124,8 @@ impl InnerConnectionMode { make_proxy_stream, #[cfg(target_os = "android")] socket_bypass_tx, + #[cfg(any(feature = "api-override", test))] + disable_tls, ) .await } @@ -153,6 +158,8 @@ impl InnerConnectionMode { make_proxy_stream, #[cfg(target_os = "android")] socket_bypass_tx, + #[cfg(any(feature = "api-override", test))] + disable_tls, ) .await } @@ -168,6 +175,8 @@ impl InnerConnectionMode { make_proxy_stream, #[cfg(target_os = "android")] socket_bypass_tx, + #[cfg(any(feature = "api-override", test))] + disable_tls, ) .await } @@ -191,6 +200,7 @@ impl InnerConnectionMode { hostname: &str, make_proxy_stream: ProxyFactory, #[cfg(target_os = "android")] socket_bypass_tx: Option<mpsc::Sender<SocketBypassRequest>>, + #[cfg(any(feature = "api-override", test))] disable_tls: bool, ) -> Result<ApiConnection, io::Error> where ProxyFactory: FnOnce(TcpStream) -> ProxyFuture, @@ -206,8 +216,8 @@ impl InnerConnectionMode { let proxy = make_proxy_stream(socket).await?; - #[cfg(feature = "api-override")] - if API.disable_tls { + #[cfg(any(feature = "api-override", test))] + if disable_tls { return Ok(ApiConnection::new(Box::new(ConnectionDecorator(proxy)))); } @@ -290,6 +300,8 @@ pub struct HttpsConnectorWithSni { dns_resolver: Arc<dyn DnsResolver>, #[cfg(target_os = "android")] socket_bypass_tx: Option<mpsc::Sender<SocketBypassRequest>>, + #[cfg(any(feature = "api-override", test))] + disable_tls: bool, } struct HttpsConnectorWithSniInner { @@ -304,6 +316,7 @@ impl HttpsConnectorWithSni { pub fn new( dns_resolver: Arc<dyn DnsResolver>, #[cfg(target_os = "android")] socket_bypass_tx: Option<mpsc::Sender<SocketBypassRequest>>, + #[cfg(any(feature = "api-override", test))] disable_tls: bool, ) -> (Self, HttpsConnectorWithSniHandle) { let (tx, mut rx) = mpsc::unbounded(); let abort_notify = Arc::new(tokio::sync::Notify::new()); @@ -352,6 +365,8 @@ impl HttpsConnectorWithSni { dns_resolver, #[cfg(target_os = "android")] socket_bypass_tx, + #[cfg(any(feature = "api-override", test))] + disable_tls, }, HttpsConnectorWithSniHandle { tx }, ) @@ -435,6 +450,9 @@ impl Service<Uri> for HttpsConnectorWithSni { let socket_bypass_tx = self.socket_bypass_tx.clone(); let dns_resolver = self.dns_resolver.clone(); + #[cfg(any(feature = "api-override", test))] + let disable_tls = self.disable_tls; + let fut = async move { if uri.scheme() != Some(&Scheme::HTTPS) { return Err(io::Error::new( @@ -460,6 +478,8 @@ impl Service<Uri> for HttpsConnectorWithSni { &addr, #[cfg(target_os = "android")] socket_bypass_tx.clone(), + #[cfg(any(feature = "api-override", test))] + disable_tls, ); pin_mut!(stream_fut); diff --git a/mullvad-api/src/lib.rs b/mullvad-api/src/lib.rs index 3b02e4fe98..a47c708b2e 100644 --- a/mullvad-api/src/lib.rs +++ b/mullvad-api/src/lib.rs @@ -10,14 +10,12 @@ use mullvad_types::{ }; use proxy::{ApiConnectionMode, ConnectionModeProvider}; use std::{ - cell::Cell, collections::BTreeMap, future::Future, io, net::{IpAddr, Ipv4Addr, SocketAddr}, - ops::Deref, path::Path, - sync::{Arc, OnceLock}, + sync::Arc, }; use talpid_types::ErrorExt; @@ -37,7 +35,6 @@ mod address_cache; pub mod device; mod relay_list; -#[cfg(target_os = "ios")] pub mod ffi; pub use address_cache::AddressCache; @@ -70,41 +67,6 @@ const APP_URL_PREFIX: &str = "app/v1"; #[cfg(target_os = "android")] const GOOGLE_PAYMENTS_URL_PREFIX: &str = "payments/google-play/v1"; -pub static API: LazyManual<ApiEndpoint> = LazyManual::new(ApiEndpoint::from_env_vars); - -unsafe impl<T, F: Send> Sync for LazyManual<T, F> where OnceLock<T>: Sync {} - -/// A value that is either initialized on access or explicitly. -pub struct LazyManual<T, F = fn() -> T> { - cell: OnceLock<T>, - lazy_fn: Cell<Option<F>>, -} - -impl<T, F> LazyManual<T, F> { - const fn new(lazy_fn: F) -> Self { - Self { - cell: OnceLock::new(), - lazy_fn: Cell::new(Some(lazy_fn)), - } - } - - /// Tries to initialize the object. An error is returned if it is - /// already initialized. - #[cfg(feature = "api-override")] - pub fn override_init(&self, val: T) -> Result<(), T> { - let _ = self.lazy_fn.take(); - self.cell.set(val) - } -} - -impl<T> Deref for LazyManual<T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.cell.get_or_init(|| (self.lazy_fn.take().unwrap())()) - } -} - pub mod env { pub const API_HOST_VAR: &str = "MULLVAD_API_HOST"; pub const API_ADDR_VAR: &str = "MULLVAD_API_ADDR"; @@ -113,7 +75,7 @@ pub mod env { } /// A hostname and socketaddr to reach the Mullvad REST API over. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ApiEndpoint { /// An overriden API hostname. Initialized with the value of the environment /// variable `MULLVAD_API_HOST` if it has been set. @@ -132,9 +94,7 @@ pub struct ApiEndpoint { /// If [`Self::address`] is populated with [`Some(SocketAddr)`], it should /// always be respected when establishing API connections. pub address: Option<SocketAddr>, - #[cfg(feature = "api-override")] - pub disable_address_cache: bool, - #[cfg(feature = "api-override")] + #[cfg(any(feature = "api-override", test))] pub disable_tls: bool, #[cfg(feature = "api-override")] /// Whether bridges/proxies can be used to access the API or not. This is @@ -175,7 +135,6 @@ impl ApiEndpoint { let mut api = ApiEndpoint { host: None, address: None, - disable_address_cache: host_var.is_some() || address_var.is_some(), disable_tls: false, force_direct: force_direct .map(|force_direct| force_direct != "0") @@ -244,6 +203,11 @@ impl ApiEndpoint { api } + #[cfg(feature = "api-override")] + pub fn should_disable_address_cache(&self) -> bool { + self.host.is_some() || self.address.is_some() + } + /// Returns the endpoint to connect to the API over. /// /// # Panics @@ -269,9 +233,31 @@ impl ApiEndpoint { ApiEndpoint { host: None, address: None, + #[cfg(test)] + disable_tls: false, } } + /// Returns a new API endpoint with the given host and socket address. + pub fn new( + host: String, + address: SocketAddr, + #[cfg(any(feature = "api-override", test))] disable_tls: bool, + ) -> Self { + Self { + host: Some(host), + address: Some(address), + #[cfg(any(feature = "api-override", test))] + disable_tls, + #[cfg(feature = "api-override")] + force_direct: false, + } + } + + pub fn set_addr(&mut self, address: SocketAddr) { + self.address = Some(address); + } + /// Read the [`Self::host`] value, falling back to /// [`Self::API_HOST_DEFAULT`] as default value if it does not exist. pub fn host(&self) -> &str { @@ -342,6 +328,7 @@ pub struct Runtime { handle: tokio::runtime::Handle, address_cache: AddressCache, api_availability: availability::ApiAvailability, + endpoint: ApiEndpoint, #[cfg(target_os = "android")] socket_bypass_tx: Option<mpsc::Sender<SocketBypassRequest>>, } @@ -362,40 +349,27 @@ pub enum Error { } impl Runtime { - /// Create a new `Runtime`. - pub fn new(handle: tokio::runtime::Handle) -> Result<Self, Error> { - Self::new_inner( - handle, - #[cfg(target_os = "android")] - None, - ) - } - - #[cfg(target_os = "ios")] - pub fn with_static_addr(handle: tokio::runtime::Handle, address: SocketAddr) -> Self { - Runtime { - handle, - address_cache: AddressCache::with_static_addr(address), - api_availability: ApiAvailability::default(), - } - } - - fn new_inner( + /// Will create a new Runtime without a cache with the provided API endpoint. + pub fn new( handle: tokio::runtime::Handle, + endpoint: &ApiEndpoint, #[cfg(target_os = "android")] socket_bypass_tx: Option<mpsc::Sender<SocketBypassRequest>>, - ) -> Result<Self, Error> { - Ok(Runtime { + ) -> Self { + Runtime { handle, - address_cache: AddressCache::new(None), + address_cache: AddressCache::new(endpoint, None), api_availability: ApiAvailability::default(), + endpoint: endpoint.clone(), #[cfg(target_os = "android")] socket_bypass_tx, - }) + } } /// Create a new `Runtime` using the specified directories. /// Try to use the cache directory first, and fall back on the bundled address otherwise. + /// Will try to construct an API endpoint from the environment. pub async fn with_cache( + endpoint: &ApiEndpoint, cache_dir: &Path, write_changes: bool, #[cfg(target_os = "android")] socket_bypass_tx: Option<mpsc::Sender<SocketBypassRequest>>, @@ -403,12 +377,13 @@ impl Runtime { let handle = tokio::runtime::Handle::current(); #[cfg(feature = "api-override")] - if API.disable_address_cache { - return Self::new_inner( + if endpoint.should_disable_address_cache() { + return Ok(Self::new( handle, + endpoint, #[cfg(target_os = "android")] socket_bypass_tx, - ); + )); } let cache_file = cache_dir.join(API_IP_CACHE_FILENAME); @@ -418,7 +393,13 @@ impl Runtime { None }; - let address_cache = match AddressCache::from_file(&cache_file, write_file.clone()).await { + let address_cache = match AddressCache::from_file( + &cache_file, + write_file.clone(), + endpoint.host().to_owned(), + ) + .await + { Ok(cache) => cache, Err(error) => { if cache_file.exists() { @@ -429,7 +410,7 @@ impl Runtime { ) ); } - AddressCache::new(write_file) + AddressCache::new(endpoint, write_file) } }; @@ -439,12 +420,14 @@ impl Runtime { handle, address_cache, api_availability, + endpoint: endpoint.clone(), #[cfg(target_os = "android")] socket_bypass_tx, }) } - /// Returns a request factory initialized to create requests for the master API + /// Returns a request factory initialized to create requests for the master API Assumes an API + /// endpoint that is constructed from env vars, or uses default values. pub fn mullvad_rest_handle<T: ConnectionModeProvider + 'static>( &self, connection_mode_provider: T, @@ -454,21 +437,10 @@ impl Runtime { Arc::new(self.address_cache.clone()), #[cfg(target_os = "android")] self.socket_bypass_tx.clone(), + #[cfg(any(feature = "api-override", test))] + self.endpoint.disable_tls, ); - let token_store = access::AccessTokenStore::new(service.clone(), API.host()); - let factory = rest::RequestFactory::new(API.host().to_owned(), Some(token_store)); - - rest::MullvadRestHandle::new(service, factory, self.availability_handle()) - } - - /// This is only to be used in test code - pub fn static_mullvad_rest_handle(&self, hostname: String) -> rest::MullvadRestHandle { - let service = self.new_request_service( - ApiConnectionMode::Direct.into_provider(), - Arc::new(self.address_cache.clone()), - #[cfg(target_os = "android")] - self.socket_bypass_tx.clone(), - ); + let hostname = self.endpoint.host().to_owned(); let token_store = access::AccessTokenStore::new(service.clone(), hostname.clone()); let factory = rest::RequestFactory::new(hostname, Some(token_store)); @@ -482,6 +454,8 @@ impl Runtime { Arc::new(dns_resolver), #[cfg(target_os = "android")] None, + #[cfg(any(feature = "api-override", test))] + false, ) } @@ -491,6 +465,7 @@ impl Runtime { connection_mode_provider: T, dns_resolver: Arc<dyn DnsResolver>, #[cfg(target_os = "android")] socket_bypass_tx: Option<mpsc::Sender<SocketBypassRequest>>, + #[cfg(any(feature = "api-override", test))] disable_tls: bool, ) -> rest::RequestServiceHandle { rest::RequestService::spawn( self.api_availability.clone(), @@ -498,6 +473,8 @@ impl Runtime { dns_resolver, #[cfg(target_os = "android")] socket_bypass_tx, + #[cfg(any(feature = "api-override", test))] + disable_tls, ) } @@ -582,7 +559,6 @@ impl AccountsProxy { } } - #[cfg(target_os = "ios")] pub fn delete_account( &self, account: AccountNumber, diff --git a/mullvad-api/src/rest.rs b/mullvad-api/src/rest.rs index 5b93eea311..cab3bb7e0f 100644 --- a/mullvad-api/src/rest.rs +++ b/mullvad-api/src/rest.rs @@ -154,11 +154,14 @@ impl<T: ConnectionModeProvider + 'static> RequestService<T> { connection_mode_provider: T, dns_resolver: Arc<dyn DnsResolver>, #[cfg(target_os = "android")] socket_bypass_tx: Option<mpsc::Sender<SocketBypassRequest>>, + #[cfg(any(feature = "api-override", test))] disable_tls: bool, ) -> RequestServiceHandle { let (connector, connector_handle) = HttpsConnectorWithSni::new( dns_resolver, #[cfg(target_os = "android")] socket_bypass_tx.clone(), + #[cfg(any(feature = "api-override", test))] + disable_tls, ); connector_handle.set_connection_mode(connection_mode_provider.initial()); @@ -461,7 +464,6 @@ where } // Parse unexpected responses and errors - let response = response?; if !self.expected_status.contains(&response.status()) { diff --git a/mullvad-daemon/src/api.rs b/mullvad-daemon/src/api.rs index db952941eb..97799d244e 100644 --- a/mullvad-daemon/src/api.rs +++ b/mullvad-daemon/src/api.rs @@ -10,6 +10,8 @@ use futures::{ channel::{mpsc, oneshot}, StreamExt, }; +#[cfg(feature = "api-override")] +use mullvad_api::ApiEndpoint; use mullvad_api::{ availability::ApiAvailability, proxy::{ApiConnectionMode, ConnectionModeProvider, ProxyConfig}, @@ -250,6 +252,8 @@ impl ConnectionModeProvider for AccessModeConnectionModeProvider { /// or via any supported custom proxy protocol /// ([`talpid_types::net::proxy::CustomProxy`]). pub struct AccessModeSelector { + #[cfg(feature = "api-override")] + api_endpoint: ApiEndpoint, cmd_rx: mpsc::UnboundedReceiver<Message>, cache_dir: PathBuf, /// Used for selecting a Bridge when the `Mullvad Bridges` access method is used. @@ -271,6 +275,7 @@ impl AccessModeSelector { relay_selector: RelaySelector, #[cfg_attr(not(feature = "api-override"), allow(unused_mut))] mut access_method_settings: Settings, + #[cfg(feature = "api-override")] api_endpoint: ApiEndpoint, access_method_event_sender: DaemonEventSender<(AccessMethodEvent, oneshot::Sender<()>)>, address_cache: AddressCache, ) -> Result<(AccessModeSelectorHandle, AccessModeConnectionModeProvider)> { @@ -278,7 +283,7 @@ impl AccessModeSelector { #[cfg(feature = "api-override")] { - if mullvad_api::API.force_direct { + if api_endpoint.force_direct { access_method_settings .update(|setting| setting.is_direct(), |setting| setting.enable()); } @@ -312,6 +317,8 @@ impl AccessModeSelector { connection_mode_provider_sender: change_tx, current: initial_connection_mode, index, + #[cfg(feature = "api-override")] + api_endpoint, }; tokio::spawn(selector.into_future()); @@ -365,7 +372,7 @@ impl AccessModeSelector { async fn use_access_method(&mut self, id: Id) { #[cfg(feature = "api-override")] { - if mullvad_api::API.force_direct { + if self.api_endpoint.force_direct { log::debug!("API proxies are disabled"); return; } @@ -392,7 +399,7 @@ impl AccessModeSelector { async fn next_connection_mode(&mut self) -> Result<ApiConnectionMode> { #[cfg(feature = "api-override")] { - if mullvad_api::API.force_direct { + if self.api_endpoint.force_direct { log::debug!("API proxies are disabled"); return Ok(ApiConnectionMode::Direct); } diff --git a/mullvad-daemon/src/api_address_updater.rs b/mullvad-daemon/src/api_address_updater.rs index f85d6259b8..de347daccb 100644 --- a/mullvad-daemon/src/api_address_updater.rs +++ b/mullvad-daemon/src/api_address_updater.rs @@ -1,5 +1,7 @@ //! A small updater that keeps the API IP address cache up to date by fetching changes from the //! Mullvad API. +#[cfg(feature = "api-override")] +use mullvad_api::ApiEndpoint; use mullvad_api::{rest::MullvadRestHandle, AddressCache, ApiProxy}; use std::time::Duration; @@ -7,9 +9,13 @@ const API_IP_CHECK_INITIAL: Duration = Duration::from_secs(15 * 60); const API_IP_CHECK_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60); const API_IP_CHECK_ERROR_INTERVAL: Duration = Duration::from_secs(15 * 60); -pub async fn run_api_address_fetcher(address_cache: AddressCache, handle: MullvadRestHandle) { +pub async fn run_api_address_fetcher( + address_cache: AddressCache, + handle: MullvadRestHandle, + #[cfg(feature = "api-override")] endpoint: ApiEndpoint, +) { #[cfg(feature = "api-override")] - if mullvad_api::API.disable_address_cache { + if endpoint.should_disable_address_cache() { return; } diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 4f98c73d01..e155ba7922 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -39,6 +39,7 @@ use futures::{ }; use geoip::GeoIpHandler; use management_interface::ManagementInterfaceServer; +use mullvad_api::ApiEndpoint; use mullvad_relay_selector::{RelaySelector, SelectorConfig}; #[cfg(target_os = "android")] use mullvad_types::account::{PlayPurchase, PlayPurchasePaymentToken}; @@ -596,6 +597,7 @@ impl Daemon { cache_dir: PathBuf, rpc_socket_path: PathBuf, daemon_command_channel: DaemonCommandChannel, + endpoint: ApiEndpoint, #[cfg(target_os = "android")] android_context: AndroidContext, ) -> Result<Self, Error> { #[cfg(target_os = "macos")] @@ -620,6 +622,7 @@ impl Daemon { mullvad_api::proxy::ApiConnectionMode::try_delete_cache(&cache_dir).await; let api_runtime = mullvad_api::Runtime::with_cache( + &endpoint, &cache_dir, true, #[cfg(target_os = "android")] @@ -667,6 +670,8 @@ impl Daemon { cache_dir.clone(), relay_selector.clone(), settings.api_access_methods.clone(), + #[cfg(feature = "api-override")] + endpoint.clone(), internal_event_tx.to_specialized_sender(), api_runtime.address_cache().clone(), ) @@ -679,6 +684,8 @@ impl Daemon { tokio::spawn(api_address_updater::run_api_address_fetcher( api_runtime.address_cache().clone(), api_handle.clone(), + #[cfg(feature = "api-override")] + endpoint, )); let access_method_handle = access_mode_handler.clone(); diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs index 3569646ccf..98910f76f8 100644 --- a/mullvad-daemon/src/main.rs +++ b/mullvad-daemon/src/main.rs @@ -1,5 +1,6 @@ use std::{path::PathBuf, thread, time::Duration}; +use mullvad_api::ApiEndpoint; #[cfg(not(windows))] use mullvad_daemon::cleanup_old_rpc_socket; use mullvad_daemon::{ @@ -221,6 +222,7 @@ async fn create_daemon(log_dir: Option<PathBuf>) -> Result<Daemon, String> { cache_dir, rpc_socket_path, daemon_command_channel, + ApiEndpoint::from_env_vars(), ) .await .map_err(|e| e.display_chain_with_msg("Unable to initialize daemon")) diff --git a/mullvad-jni/src/api.rs b/mullvad-jni/src/api.rs index b7dd5d6747..81043871f8 100644 --- a/mullvad-jni/src/api.rs +++ b/mullvad-jni/src/api.rs @@ -20,7 +20,6 @@ pub fn api_endpoint_from_java( Some(mullvad_api::ApiEndpoint { host: Some(hostname), address, - disable_address_cache: disable_address_cache_from_java(env, endpoint_override), disable_tls: disable_tls_from_java(env, endpoint_override), force_direct: force_direct_from_java(env, endpoint_override), }) @@ -71,16 +70,8 @@ fn port_from_java(env: &JnixEnv<'_>, endpoint_override: JObject<'_>) -> u16 { } #[cfg(feature = "api-override")] -fn disable_address_cache_from_java(env: &JnixEnv<'_>, endpoint_override: JObject<'_>) -> bool { - env.call_method(endpoint_override, "component3", "()Z", &[]) - .expect("missing ApiEndpointOverride.disableAddressCache") - .z() - .expect("ApiEndpointOverride.disableAddressCache is not a bool") -} - -#[cfg(feature = "api-override")] fn disable_tls_from_java(env: &JnixEnv<'_>, endpoint_override: JObject<'_>) -> bool { - env.call_method(endpoint_override, "component4", "()Z", &[]) + env.call_method(endpoint_override, "component3", "()Z", &[]) .expect("missing ApiEndpointOverride.disableTls") .z() .expect("ApiEndpointOverride.disableTls is not a bool") @@ -88,7 +79,7 @@ fn disable_tls_from_java(env: &JnixEnv<'_>, endpoint_override: JObject<'_>) -> b #[cfg(feature = "api-override")] fn force_direct_from_java(env: &JnixEnv<'_>, endpoint_override: JObject<'_>) -> bool { - env.call_method(endpoint_override, "component5", "()Z", &[]) + env.call_method(endpoint_override, "component4", "()Z", &[]) .expect("missing ApiEndpointOverride.forceDirectConnection") .z() .expect("ApiEndpointOverride.forceDirectConnection is not a bool") diff --git a/mullvad-jni/src/lib.rs b/mullvad-jni/src/lib.rs index 1dd7c86942..2d2561403f 100644 --- a/mullvad-jni/src/lib.rs +++ b/mullvad-jni/src/lib.rs @@ -12,6 +12,7 @@ use jnix::{ }, FromJava, JnixEnv, }; +use mullvad_api::ApiEndpoint; use mullvad_daemon::{ cleanup_old_rpc_socket, exception_logging, logging, runtime::new_multi_thread, version, Daemon, DaemonCommandChannel, DaemonCommandSender, @@ -150,7 +151,13 @@ fn start( log::warn!("api_endpoint will be ignored since 'api-override' is not enabled"); } - spawn_daemon(android_context, rpc_socket, files_dir, cache_dir) + spawn_daemon( + android_context, + rpc_socket, + files_dir, + cache_dir, + api_endpoint.unwrap_or(ApiEndpoint::from_env_vars()), + ) } fn spawn_daemon( @@ -158,6 +165,7 @@ fn spawn_daemon( rpc_socket: PathBuf, files_dir: PathBuf, cache_dir: PathBuf, + endpoint: ApiEndpoint, ) -> Result<DaemonContext, Error> { let daemon_command_channel = DaemonCommandChannel::new(); let daemon_command_tx = daemon_command_channel.sender(); @@ -170,6 +178,7 @@ fn spawn_daemon( cache_dir, daemon_command_channel, android_context, + endpoint, ))?; Ok(DaemonContext { @@ -185,6 +194,7 @@ async fn spawn_daemon_inner( cache_dir: PathBuf, daemon_command_channel: DaemonCommandChannel, android_context: AndroidContext, + endpoint: ApiEndpoint, ) -> Result<tokio::task::JoinHandle<()>, Error> { cleanup_old_rpc_socket(&rpc_socket).await; @@ -195,6 +205,7 @@ async fn spawn_daemon_inner( cache_dir, rpc_socket, daemon_command_channel, + endpoint, android_context, ) .await diff --git a/mullvad-jni/src/problem_report.rs b/mullvad-jni/src/problem_report.rs index 9943ec4c59..dc3693cfff 100644 --- a/mullvad-jni/src/problem_report.rs +++ b/mullvad-jni/src/problem_report.rs @@ -6,6 +6,7 @@ use jnix::{ }, FromJava, JnixEnv, }; +use mullvad_api::ApiEndpoint; use std::path::Path; use talpid_types::ErrorExt; @@ -44,6 +45,7 @@ pub extern "system" fn Java_net_mullvad_mullvadvpn_dataproxy_MullvadProblemRepor userMessage: JString<'_>, outputPath: JString<'_>, cacheDirectory: JString<'_>, + endpoint: JObject<'_>, ) -> jboolean { let env = JnixEnv::from(env); let user_email = String::from_java(&env, userEmail); @@ -52,12 +54,15 @@ pub extern "system" fn Java_net_mullvad_mullvadvpn_dataproxy_MullvadProblemRepor let output_path = Path::new(&output_path_string); let cache_directory_string = String::from_java(&env, cacheDirectory); let cache_directory = Path::new(&cache_directory_string); + let api_endpoint = + crate::api::api_endpoint_from_java(&env, endpoint).unwrap_or(ApiEndpoint::from_env_vars()); let send_result = mullvad_problem_report::send_problem_report( &user_email, &user_message, output_path, cache_directory, + api_endpoint, ); match send_result { diff --git a/mullvad-problem-report/src/lib.rs b/mullvad-problem-report/src/lib.rs index 270de55f95..b2a4e22166 100644 --- a/mullvad-problem-report/src/lib.rs +++ b/mullvad-problem-report/src/lib.rs @@ -1,4 +1,4 @@ -use mullvad_api::proxy::ApiConnectionMode; +use mullvad_api::{proxy::ApiConnectionMode, ApiEndpoint}; use regex::Regex; use std::{ borrow::Cow, @@ -261,6 +261,7 @@ pub fn send_problem_report( user_message: &str, report_path: &Path, cache_dir: &Path, + endpoint: ApiEndpoint, ) -> Result<(), Error> { let report_content = normalize_newlines( read_file_lossy(report_path, REPORT_MAX_SIZE).map_err(|source| { @@ -281,6 +282,7 @@ pub fn send_problem_report( user_message, &report_content, cache_dir, + &endpoint, )) } @@ -289,9 +291,11 @@ async fn send_problem_report_inner( user_message: &str, report_content: &str, cache_dir: &Path, + endpoint: &ApiEndpoint, ) -> Result<(), Error> { let metadata = ProblemReport::parse_metadata(report_content).unwrap_or_else(metadata::collect); let api_runtime = mullvad_api::Runtime::with_cache( + endpoint, cache_dir, false, #[cfg(target_os = "android")] diff --git a/mullvad-problem-report/src/main.rs b/mullvad-problem-report/src/main.rs index bc3e680cdf..5a073098a3 100644 --- a/mullvad-problem-report/src/main.rs +++ b/mullvad-problem-report/src/main.rs @@ -1,4 +1,5 @@ use clap::Parser; +use mullvad_api::ApiEndpoint; use mullvad_problem_report::{collect_report, Error}; use std::{ env, @@ -89,10 +90,16 @@ fn send_problem_report( report_path: &Path, ) -> Result<(), Error> { let cache_dir = mullvad_paths::get_cache_dir().map_err(Error::ObtainCacheDirectory)?; - mullvad_problem_report::send_problem_report(user_email, user_message, report_path, &cache_dir) - .inspect_err(|error| { - eprintln!("{}", error.display_chain()); - })?; + mullvad_problem_report::send_problem_report( + user_email, + user_message, + report_path, + &cache_dir, + ApiEndpoint::from_env_vars(), + ) + .inspect_err(|error| { + eprintln!("{}", error.display_chain()); + })?; println!("Problem report sent"); Ok(()) diff --git a/mullvad-setup/src/main.rs b/mullvad-setup/src/main.rs index d3dfd6de8a..e525d5cb88 100644 --- a/mullvad-setup/src/main.rs +++ b/mullvad-setup/src/main.rs @@ -1,7 +1,7 @@ use clap::Parser; use std::{path::PathBuf, process, str::FromStr, sync::LazyLock, time::Duration}; -use mullvad_api::{proxy::ApiConnectionMode, DEVICE_NOT_FOUND}; +use mullvad_api::{proxy::ApiConnectionMode, ApiEndpoint, DEVICE_NOT_FOUND}; use mullvad_management_interface::MullvadProxyClient; use mullvad_types::version::ParsedAppVersion; use talpid_core::firewall::{self, Firewall}; @@ -152,9 +152,10 @@ async fn remove_device() -> Result<(), Error> { .await .map_err(Error::ReadDeviceCacheError)?; if let Some(device) = state.into_device() { - let api_runtime = mullvad_api::Runtime::with_cache(&cache_path, false) - .await - .map_err(Error::RpcInitializationError)?; + let api_runtime = + mullvad_api::Runtime::with_cache(&ApiEndpoint::from_env_vars(), &cache_path, false) + .await + .map_err(Error::RpcInitializationError)?; let connection_mode = ApiConnectionMode::try_from_cache(&cache_path).await; let proxy = mullvad_api::DevicesProxy::new( diff --git a/test/test-manager/src/tests/account.rs b/test/test-manager/src/tests/account.rs index 7fe14ae58e..29227cc82b 100644 --- a/test/test-manager/src/tests/account.rs +++ b/test/test-manager/src/tests/account.rs @@ -278,9 +278,8 @@ pub async fn clear_devices(device_client: &DevicesProxy) -> anyhow::Result<()> { } pub async fn new_device_client() -> anyhow::Result<DevicesProxy> { - use mullvad_api::{proxy::ApiConnectionMode, ApiEndpoint, API}; + use mullvad_api::{proxy::ApiConnectionMode, ApiEndpoint}; - let api_endpoint = ApiEndpoint::from_env_vars(); let api_host = format!("api.{}", TEST_CONFIG.mullvad_host); let api_host_with_port = format!("{api_host}:443"); @@ -289,14 +288,10 @@ pub async fn new_device_client() -> anyhow::Result<DevicesProxy> { .context("failed to resolve API host")?; // Override the API endpoint to use the one specified in the test config - let _ = API.override_init(ApiEndpoint { - host: Some(api_host), - address: Some(api_address), - ..api_endpoint - }); + let endpoint = ApiEndpoint::new(api_host, api_address, false); + + let api = mullvad_api::Runtime::new(tokio::runtime::Handle::current(), &endpoint); - let api = mullvad_api::Runtime::new(tokio::runtime::Handle::current()) - .expect("failed to create api runtime"); let rest_handle = api.mullvad_rest_handle(ApiConnectionMode::Direct.into_provider()); Ok(DevicesProxy::new(rest_handle)) } |
