summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-03-21 11:09:07 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-04-11 06:35:07 -0300
commit17c306705a16914c2b1476d2ae86790271f628e3 (patch)
tree2f0d42e417eec45cb69687183657fddc68caeb83
parent5eedeac9cbf6befd7ffbcd06ff767b48c8b004fe (diff)
downloadmullvadvpn-17c306705a16914c2b1476d2ae86790271f628e3.tar.xz
mullvadvpn-17c306705a16914c2b1476d2ae86790271f628e3.zip
Ensure cache is periodically invalidated
-rw-r--r--Cargo.lock12
-rw-r--r--mullvad-rpc/Cargo.toml1
-rw-r--r--mullvad-rpc/src/cached_dns_resolver.rs86
3 files changed, 90 insertions, 9 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1ae167fb80..0152eb979c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -255,6 +255,16 @@ dependencies = [
]
[[package]]
+name = "filetime"
+version = "0.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "fnv"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -666,6 +676,7 @@ version = "0.1.0"
dependencies = [
"chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.11.21 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1497,6 +1508,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum errno 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b2c858c42ac0b88532f48fca88b0ed947cad4f1f64d904bcd6c9f138f7b95d70"
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
"checksum fern 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "50475651fccc56343c766e4d1889428ea753308a977e1315db358ada28cc8c9d"
+"checksum filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "714653f3e34871534de23771ac7b26e999651a0a228f47beb324dfdf1dd4b10f"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
diff --git a/mullvad-rpc/Cargo.toml b/mullvad-rpc/Cargo.toml
index b76062a226..cf6360b6b0 100644
--- a/mullvad-rpc/Cargo.toml
+++ b/mullvad-rpc/Cargo.toml
@@ -21,4 +21,5 @@ log = "0.4"
mullvad-types = { path = "../mullvad-types" }
[dev-dependencies]
+filetime = "0.1"
tempdir = "0.3"
diff --git a/mullvad-rpc/src/cached_dns_resolver.rs b/mullvad-rpc/src/cached_dns_resolver.rs
index 9e25bff511..3acfad378c 100644
--- a/mullvad-rpc/src/cached_dns_resolver.rs
+++ b/mullvad-rpc/src/cached_dns_resolver.rs
@@ -2,6 +2,12 @@ use std::fs::File;
use std::io::{self, Read, Write};
use std::net::{IpAddr, ToSocketAddrs};
use std::path::{Path, PathBuf};
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
+
+
+static MAX_CACHE_AGE: Duration = Duration::from_secs(3600);
+static EXPIRED_CACHE_TIMESTAMP: SystemTime = UNIX_EPOCH;
+
pub trait DnsResolver {
fn resolve(&self, host: &str) -> io::Result<IpAddr>;
@@ -26,7 +32,7 @@ pub struct CachedDnsResolver<R: DnsResolver = SystemDnsResolver> {
dns_resolver: R,
cache_file: PathBuf,
cached_address: IpAddr,
- should_update: bool,
+ last_updated: SystemTime,
}
impl CachedDnsResolver<SystemDnsResolver> {
@@ -42,7 +48,7 @@ impl<R: DnsResolver> CachedDnsResolver<R> {
cache_file: PathBuf,
fallback_address: IpAddr,
) -> Self {
- let (cached_address, should_update) =
+ let (cached_address, last_updated) =
Self::load_initial_cached_address(&cache_file, fallback_address);
CachedDnsResolver {
@@ -50,22 +56,34 @@ impl<R: DnsResolver> CachedDnsResolver<R> {
dns_resolver,
cache_file,
cached_address,
- should_update,
+ last_updated,
}
}
pub fn resolve(&mut self) -> IpAddr {
- if self.should_update {
+ if let Ok(cache_age) = self.last_updated.elapsed() {
+ if cache_age > MAX_CACHE_AGE {
+ self.resolve_into_cache();
+ }
+ } else {
self.resolve_into_cache();
}
self.cached_address
}
- fn load_initial_cached_address(cache_file: &Path, fallback_address: IpAddr) -> (IpAddr, bool) {
+ fn load_initial_cached_address(
+ cache_file: &Path,
+ fallback_address: IpAddr,
+ ) -> (IpAddr, SystemTime) {
match Self::load_from_file(cache_file) {
- Ok(previously_cached_address) => (previously_cached_address, false),
- Err(_) => (fallback_address, true),
+ Ok(previously_cached_address) => {
+ let last_updated = Self::read_file_modification_time(cache_file)
+ .unwrap_or(EXPIRED_CACHE_TIMESTAMP);
+
+ (previously_cached_address, last_updated)
+ }
+ Err(_) => (fallback_address, EXPIRED_CACHE_TIMESTAMP),
}
}
@@ -81,10 +99,16 @@ impl<R: DnsResolver> CachedDnsResolver<R> {
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Invalid address data"))
}
+ fn read_file_modification_time(cache_file: &Path) -> Option<SystemTime> {
+ let metadata = cache_file.metadata().ok()?;
+
+ metadata.modified().ok()
+ }
+
fn resolve_into_cache(&mut self) {
if let Ok(address) = self.dns_resolver.resolve(&self.hostname) {
self.cached_address = address;
- self.should_update = false;
+ self.last_updated = SystemTime::now();
self.update_cache_file();
}
}
@@ -98,6 +122,7 @@ impl<R: DnsResolver> CachedDnsResolver<R> {
#[cfg(test)]
mod tests {
+ extern crate filetime;
extern crate tempdir;
use std::fs::{self, File};
@@ -105,11 +130,12 @@ mod tests {
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
+ use self::filetime::FileTime;
use self::tempdir::TempDir;
use super::*;
#[test]
- fn uses_cached_address() {
+ fn uses_previously_cached_address() {
let (_temp_dir, cache_dir) = create_test_dirs();
let mock_resolver = MockDnsResolver::with_address("192.168.1.206".parse().unwrap());
let mock_resolver_was_called = mock_resolver.was_called_handle();
@@ -125,6 +151,40 @@ mod tests {
}
#[test]
+ fn old_cache_file_is_updated() {
+ let (_temp_dir, cache_dir) = create_test_dirs();
+ let cached_address = "127.0.0.1".parse().unwrap();
+ let mock_address = "192.168.1.206".parse().unwrap();
+ let mock_resolver = MockDnsResolver::with_address(mock_address);
+
+ let cache_file_path = write_address(&cache_dir, cached_address);
+
+ make_file_old(&cache_file_path);
+
+ let mut cache = create_cached_dns_resolver(mock_resolver, &cache_dir, None);
+ let address = cache.resolve();
+
+ assert_eq!(get_cached_address(&cache_dir), address.to_string());
+ assert_eq!(address, mock_address);
+ }
+
+ #[test]
+ fn old_cache_file_is_used_if_resolution_fails() {
+ let (_temp_dir, cache_dir) = create_test_dirs();
+ let mock_resolver = MockDnsResolver::that_fails();
+ let cached_address = "127.0.0.1".parse().unwrap();
+
+ let cache_file_path = write_address(&cache_dir, cached_address);
+
+ make_file_old(&cache_file_path);
+
+ let mut cache = create_cached_dns_resolver(mock_resolver, &cache_dir, None);
+ let address = cache.resolve();
+
+ assert_eq!(address, cached_address);
+ }
+
+ #[test]
fn caches_resolved_ip() {
let (_temp_dir, cache_dir) = create_test_dirs();
let mock_address = "192.168.1.206".parse().unwrap();
@@ -197,6 +257,14 @@ mod tests {
file_path
}
+ fn make_file_old(file: &Path) {
+ let file_metadata = file.metadata().unwrap();
+ let last_access_time = FileTime::from_last_access_time(&file_metadata);
+ let fake_modification_time = FileTime::from_seconds_since_1970(100_000, 0);
+
+ filetime::set_file_times(&file, last_access_time, fake_modification_time).unwrap();
+ }
+
fn get_cached_address(cache_dir: &Path) -> String {
let cache_file_path = cache_dir.join("api_ip_address.txt");