diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2018-03-16 13:52:05 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2018-04-11 06:35:07 -0300 |
| commit | c856368d5d91b6d593ae7bc22d60ba9f679fbc1c (patch) | |
| tree | 6466ae7a96c934dea4619fcd84c99434ae4b003a | |
| parent | b2b79a77b8a7eafbdaa7fc2972c218cf45ef319c (diff) | |
| download | mullvadvpn-c856368d5d91b6d593ae7bc22d60ba9f679fbc1c.tar.xz mullvadvpn-c856368d5d91b6d593ae7bc22d60ba9f679fbc1c.zip | |
Create `CachedDnsResolver` helper type
Use it to cache the API address in RPC factory when possible.
| -rw-r--r-- | mullvad-rpc/src/cached_dns_resolver.rs | 63 | ||||
| -rw-r--r-- | mullvad-rpc/src/lib.rs | 32 |
2 files changed, 88 insertions, 7 deletions
diff --git a/mullvad-rpc/src/cached_dns_resolver.rs b/mullvad-rpc/src/cached_dns_resolver.rs new file mode 100644 index 0000000000..9cf341de69 --- /dev/null +++ b/mullvad-rpc/src/cached_dns_resolver.rs @@ -0,0 +1,63 @@ +use std::fs::File; +use std::io::{self, Read, Write}; +use std::net::{IpAddr, ToSocketAddrs}; +use std::path::{Path, PathBuf}; + +pub struct CachedDnsResolver { + hostname: String, + cache_file: PathBuf, + cached_address: Option<IpAddr>, +} + +impl CachedDnsResolver { + pub fn new(hostname: String, cache_file: PathBuf) -> Self { + let cached_address = Self::load_from_file(&cache_file).ok(); + + CachedDnsResolver { + hostname, + cache_file, + cached_address, + } + } + + pub fn resolve(&self) -> Option<IpAddr> { + self.cached_address + .or_else(|| self.resolve_into_cache().ok()) + } + + fn load_from_file(file_path: &Path) -> io::Result<IpAddr> { + let mut file = File::open(file_path)?; + let mut address = String::new(); + + file.read_to_string(&mut address)?; + + address + .trim() + .parse() + .map_err(|_| io::Error::new(io::ErrorKind::Other, "Invalid address data")) + } + + fn resolve_into_cache(&self) -> io::Result<IpAddr> { + let address = Self::resolve_address(&self.hostname)?; + + let _ = self.store_in_cache(address); + + Ok(address) + } + + fn resolve_address(hostname: &str) -> io::Result<IpAddr> { + (hostname, 0) + .to_socket_addrs()? + .next() + .map(|socket_address| socket_address.ip()) + .ok_or_else(|| { + io::Error::new(io::ErrorKind::NotFound, "Mullvad RPC API host not found") + }) + } + + fn store_in_cache(&self, address: IpAddr) -> io::Result<()> { + let mut cache_file = File::create(&self.cache_file)?; + + writeln!(cache_file, "{}", address) + } +} diff --git a/mullvad-rpc/src/lib.rs b/mullvad-rpc/src/lib.rs index 03be52bea7..a059620a6d 100644 --- a/mullvad-rpc/src/lib.rs +++ b/mullvad-rpc/src/lib.rs @@ -37,30 +37,39 @@ use mullvad_types::relay_list::RelayList; use mullvad_types::version; use std::collections::HashMap; -use std::path::PathBuf; +use std::path::Path; pub mod event_loop; pub mod rest; +mod cached_dns_resolver; +use cached_dns_resolver::CachedDnsResolver; static MASTER_API_HOST: &str = "api.mullvad.net"; /// A type that helps with the creation of RPC connections. pub struct MullvadRpcFactory { - resource_dir: Option<PathBuf>, + address_cache: Option<CachedDnsResolver>, } impl MullvadRpcFactory { /// Create a new `MullvadRpcFactory`. pub fn new() -> Self { - MullvadRpcFactory { resource_dir: None } + MullvadRpcFactory { + address_cache: None, + } } /// Create a new `MullvadRpcFactory` using the specified resource directory. - pub fn with_resource_dir(resource_dir: PathBuf) -> Self { + pub fn with_resource_dir(resource_dir: &Path) -> Self { + let hostname = MASTER_API_HOST.to_owned(); + let cache_file = resource_dir.join("api_ip_address.txt"); + + let cached_dns_resolver = CachedDnsResolver::new(hostname, cache_file); + MullvadRpcFactory { - resource_dir: Some(resource_dir), + address_cache: Some(cached_dns_resolver), } } @@ -75,13 +84,22 @@ impl MullvadRpcFactory { } fn setup_connection(&self, transport: HttpTransport) -> Result<HttpHandle, HttpError> { - let uri = format!("https://{}/rpc/", MASTER_API_HOST); - let mut handle = transport.handle(&uri)?; + let mut handle = transport.handle(&self.api_uri())?; handle.set_header(Host::new(MASTER_API_HOST, None)); Ok(handle) } + + fn api_uri(&self) -> String { + let address = self.address_cache + .as_ref() + .and_then(CachedDnsResolver::resolve) + .map(|ip| ip.to_string()) + .unwrap_or_else(|| MASTER_API_HOST.to_owned()); + + format!("https://{}/rpc/", address) + } } jsonrpc_client!(pub struct AccountsProxy { |
