summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2022-01-14 14:18:59 +0100
committerDavid Lönnhager <david.l@mullvad.net>2022-03-01 15:30:21 +0100
commit1cd2c994e4a6aff4856de9e89e0ce58d7e51da59 (patch)
tree9354ec53a85c93ad20a5d1afb90af999f9ed483a
parent0ef6487e04e6adc2045ec5c88f4056c72f736dbb (diff)
downloadmullvadvpn-1cd2c994e4a6aff4856de9e89e0ce58d7e51da59.tar.xz
mullvadvpn-1cd2c994e4a6aff4856de9e89e0ce58d7e51da59.zip
Remove API address rotation
-rw-r--r--.github/workflows/android-app.yml1
-rw-r--r--.gitignore1
-rw-r--r--android/app/build.gradle.kts1
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt2
-rwxr-xr-xbuild-apk.sh2
-rwxr-xr-xbuild.sh2
-rw-r--r--gui/tasks/distribution.js1
-rw-r--r--mullvad-daemon/src/lib.rs3
-rw-r--r--mullvad-problem-report/src/lib.rs1
-rw-r--r--mullvad-rpc/src/address_cache.rs204
-rw-r--r--mullvad-rpc/src/bin/address_cache.rs43
-rw-r--r--mullvad-rpc/src/lib.rs24
-rw-r--r--mullvad-rpc/src/rest.rs49
-rw-r--r--mullvad-setup/src/main.rs2
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"
diff --git a/build.sh b/build.sh
index 3c01b83735..22f154b90a 100755
--- a/build.sh
+++ b/build.sh
@@ -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 =