summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2018-01-11 10:47:41 +0100
committerLinus Färnstrand <linus@mullvad.net>2018-01-15 15:54:16 +0100
commitfc4178d7e3329fb1fd0014e4ce349a79eafeca51 (patch)
treee6e557827dd417a626ba1a44a9045c3a8d95b435
parentc50febb59f6f36f829fe56935fd2b0cdcf3ad92f (diff)
downloadmullvadvpn-fc4178d7e3329fb1fd0014e4ce349a79eafeca51.tar.xz
mullvadvpn-fc4178d7e3329fb1fd0014e4ce349a79eafeca51.zip
Download geoip data from am.i.mullvad on request
-rw-r--r--mullvad-cli/src/cmds/relay.rs5
-rw-r--r--mullvad-cli/src/cmds/status.rs19
-rw-r--r--mullvad-daemon/Cargo.toml1
-rw-r--r--mullvad-daemon/src/geoip.rs32
-rw-r--r--mullvad-daemon/src/main.rs76
-rw-r--r--mullvad-daemon/src/management_interface.rs24
-rw-r--r--mullvad-daemon/src/relays.rs6
-rw-r--r--mullvad-rpc/Cargo.toml5
-rw-r--r--mullvad-rpc/src/event_loop.rs13
-rw-r--r--mullvad-rpc/src/lib.rs7
-rw-r--r--mullvad-rpc/src/rest.rs70
-rw-r--r--mullvad-types/src/location.rs15
-rw-r--r--mullvad-types/src/relay_list.rs3
13 files changed, 202 insertions, 74 deletions
diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs
index cbd18f1f03..86dfd7763c 100644
--- a/mullvad-cli/src/cmds/relay.rs
+++ b/mullvad-cli/src/cmds/relay.rs
@@ -196,7 +196,10 @@ impl Relay {
country.cities.sort_by(|c1, c2| c1.name.cmp(&c2.name));
println!("{} ({})", country.name, country.code);
for city in &country.cities {
- println!("\t{} ({}) @ {:?}", city.name, city.code, city.position);
+ println!(
+ "\t{} ({}) @ {:.5}°N, {:.5}°W",
+ city.name, city.code, city.latitude, city.longitude
+ );
}
println!("");
}
diff --git a/mullvad-cli/src/cmds/status.rs b/mullvad-cli/src/cmds/status.rs
index db63978798..b904f4df4a 100644
--- a/mullvad-cli/src/cmds/status.rs
+++ b/mullvad-cli/src/cmds/status.rs
@@ -2,12 +2,10 @@ use Command;
use Result;
use clap;
-use mullvad_types::location::Location;
+use mullvad_types::location::GeoIpLocation;
use mullvad_types::states::{DaemonState, SecurityState, TargetState};
use rpc;
-use std::net::IpAddr;
-
pub struct Status;
impl Command for Status {
@@ -29,15 +27,18 @@ impl Command for Status {
(SecurityState::Secured, TargetState::Secured) => println!("Connected"),
}
- let location: Location = rpc::call("get_current_location", &[] as &[u8; 0])?;
- println!("Location: {}, {}", location.city, location.country);
+ let location: GeoIpLocation = rpc::call("get_current_location", &[] as &[u8; 0])?;
+ let city_and_country = if let Some(city) = location.city {
+ format!("{}, {}", city, location.country)
+ } else {
+ format!("{}", location.country)
+ };
+ println!("Location: {}", city_and_country);
println!(
"Position: {:.5}°N, {:.5}°W",
- location.position[0], location.position[1]
+ location.latitude, location.longitude
);
-
- let ip: IpAddr = rpc::call("get_public_ip", &[] as &[u8; 0])?;
- println!("IP: {}", ip);
+ println!("IP: {}", location.ip);
Ok(())
}
}
diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml
index 473454a94a..48bd3c59af 100644
--- a/mullvad-daemon/Cargo.toml
+++ b/mullvad-daemon/Cargo.toml
@@ -23,6 +23,7 @@ jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc", tag = "v7.1
uuid = { version = "0.5", features = ["v4"] }
lazy_static = "1.0"
rand = "0.3"
+tokio-core = "0.1"
tokio-timer = "0.1"
regex = "0.2"
diff --git a/mullvad-daemon/src/geoip.rs b/mullvad-daemon/src/geoip.rs
new file mode 100644
index 0000000000..4118ed7871
--- /dev/null
+++ b/mullvad-daemon/src/geoip.rs
@@ -0,0 +1,32 @@
+use futures::{self, Future};
+use mullvad_rpc;
+use mullvad_types::location::GeoIpLocation;
+use serde_json;
+
+
+static URI: &str = "https://am.i.mullvad.net/json";
+
+error_chain! {
+ errors {
+ NoResponse { description("The request was dropped without any response") }
+ }
+ links {
+ Transport(mullvad_rpc::rest::Error, mullvad_rpc::rest::ErrorKind);
+ }
+ foreign_links {
+ Deserialize(serde_json::error::Error);
+ }
+}
+
+pub fn send_location_request(
+ request_sender: mullvad_rpc::rest::RequestSender,
+) -> Box<Future<Item = GeoIpLocation, Error = Error>> {
+ let (response_tx, response_rx) = futures::sync::oneshot::channel();
+ let request = mullvad_rpc::rest::create_get_request(URI.parse().unwrap());
+ let future = futures::Sink::send(request_sender, (request, response_tx))
+ .map_err(|e| Error::with_chain(e, ErrorKind::NoResponse))
+ .and_then(|_| response_rx.map_err(|e| Error::with_chain(e, ErrorKind::NoResponse)))
+ .and_then(|response_result| response_result.map_err(Error::from))
+ .and_then(|response| serde_json::from_slice(&response).map_err(Error::from));
+ Box::new(future)
+}
diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs
index 32c86330f2..0668d09b6e 100644
--- a/mullvad-daemon/src/main.rs
+++ b/mullvad-daemon/src/main.rs
@@ -30,6 +30,7 @@ extern crate jsonrpc_ws_server;
#[macro_use]
extern crate lazy_static;
extern crate rand;
+extern crate tokio_core;
extern crate tokio_timer;
extern crate uuid;
@@ -39,13 +40,14 @@ extern crate talpid_core;
extern crate talpid_ipc;
extern crate talpid_types;
+mod account_history;
mod cli;
+mod geoip;
mod management_interface;
mod relays;
mod rpc_info;
mod settings;
mod shutdown;
-mod account_history;
use app_dirs::AppInfo;
@@ -57,14 +59,14 @@ use management_interface::{BoxFuture, ManagementInterfaceServer, TunnelCommand};
use mullvad_rpc::{AccountsProxy, HttpHandle};
use mullvad_types::account::{AccountData, AccountToken};
-use mullvad_types::location::Location;
+use mullvad_types::location::GeoIpLocation;
use mullvad_types::relay_constraints::{RelaySettings, RelaySettingsUpdate};
use mullvad_types::relay_list::{Relay, RelayList};
use mullvad_types::states::{DaemonState, SecurityState, TargetState};
use std::env;
use std::io;
-use std::net::{IpAddr, Ipv4Addr};
+use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
@@ -188,6 +190,8 @@ struct Daemon {
management_interface_broadcaster: management_interface::EventBroadcaster,
settings: settings::Settings,
accounts_proxy: AccountsProxy<HttpHandle>,
+ http_handle: mullvad_rpc::rest::RequestSender,
+ tokio_remote: tokio_core::reactor::Remote,
relay_selector: relays::RelaySelector,
firewall: FirewallProxy,
current_relay: Option<Relay>,
@@ -201,11 +205,18 @@ impl Daemon {
pub fn new(tunnel_log: Option<PathBuf>) -> Result<Self> {
let resource_dir = get_resource_dir();
- let rpc_http_handle = mullvad_rpc::event_loop::create(|core| {
- mullvad_rpc::shared(&core.handle())
- }).chain_err(|| "Unable to initialize network event loop")?;
+ let (rpc_handle, http_handle, tokio_remote) =
+ mullvad_rpc::event_loop::create(|core| {
+ let handle = core.handle();
+ let rpc = mullvad_rpc::shared(&handle);
+ let http = mullvad_rpc::rest::create_http_client(&handle);
+ let remote = core.remote();
+ (rpc, http, remote)
+ }).chain_err(|| "Unable to initialize network event loop")?;
+ let rpc_handle = rpc_handle.chain_err(|| "Unable to create RPC client")?;
+ let http_handle = http_handle.chain_err(|| "Unable to create HTTP client")?;
- let relay_selector = Self::create_relay_selector(rpc_http_handle.clone(), &resource_dir)?;
+ let relay_selector = Self::create_relay_selector(rpc_handle.clone(), &resource_dir)?;
let (tx, rx) = mpsc::channel();
let management_interface_broadcaster = Self::start_management_interface(tx.clone())?;
@@ -224,7 +235,9 @@ impl Daemon {
tx,
management_interface_broadcaster,
settings: settings::Settings::load().chain_err(|| "Unable to read settings")?,
- accounts_proxy: AccountsProxy::new(rpc_http_handle),
+ accounts_proxy: AccountsProxy::new(rpc_handle),
+ http_handle,
+ tokio_remote,
relay_selector,
firewall: FirewallProxy::new().chain_err(|| ErrorKind::FirewallError)?,
current_relay: None,
@@ -236,10 +249,10 @@ impl Daemon {
}
fn create_relay_selector(
- rpc_http_handle: mullvad_rpc::HttpHandle,
+ rpc_handle: mullvad_rpc::HttpHandle,
resource_dir: &Path,
) -> Result<relays::RelaySelector> {
- let mut relay_selector = relays::RelaySelector::new(rpc_http_handle, &resource_dir)
+ let mut relay_selector = relays::RelaySelector::new(rpc_handle, &resource_dir)
.chain_err(|| "Unable to initialize relay list cache")?;
if let Ok(elapsed) = relay_selector.get_last_updated().elapsed() {
if elapsed > *MAX_RELAY_CACHE_AGE {
@@ -351,7 +364,6 @@ impl Daemon {
match event {
SetTargetState(state) => self.on_set_target_state(state),
GetState(tx) => Ok(self.on_get_state(tx)),
- GetPublicIp(tx) => Ok(self.on_get_ip(tx)),
GetCurrentLocation(tx) => Ok(self.on_get_current_location(tx)),
GetAccountData(tx, account_token) => Ok(self.on_get_account_data(tx, account_token)),
GetRelayLocations(tx) => Ok(self.on_get_relay_locations(tx)),
@@ -378,28 +390,28 @@ impl Daemon {
Self::oneshot_send(tx, self.last_broadcasted_state, "current state");
}
- fn on_get_ip(&self, tx: OneshotSender<IpAddr>) {
- let ip = if let Some(ref relay) = self.current_relay {
- IpAddr::V4(relay.ipv4_addr_exit)
+ fn on_get_current_location(&self, tx: OneshotSender<GeoIpLocation>) {
+ if let Some(ref relay) = self.current_relay {
+ let location = relay.location.as_ref().cloned().unwrap();
+ let geo_ip_location = GeoIpLocation {
+ ip: IpAddr::V4(relay.ipv4_addr_exit),
+ country: location.country,
+ city: Some(location.city),
+ latitude: location.latitude,
+ longitude: location.longitude,
+ mullvad_exit_ip: true,
+ };
+ Self::oneshot_send(tx, geo_ip_location, "current location");
} else {
- IpAddr::V4(Ipv4Addr::new(1, 3, 3, 7))
- };
- Self::oneshot_send(tx, ip, "current ip");
- }
-
- fn on_get_current_location(&self, tx: OneshotSender<Location>) {
- let location = if let Some(ref relay) = self.current_relay {
- relay.location.as_ref().cloned().unwrap()
- } else {
- Location {
- country: String::from("Narnia"),
- country_code: String::from("na"),
- city: String::from("Le City"),
- city_code: String::from("le"),
- position: [13.37, 0.0],
- }
- };
- Self::oneshot_send(tx, location, "current location");
+ let http_handle = self.http_handle.clone();
+ self.tokio_remote.spawn(move |_| {
+ geoip::send_location_request(http_handle)
+ .map(move |location| Self::oneshot_send(tx, location, "current location"))
+ .map_err(|e| {
+ warn!("Unable to fetch GeoIP location: {}", e.display_chain());
+ })
+ });
+ }
}
fn on_get_account_data(
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index 5bb79e8d14..3f3795183c 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -9,7 +9,7 @@ use jsonrpc_pubsub::{PubSubHandler, PubSubMetadata, Session, SubscriptionId};
use jsonrpc_ws_server;
use mullvad_rpc;
use mullvad_types::account::{AccountData, AccountToken};
-use mullvad_types::location::Location;
+use mullvad_types::location::GeoIpLocation;
use mullvad_types::relay_constraints::{RelaySettings, RelaySettingsUpdate};
use mullvad_types::relay_list::RelayList;
@@ -19,7 +19,6 @@ use serde;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
-use std::net::IpAddr;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::atomic::{AtomicBool, Ordering};
use talpid_core::mpsc::IntoSender;
@@ -99,14 +98,10 @@ build_rpc_trait! {
#[rpc(meta, name = "get_state")]
fn get_state(&self, Self::Metadata) -> BoxFuture<DaemonState, Error>;
- /// Returns the current public IP of this computer.
- #[rpc(meta, name = "get_public_ip")]
- fn get_public_ip(&self, Self::Metadata) -> BoxFuture<IpAddr, Error>;
-
/// Performs a geoIP lookup and returns the current location as perceived by the public
/// internet.
#[rpc(meta, name = "get_current_location")]
- fn get_current_location(&self, Self::Metadata) -> BoxFuture<Location, Error>;
+ fn get_current_location(&self, Self::Metadata) -> BoxFuture<GeoIpLocation, Error>;
/// Makes the daemon exit its main loop and quit.
#[rpc(meta, name = "shutdown")]
@@ -149,10 +144,8 @@ pub enum TunnelCommand {
SetTargetState(TargetState),
/// Request the current state.
GetState(OneshotSender<DaemonState>),
- /// Get the current IP as viewed from the internet.
- GetPublicIp(OneshotSender<IpAddr>),
/// Get the current geographical location.
- GetCurrentLocation(OneshotSender<Location>),
+ GetCurrentLocation(OneshotSender<GeoIpLocation>),
/// Request the metadata for an account.
GetAccountData(
OneshotSender<BoxFuture<AccountData, mullvad_rpc::Error>>,
@@ -505,16 +498,7 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem
Box::new(future)
}
- fn get_public_ip(&self, meta: Self::Metadata) -> BoxFuture<IpAddr, Error> {
- trace!("get_public_ip");
- try_future!(self.check_auth(&meta));
- let (tx, rx) = sync::oneshot::channel();
- let future = self.send_command_to_daemon(TunnelCommand::GetPublicIp(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
- }
-
- fn get_current_location(&self, meta: Self::Metadata) -> BoxFuture<Location, Error> {
+ fn get_current_location(&self, meta: Self::Metadata) -> BoxFuture<GeoIpLocation, Error> {
trace!("get_current_location");
try_future!(self.check_auth(&meta));
let (tx, rx) = sync::oneshot::channel();
diff --git a/mullvad-daemon/src/relays.rs b/mullvad-daemon/src/relays.rs
index 299a0f940b..f19feb8edb 100644
--- a/mullvad-daemon/src/relays.rs
+++ b/mullvad-daemon/src/relays.rs
@@ -269,14 +269,16 @@ impl RelaySelector {
city.has_active_relays = !city.relays.is_empty();
let city_name = city.name.clone();
let city_code = city.code.clone();
- let position = city.position;
+ let latitude = city.latitude;
+ let longitude = city.longitude;
relays.extend(city.relays.drain(..).map(|mut relay| {
relay.location = Some(Location {
country: country_name.clone(),
country_code: country_code.clone(),
city: city_name.clone(),
city_code: city_code.clone(),
- position,
+ latitude,
+ longitude,
});
relay
}));
diff --git a/mullvad-rpc/Cargo.toml b/mullvad-rpc/Cargo.toml
index 69d6cd5bbf..39c17df17d 100644
--- a/mullvad-rpc/Cargo.toml
+++ b/mullvad-rpc/Cargo.toml
@@ -8,9 +8,14 @@ license = "GPL-3.0"
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
error-chain = "0.11"
+futures = "0.1.15"
jsonrpc-client-core = "0.2.1"
jsonrpc-client-http = "0.2.1"
serde_json = "1.0"
tokio-core = "0.1"
+hyper = "0.11"
+hyper-tls = "0.1"
+native-tls = "0.1"
+log = "0.3"
mullvad-types = { path = "../mullvad-types" }
diff --git a/mullvad-rpc/src/event_loop.rs b/mullvad-rpc/src/event_loop.rs
index aaae5900ec..09af5ed54c 100644
--- a/mullvad-rpc/src/event_loop.rs
+++ b/mullvad-rpc/src/event_loop.rs
@@ -4,18 +4,16 @@ use tokio_core::reactor::Core;
error_chain! {
errors {
CoreError { description("Error when creating event loop") }
- InitCallbackError { description("Error while executing supplied init closure") }
}
}
/// Creates a new tokio event loop on a new thread, runs the provided `init` closure on the thread
/// and sends back the result.
/// Used to spawn futures on the core in the separate thread and be able to return sendable handles.
-pub fn create<F, T, E>(init: F) -> Result<T>
+pub fn create<F, T>(init: F) -> Result<T>
where
- F: FnOnce(&mut Core) -> ::std::result::Result<T, E> + Send + 'static,
+ F: FnOnce(&mut Core) -> T + Send + 'static,
T: Send + 'static,
- E: ::std::error::Error + Send + 'static,
{
let (tx, rx) = ::std::sync::mpsc::channel();
thread::spawn(move || match create_core(init) {
@@ -30,12 +28,11 @@ where
rx.recv().unwrap()
}
-fn create_core<F, T, E>(init: F) -> Result<(Core, T)>
+fn create_core<F, T>(init: F) -> Result<(Core, T)>
where
- F: FnOnce(&mut Core) -> ::std::result::Result<T, E> + Send + 'static,
- E: ::std::error::Error + Send + 'static,
+ F: FnOnce(&mut Core) -> T + Send + 'static,
{
let mut core = Core::new().chain_err(|| ErrorKind::CoreError)?;
- let out = init(&mut core).chain_err(|| ErrorKind::InitCallbackError)?;
+ let out = init(&mut core);
Ok((core, out))
}
diff --git a/mullvad-rpc/src/lib.rs b/mullvad-rpc/src/lib.rs
index 51da875891..ad20b1a0a3 100644
--- a/mullvad-rpc/src/lib.rs
+++ b/mullvad-rpc/src/lib.rs
@@ -9,9 +9,15 @@
extern crate chrono;
#[macro_use]
extern crate error_chain;
+extern crate futures;
+extern crate hyper;
+extern crate hyper_tls;
#[macro_use]
extern crate jsonrpc_client_core;
extern crate jsonrpc_client_http;
+#[macro_use]
+extern crate log;
+extern crate native_tls;
extern crate serde_json;
extern crate tokio_core;
@@ -31,6 +37,7 @@ use mullvad_types::relay_list::RelayList;
use std::collections::HashMap;
pub mod event_loop;
+pub mod rest;
static MASTER_API_URI: &str = "https://api.mullvad.net/rpc/";
diff --git a/mullvad-rpc/src/rest.rs b/mullvad-rpc/src/rest.rs
new file mode 100644
index 0000000000..54123e6b28
--- /dev/null
+++ b/mullvad-rpc/src/rest.rs
@@ -0,0 +1,70 @@
+use futures::{future, Future, Stream};
+use futures::sync::{mpsc, oneshot};
+
+use hyper;
+use hyper::{Request, StatusCode, Uri};
+use hyper::client::Client;
+use hyper_tls::HttpsConnector;
+use native_tls;
+
+use tokio_core::reactor::Handle;
+
+
+error_chain! {
+ errors {
+ /// When the http status code of the response is not 200 OK
+ HttpError(http_code: StatusCode) {
+ description("Http error. Server did not return 200 OK")
+ display("Http error. Status code {}", http_code)
+ }
+ }
+ foreign_links {
+ Tls(native_tls::Error);
+ Hyper(hyper::Error) #[doc = "An error occured in Hyper."];
+ Uri(hyper::error::UriError) #[doc = "The string given was not a valid URI."];
+ }
+}
+
+
+pub type RequestSender = mpsc::UnboundedSender<(Request, oneshot::Sender<Result<Vec<u8>>>)>;
+type RequestReceiver = mpsc::UnboundedReceiver<(Request, oneshot::Sender<Result<Vec<u8>>>)>;
+
+pub fn create_http_client(handle: &Handle) -> Result<RequestSender> {
+ let connector = HttpsConnector::new(1, handle)?;
+ let client = Client::configure().connector(connector).build(handle);
+ let (request_tx, request_rx) = mpsc::unbounded();
+ handle.spawn(create_request_processing_future(request_rx, client));
+ Ok(request_tx)
+}
+
+fn create_request_processing_future<CC: hyper::client::Connect>(
+ request_rx: RequestReceiver,
+ client: Client<CC, hyper::Body>,
+) -> Box<Future<Item = (), Error = ()>> {
+ let f = request_rx.for_each(move |(request, response_tx)| {
+ trace!("Sending request to {}", request.uri());
+ client
+ .request(request)
+ .from_err()
+ .and_then(|response: hyper::Response| {
+ if response.status() == hyper::StatusCode::Ok {
+ future::ok(response)
+ } else {
+ future::err(ErrorKind::HttpError(response.status()).into())
+ }
+ })
+ .and_then(|response: hyper::Response| response.body().concat2().from_err())
+ .map(|response_chunk| response_chunk.to_vec())
+ .then(move |response_result| {
+ if let Err(_) = response_tx.send(response_result) {
+ warn!("Unable to send response back to caller");
+ }
+ Ok(())
+ })
+ });
+ Box::new(f) as Box<Future<Item = (), Error = ()>>
+}
+
+pub fn create_get_request(uri: Uri) -> Request {
+ Request::new(hyper::Method::Get, uri)
+}
diff --git a/mullvad-types/src/location.rs b/mullvad-types/src/location.rs
index 6789e77ead..6c4ed94b00 100644
--- a/mullvad-types/src/location.rs
+++ b/mullvad-types/src/location.rs
@@ -1,3 +1,5 @@
+use std::net::IpAddr;
+
pub type CountryCode = String;
pub type CityCode = String;
@@ -7,5 +9,16 @@ pub struct Location {
pub country_code: CountryCode,
pub city: String,
pub city_code: CityCode,
- pub position: [f64; 2],
+ pub latitude: f64,
+ pub longitude: f64,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct GeoIpLocation {
+ pub ip: IpAddr,
+ pub country: String,
+ pub city: Option<String>,
+ pub latitude: f64,
+ pub longitude: f64,
+ pub mullvad_exit_ip: bool,
}
diff --git a/mullvad-types/src/relay_list.rs b/mullvad-types/src/relay_list.rs
index 4eed3985d9..86d88ce615 100644
--- a/mullvad-types/src/relay_list.rs
+++ b/mullvad-types/src/relay_list.rs
@@ -21,7 +21,8 @@ pub struct RelayListCountry {
pub struct RelayListCity {
pub name: String,
pub code: CityCode,
- pub position: [f64; 2],
+ pub latitude: f64,
+ pub longitude: f64,
#[serde(skip_deserializing)] pub has_active_relays: bool,
#[serde(skip_serializing_if = "Vec::is_empty", default)] pub relays: Vec<Relay>,
}