summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-03-16 13:52:05 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-04-11 06:35:07 -0300
commitc856368d5d91b6d593ae7bc22d60ba9f679fbc1c (patch)
tree6466ae7a96c934dea4619fcd84c99434ae4b003a
parentb2b79a77b8a7eafbdaa7fc2972c218cf45ef319c (diff)
downloadmullvadvpn-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.rs63
-rw-r--r--mullvad-rpc/src/lib.rs32
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 {