diff options
| author | David Lönnhager <david.l@mullvad.net> | 2022-01-14 14:18:59 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2022-03-01 15:30:21 +0100 |
| commit | 1cd2c994e4a6aff4856de9e89e0ce58d7e51da59 (patch) | |
| tree | 9354ec53a85c93ad20a5d1afb90af999f9ed483a | |
| parent | 0ef6487e04e6adc2045ec5c88f4056c72f736dbb (diff) | |
| download | mullvadvpn-1cd2c994e4a6aff4856de9e89e0ce58d7e51da59.tar.xz mullvadvpn-1cd2c994e4a6aff4856de9e89e0ce58d7e51da59.zip | |
Remove API address rotation
| -rw-r--r-- | .github/workflows/android-app.yml | 1 | ||||
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | android/app/build.gradle.kts | 1 | ||||
| -rw-r--r-- | android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt | 2 | ||||
| -rwxr-xr-x | build-apk.sh | 2 | ||||
| -rwxr-xr-x | build.sh | 2 | ||||
| -rw-r--r-- | gui/tasks/distribution.js | 1 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 3 | ||||
| -rw-r--r-- | mullvad-problem-report/src/lib.rs | 1 | ||||
| -rw-r--r-- | mullvad-rpc/src/address_cache.rs | 204 | ||||
| -rw-r--r-- | mullvad-rpc/src/bin/address_cache.rs | 43 | ||||
| -rw-r--r-- | mullvad-rpc/src/lib.rs | 24 | ||||
| -rw-r--r-- | mullvad-rpc/src/rest.rs | 49 | ||||
| -rw-r--r-- | mullvad-setup/src/main.rs | 2 |
14 files changed, 64 insertions, 272 deletions
diff --git a/.github/workflows/android-app.yml b/.github/workflows/android-app.yml index d340431ddc..3bc2192146 100644 --- a/.github/workflows/android-app.yml +++ b/.github/workflows/android-app.yml @@ -95,7 +95,6 @@ jobs: source env.sh $TARGET cargo build --target $TARGET --verbose --package mullvad-jni cargo run --bin relay_list > dist-assets/relays.json - cargo run --bin address_cache > dist-assets/api-ip-address.txt $NDK_TOOLCHAIN_STRIP_TOOL --strip-debug --strip-unneeded -o "$STRIPPED_LIB_PATH" "$UNSTRIPPED_LIB_PATH" - name: Configure Android SDK diff --git a/.gitignore b/.gitignore index e41edf6b7c..b75c65b33e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ .DS_Store *.log /dist-assets/relays.json -/dist-assets/api-ip-address.txt /dist-assets/mullvad /dist-assets/mullvad.exe /dist-assets/mullvad-daemon diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 7fa36a9986..dd7d97d1ec 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -128,7 +128,6 @@ tasks.withType<KotlinCompile>().all { tasks.register("copyExtraAssets", Copy::class) { from("$repoRootPath/dist-assets") include("relays.json") - include("api-ip-address.txt") into(extraAssetsDirectory) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt index 23b127addf..8b900038fc 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt @@ -11,7 +11,6 @@ import kotlinx.coroutines.channels.actor import kotlinx.coroutines.channels.sendBlocking import net.mullvad.mullvadvpn.util.Intermittent -private const val API_IP_ADDRESS_FILE = "api-ip-address.txt" private const val RELAYS_FILE = "relays.json" class DaemonInstance(val vpnService: MullvadVpnService) { @@ -88,7 +87,6 @@ class DaemonInstance(val vpnService: MullvadVpnService) { lastUpdatedTime() > File(vpnService.filesDir, RELAYS_FILE).lastModified() FileResourceExtractor(vpnService).apply { - extract(API_IP_ADDRESS_FILE, false) extract(RELAYS_FILE, shouldOverwriteRelayList) } } diff --git a/build-apk.sh b/build-apk.sh index c5a1fea9d1..8cc4e6ce76 100755 --- a/build-apk.sh +++ b/build-apk.sh @@ -128,8 +128,6 @@ done echo "Updating relays.json..." cargo run --bin relay_list $CARGO_ARGS > dist-assets/relays.json -echo "Updating api-ip-address..." -cargo run --bin address_cache $CARGO_ARGS > dist-assets/api-ip-address.txt cd "$SCRIPT_DIR/android" $GRADLE_CMD --console plain "$GRADLE_TASK" @@ -361,8 +361,6 @@ fi log_info "Updating relays.json..." cargo run --bin relay_list "${CARGO_ARGS[@]}" > dist-assets/relays.json -log_info "Updating api-ip-address..." -cargo run --bin address_cache "${CARGO_ARGS[@]}" > dist-assets/api-ip-address.txt log_header "Installing JavaScript dependencies" diff --git a/gui/tasks/distribution.js b/gui/tasks/distribution.js index f0eb41de67..dcdde80841 100644 --- a/gui/tasks/distribution.js +++ b/gui/tasks/distribution.js @@ -20,7 +20,6 @@ const config = { extraResources: [ { from: distAssets('ca.crt'), to: '.' }, { from: distAssets('relays.json'), to: '.' }, - { from: distAssets('api-ip-address.txt'), to: '.' }, { from: root('CHANGELOG.md'), to: '.' }, ], diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index a62979b63f..f8890abc9b 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -607,7 +607,6 @@ where }; let mut rpc_runtime = mullvad_rpc::MullvadRpcRuntime::with_cache( - Some(&resource_dir), &cache_dir, true, #[cfg(target_os = "android")] @@ -620,7 +619,7 @@ where api_availability.suspend(); let initial_api_endpoint = - Self::get_allowed_endpoint(rpc_runtime.address_cache.peek_address()); + Self::get_allowed_endpoint(rpc_runtime.address_cache.get_address()); let (offline_state_tx, offline_state_rx) = mpsc::unbounded(); #[cfg(target_os = "windows")] diff --git a/mullvad-problem-report/src/lib.rs b/mullvad-problem-report/src/lib.rs index 48b80dd1ff..b652a0411f 100644 --- a/mullvad-problem-report/src/lib.rs +++ b/mullvad-problem-report/src/lib.rs @@ -281,7 +281,6 @@ pub fn send_problem_report( let mut rpc_manager = runtime .block_on(mullvad_rpc::MullvadRpcRuntime::with_cache( - None, cache_dir, false, #[cfg(target_os = "android")] diff --git a/mullvad-rpc/src/address_cache.rs b/mullvad-rpc/src/address_cache.rs index f1048b7ede..099ffbc075 100644 --- a/mullvad-rpc/src/address_cache.rs +++ b/mullvad-rpc/src/address_cache.rs @@ -1,5 +1,3 @@ -use super::API; -use rand::seq::SliceRandom; use std::{ io, net::SocketAddr, @@ -7,10 +5,9 @@ use std::{ path::Path, sync::{Arc, Mutex}, }; -use talpid_types::ErrorExt; use tokio::{ fs, - io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, + io::{AsyncReadExt, AsyncWriteExt}, }; #[derive(err_derive::Error, Debug)] @@ -22,6 +19,9 @@ pub enum Error { #[error(display = "Failed to read the address cache file")] ReadAddressCache(#[error(source)] io::Error), + #[error(display = "Failed to parse the address cache file")] + ParseAddressCache, + #[error(display = "Failed to update the address cache file")] WriteAddressCache(#[error(source)] io::Error), @@ -44,10 +44,9 @@ pub struct AddressCache { impl AddressCache { /// Initialize cache using the given list, and write changes to `write_path`. - pub fn new(addresses: Vec<SocketAddr>, write_path: Option<Box<Path>>) -> Result<Self, Error> { - let mut cache = AddressCacheInner::from_addresses(addresses)?; - cache.shuffle_tail(); - log::trace!("API address cache: {:?}", cache.addresses); + pub fn new(address: SocketAddr, write_path: Option<Box<Path>>) -> Result<Self, Error> { + let cache = AddressCacheInner::from_address(address); + log::trace!("API address cache: {:?}", cache.address); log::debug!("Using API address: {:?}", Self::get_address_inner(&cache)); let address_cache = Self { @@ -70,134 +69,41 @@ impl AddressCache { /// Returns the currently selected address. pub fn get_address(&self) -> SocketAddr { - let mut inner = self.inner.lock().unwrap(); - inner.tried_current = true; - Self::get_address_inner(&inner) - } - - /// Returns the current address without registering it as "tried" - /// in [Self::has_tried_current_address]. - pub fn peek_address(&self) -> SocketAddr { let inner = self.inner.lock().unwrap(); Self::get_address_inner(&inner) } fn get_address_inner(inner: &AddressCacheInner) -> SocketAddr { - if inner.addresses.is_empty() { - return API.addr; - } - *inner - .addresses - .get(inner.choice % inner.addresses.len()) - .unwrap_or(&API.addr) - } - - pub fn has_tried_current_address(&self) -> bool { - let inner = self.inner.lock().unwrap(); - inner.tried_current - } - - pub async fn select_new_address(&self) { - { - let mut inner = self.inner.lock().unwrap(); - let mut transaction = AddressCacheTransaction::new(&mut inner); - - transaction.choice = transaction.current.choice.wrapping_add(1); - if transaction.choice == transaction.current.choice { - return; - } - transaction.tried_current = false; - - tokio::task::block_in_place(move || { - if (*self.change_listener)(Self::get_address_inner(&transaction)).is_err() { - log::error!("Failed to select a new API endpoint"); - return; - } - transaction.commit(); - }); - } - - if let Err(error) = self.save_to_disk().await { - log::error!("{}", error.display_chain()); - } - } - - /// Forgets the currently selected address and randomizes - /// the entire list. - pub async fn randomize(&self) -> Result<(), Error> { - { - let mut inner = self.inner.lock().unwrap(); - - let mut transaction = AddressCacheTransaction::new(&mut inner); - transaction.shuffle(); - transaction.choice = 0; - - let current_address = Self::get_address_inner(&transaction.current); - let new_address = Self::get_address_inner(&transaction); - - tokio::task::block_in_place(move || { - if new_address != current_address { - transaction.tried_current = false; - if (*self.change_listener)(new_address).is_err() { - return Err(Error::ChangeListenerError); - } - } - - transaction.commit(); - Ok(()) - })?; - } - self.save_to_disk().await.map_err(Error::WriteAddressCache) + inner.address } - pub async fn set_addresses(&self, mut addresses: Vec<SocketAddr>) -> io::Result<()> { + pub async fn set_address(&self, address: SocketAddr) -> io::Result<()> { let should_update = { let mut inner = self.inner.lock().unwrap(); let mut transaction = AddressCacheTransaction::new(&mut inner); - addresses.sort(); + let current_address = transaction.address.clone(); - let mut current_sorted = transaction.addresses.clone(); - current_sorted.sort(); - - if addresses != current_sorted { - let current_address = Self::get_address_inner(&transaction); - - transaction.addresses = addresses.clone(); - transaction.shuffle(); - - // Prefer a likely-working address - let choice = transaction - .addresses - .iter() - .position(|&addr| addr == current_address); - if let Some(choice) = choice { - transaction.choice = choice; + if address != current_address { + transaction.address = address.clone(); + tokio::task::block_in_place(move || { + if (*self.change_listener)(Self::get_address_inner(&transaction)).is_err() { + log::error!("Failed to select new API endpoint"); + return Err(io::Error::new( + io::ErrorKind::Other, + "callback returned an error", + )); + } transaction.commit(); - } else { - transaction.choice = 0; - transaction.tried_current = false; - - tokio::task::block_in_place(move || { - if (*self.change_listener)(Self::get_address_inner(&transaction)).is_err() { - log::error!("Failed to select a new API endpoint"); - return Err(io::Error::new( - io::ErrorKind::Other, - "callback returned an error", - )); - } - transaction.commit(); - Ok(()) - })?; - } - + Ok(()) + })?; true } else { false } }; if should_update { - log::trace!("API address cache: {:?}", addresses); + log::trace!("API address cache: {}", address); self.save_to_disk().await?; } Ok(()) @@ -209,25 +115,15 @@ impl AddressCache { None => return Ok(()), }; - let (mut addresses, choice) = { + let address = { let inner = self.inner.lock().unwrap(); - (inner.addresses.clone(), inner.choice) + inner.address.clone() }; - // Place the current choice on top - if !addresses.is_empty() { - let addresses_len = addresses.len(); - addresses.swap(0, choice % addresses_len); - } - let temp_path = write_path.with_file_name("api-cache.temp"); let mut file = fs::File::create(&temp_path).await?; - let mut contents = addresses - .iter() - .map(ToString::to_string) - .collect::<Vec<String>>() - .join("\n"); + let mut contents = address.to_string(); contents += "\n"; file.write_all(contents.as_bytes()).await?; file.sync_data().await?; @@ -248,32 +144,12 @@ impl crate::rest::AddressProvider for AddressCache { #[derive(Clone, PartialEq, Eq)] struct AddressCacheInner { - addresses: Vec<SocketAddr>, - choice: usize, - tried_current: bool, + address: SocketAddr, } impl AddressCacheInner { - fn from_addresses(addresses: Vec<SocketAddr>) -> Result<Self, Error> { - if addresses.is_empty() { - return Err(Error::EmptyAddressCache); - } - Ok(Self { - addresses, - choice: 0, - tried_current: false, - }) - } - - fn shuffle(&mut self) { - let mut rng = rand::thread_rng(); - (&mut self.addresses[..]).shuffle(&mut rng); - } - - /// Shuffle all but the first element - fn shuffle_tail(&mut self) { - let mut rng = rand::thread_rng(); - (&mut self.addresses[1..]).shuffle(&mut rng); + fn from_address(address: SocketAddr) -> Self { + Self { address } } } @@ -309,23 +185,13 @@ impl<'a> DerefMut for AddressCacheTransaction<'a> { } } -async fn read_address_file(path: &Path) -> Result<Vec<SocketAddr>, Error> { - let file = fs::File::open(path) +async fn read_address_file(path: &Path) -> Result<SocketAddr, Error> { + let mut file = fs::File::open(path) .await .map_err(|error| Error::OpenAddressCache(error))?; - let mut lines = BufReader::new(file).lines(); - let mut addresses = vec![]; - while let Some(line) = lines - .next_line() + let mut address = String::new(); + file.read_to_string(&mut address) .await - .map_err(|error| Error::ReadAddressCache(error))? - { - match line.trim().parse() { - Ok(address) => addresses.push(address), - Err(err) => { - log::error!("Failed to parse cached address line: {}", err); - } - } - } - Ok(addresses) + .map_err(Error::ReadAddressCache)?; + address.trim().parse().map_err(|_| Error::ParseAddressCache) } diff --git a/mullvad-rpc/src/bin/address_cache.rs b/mullvad-rpc/src/bin/address_cache.rs deleted file mode 100644 index 066ce5c910..0000000000 --- a/mullvad-rpc/src/bin/address_cache.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! Fetches and prints a list of IP addresses and ports where the Mullvad API -//! can be reached. -//! Used by the installer artifact packer to bundle the latest available list -//! of API IPs. - -use mullvad_rpc::{rest::Error as RestError, ApiProxy, MullvadRpcRuntime}; -use std::process; -use talpid_types::ErrorExt; - -#[tokio::main] -async fn main() { - let mut runtime = - MullvadRpcRuntime::new(tokio::runtime::Handle::current()).expect("Failed to load runtime"); - - let api_proxy = ApiProxy::new(runtime.mullvad_rest_handle()); - let request = api_proxy.get_api_addrs().await; - - let api_list = match request { - Ok(api_list) => api_list, - Err(RestError::TimeoutError(_)) => { - eprintln!("Request timed out"); - process::exit(2); - } - Err(e @ RestError::DeserializeError(_)) => { - eprintln!( - "{}", - e.display_chain_with_msg("Failed to deserialize API address list") - ); - process::exit(3); - } - Err(e) => { - eprintln!( - "{}", - e.display_chain_with_msg("Failed to fetch API address list") - ); - process::exit(1); - } - }; - - for address in api_list { - println!("{}", address); - } -} diff --git a/mullvad-rpc/src/lib.rs b/mullvad-rpc/src/lib.rs index faf20e00c9..08c13fd1f0 100644 --- a/mullvad-rpc/src/lib.rs +++ b/mullvad-rpc/src/lib.rs @@ -150,7 +150,7 @@ impl MullvadRpcRuntime { ) -> Result<Self, Error> { Ok(MullvadRpcRuntime { handle, - address_cache: AddressCache::new(vec![API.addr], None)?, + address_cache: AddressCache::new(API.addr, None)?, api_availability: ApiAvailability::new(availability::State::default()), #[cfg(target_os = "android")] socket_bypass_tx, @@ -158,10 +158,8 @@ impl MullvadRpcRuntime { } /// Create a new `MullvadRpcRuntime` using the specified directories. - /// Try to use the cache directory first, and fall back on the resource directory - /// if it fails. + /// Try to use the cache directory first, and fall back on the bundled address otherwise. pub async fn with_cache( - resource_dir: Option<&Path>, cache_dir: &Path, write_changes: bool, #[cfg(target_os = "android")] socket_bypass_tx: Option<mpsc::Sender<SocketBypassRequest>>, @@ -185,26 +183,15 @@ impl MullvadRpcRuntime { let address_cache = match AddressCache::from_file(&cache_file, write_file.clone()).await { Ok(cache) => cache, Err(error) => { - let cache_exists = cache_file.exists(); - if cache_exists { + if cache_file.exists() { log::error!( "{}", error.display_chain_with_msg( - "Failed to load cached API addresses. Falling back on bundled list" + "Failed to load cached API addresses. Falling back on bundled address" ) ); } - - // Initialize the cache directory cache using the resource directory - match resource_dir { - Some(resource_dir) => { - let read_file = resource_dir.join(API_IP_CACHE_FILENAME); - let cache = AddressCache::from_file(&read_file, write_file).await?; - cache.randomize().await?; - cache - } - None => return Err(Error::AddressCacheError(error)), - } + AddressCache::new(API.addr, write_file)? } }; @@ -235,7 +222,6 @@ impl MullvadRpcRuntime { self.handle.clone(), sni_hostname, self.api_availability.handle(), - self.address_cache.clone(), #[cfg(target_os = "android")] socket_bypass_tx, ); diff --git a/mullvad-rpc/src/rest.rs b/mullvad-rpc/src/rest.rs index 41b637f84d..e05c91ab6e 100644 --- a/mullvad-rpc/src/rest.rs +++ b/mullvad-rpc/src/rest.rs @@ -97,7 +97,6 @@ pub(crate) struct RequestService { next_id: u64, in_flight_requests: BTreeMap<u64, AbortHandle>, api_availability: ApiAvailabilityHandle, - address_cache: AddressCache, } impl RequestService { @@ -106,7 +105,6 @@ impl RequestService { handle: Handle, sni_hostname: Option<String>, api_availability: ApiAvailabilityHandle, - address_cache: AddressCache, #[cfg(target_os = "android")] socket_bypass_tx: Option<mpsc::Sender<SocketBypassRequest>>, ) -> RequestService { let (connector, connector_handle) = HttpsConnectorWithSni::new( @@ -128,7 +126,6 @@ impl RequestService { in_flight_requests: BTreeMap::new(), next_id: 0, api_availability, - address_cache, } } @@ -158,8 +155,6 @@ impl RequestService { let _ = suspend_fut.await; request_fut.await }); - let address_cache = self.address_cache.clone(); - let handle = self.handle.clone(); let future = async move { let response = @@ -168,29 +163,14 @@ impl RequestService { .map_err(Error::TimeoutError); let response = flatten_result(flatten_result(response)); - if let Some(host_addr) = host_addr { + if host_addr.is_some() { if let Err(err) = &response { - if err.is_network_error() { + if err.is_network_error() && !api_availability.get_state().is_offline() + { log::error!( "{}", err.display_chain_with_msg("HTTP request failed") ); - if !api_availability.get_state().is_offline() { - let current_address = address_cache.peek_address(); - if current_address == host_addr - && address_cache.has_tried_current_address() - { - handle.spawn(async move { - address_cache.select_new_address().await; - let new_address = address_cache.peek_address(); - log::error!( - "Request failed using address {}. Trying next API address: {}", - current_address, - new_address, - ); - }); - } - } } } } @@ -652,14 +632,29 @@ impl MullvadRestHandle { } match api_proxy.clone().get_api_addrs().await { Ok(new_addrs) => { - log::debug!("Fetched new API addresses {:?}, will fetch again in {} hours", new_addrs, API_IP_CHECK_INTERVAL.as_secs() / ( 60 * 60 )); - if let Err(err) = address_cache.set_addresses(new_addrs).await { - log::error!("Failed to save newly updated API addresses: {}", err); + if let Some(addr) = new_addrs.get(0) { + log::debug!( + "Fetched new API address {:?}. Fetching again in {} hours", + addr, + API_IP_CHECK_INTERVAL.as_secs() / (60 * 60) + ); + if let Err(err) = address_cache.set_address(*addr).await { + log::error!( + "Failed to save newly updated API address: {}", + err + ); + } + } else { + log::error!("API returned no API addresses"); } next_check = next_regular_check(); } Err(err) => { - log::error!("Failed to fetch new API addresses: {}, will retry again in {} seconds", err, API_IP_CHECK_ERROR_INTERVAL.as_secs()); + log::error!( + "Failed to fetch new API addresses: {}. Retrying in {} seconds", + err, + API_IP_CHECK_ERROR_INTERVAL.as_secs() + ); next_check = next_error_check(); } } diff --git a/mullvad-setup/src/main.rs b/mullvad-setup/src/main.rs index 947f9bd9ca..36e1004742 100644 --- a/mullvad-setup/src/main.rs +++ b/mullvad-setup/src/main.rs @@ -165,7 +165,7 @@ async fn remove_wireguard_key() -> Result<(), Error> { if let Some(token) = settings.get_account_token() { if let Some(wg_data) = settings.get_wireguard() { - let mut rpc_runtime = MullvadRpcRuntime::with_cache(None, &cache_path, false) + let mut rpc_runtime = MullvadRpcRuntime::with_cache(&cache_path, false) .await .map_err(Error::RpcInitializationError)?; let mut key_proxy = |
